changeset 27556:410622ac120f

improve run selection from editor (bug #42705) * file-editor-tab.cc (file_editor_tab): relay interpreter event signals to the ones of the editor widget * file-editor.cc (make_file_editor_tab): connect signal of edit area for giving focus to console window to the new slot in main_window * octave-qscintilla.cc: include headers required for interpreter threads and for additionally used Qt libs; (octave_qscintilla): connect new signals for finished run selection action to the new related slot; (contextmenu_run_temp_error): new method displaying a message box on error handling temp. files; (contextmenu_run): create a temp. file with all selected lines extended by commands for displaying each line and add it to the command history, disable opening a file at breakpoints, run this temp. file by the interpreter and emit new signal when running this file is finished; (ctx_menu_run_finished): slot when running the temp. file is finished, removing temp. files and reset modified editor preferences; * octave-qscintilla.h: include QSettings and interpreter-events, interpreter signal, signal for focusing console window and signal when running temp file is finished, new slot for the latter * main-window.cc (focus_console_after_command): do not return the related setting, but actually give console window the focus if desired, (run_file_in_terminal, execute_command_in_terminal): use this new form * main-window.h: change focus_console_method from private method into public slot * resource-manager.cc (resource_manager): initialize new list of created temp. files; (~resource_manager): remove any temp. files that are still existing; (do_create_tmp_file): create a new temp. file with the given contents and store its smart pointer into a list for removing the file later; (do_remove_tmp_file): remove given temp. file and remove it from the list; * resource-manager.h: include QPointer and QTemporaryFile, (create_tmp_file): new static method calling internal do_create_tmp_file; (remove_tmp_file): new static method calling internal do_create_tmp_file; new internal methods do_create_tmp_file and do_remove_tmp_file, new list for storing temp. file pointers
author Torsten Lilge <ttl-octave@mailbox.org>
date Fri, 25 Oct 2019 07:36:37 +0200
parents c2f2fb1df9ed
children f9f21bb2ea1c
files libgui/src/m-editor/file-editor-tab.cc libgui/src/m-editor/file-editor.cc libgui/src/m-editor/octave-qscintilla.cc libgui/src/m-editor/octave-qscintilla.h libgui/src/main-window.cc libgui/src/main-window.h libgui/src/resource-manager.cc libgui/src/resource-manager.h
diffstat 8 files changed, 251 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/src/m-editor/file-editor-tab.cc	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/m-editor/file-editor-tab.cc	Fri Oct 25 07:36:37 2019 +0200
@@ -216,6 +216,15 @@
     edit_area_layout->setSpacing (0);
     setLayout (edit_area_layout);
 
+    // Any interpreter_event signal from a file_editor_tab_widget is
+    // handled the same as for the parent main_window object.
+
+    connect (m_edit_area, SIGNAL (interpreter_event (const fcn_callback&)),
+             this, SIGNAL (interpreter_event (const fcn_callback&)));
+
+    connect (m_edit_area, SIGNAL (interpreter_event (const meth_callback&)),
+             this, SIGNAL (interpreter_event (const meth_callback&)));
+
     // connect modified signal
     connect (m_edit_area, SIGNAL (modificationChanged (bool)),
              this, SLOT (update_window_title (bool)));
--- a/libgui/src/m-editor/file-editor.cc	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/m-editor/file-editor.cc	Fri Oct 25 07:36:37 2019 +0200
@@ -2263,6 +2263,10 @@
              SIGNAL (execute_command_in_terminal_signal (const QString&)),
              main_win (), SLOT (execute_command_in_terminal (const QString&)));
 
+    connect (f->qsci_edit_area (),
+             SIGNAL (focus_console_after_command_signal (void)),
+             main_win (), SLOT (focus_console_after_command (void)));
+
     // Signals from the file editor_tab
     connect (f, SIGNAL (file_name_changed (const QString&, const QString&, bool)),
              this, SLOT (handle_file_name_changed (const QString&,
--- a/libgui/src/m-editor/octave-qscintilla.cc	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/m-editor/octave-qscintilla.cc	Fri Oct 25 07:36:37 2019 +0200
@@ -20,7 +20,7 @@
 
 */
 
-// Author: Torsten <ttl@justmail.de>
+// Author: Torsten Lilge <ttl-octave@mailbox.org>
 
 #if defined (HAVE_CONFIG_H)
 #  include "config.h"
@@ -30,7 +30,9 @@
 
 #include <Qsci/qscilexer.h>
 
+#include <QDir>
 #include <QKeySequence>
+#include <QMessageBox>
 #include <QMimeData>
 #include <QShortcut>
 #include <QToolTip>
@@ -57,6 +59,11 @@
 #include "resource-manager.h"
 #include "shortcut-manager.h"
 
+#include "builtin-defun-decls.h"
+#include "cmd-edit.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+
 // Return true if CANDIDATE is a "closing" that matches OPENING,
 // such as "end" or "endif" for "if", or "catch" for "try".
 // Used for testing the last word of an "if" etc. line,
@@ -112,6 +119,12 @@
     connect (this, SIGNAL (cursorPositionChanged (int, int)),
              this, SLOT (cursor_position_changed (int, int)));
 
+    connect (this, SIGNAL (ctx_menu_run_finished_signal (bool, QTemporaryFile*,
+                                                         QTemporaryFile*, QTemporaryFile*)),
+             this, SLOT (ctx_menu_run_finished (bool, QTemporaryFile*,
+                                                QTemporaryFile*, QTemporaryFile*)),
+             Qt::QueuedConnection);
+
     // clear scintilla edit shortcuts that are handled by the editor
     QsciCommandSet *cmd_set = standardCommands ();
 
@@ -748,14 +761,152 @@
     emit context_menu_edit_signal (m_word_at_cursor);
   }
 
+  void octave_qscintilla::contextmenu_run_temp_error (void)
+  {
+    QMessageBox::critical (this, tr ("Octave Editor"),
+                        tr ("Creating temporary files failed.\n"
+                            "Make sure you have write access to temp. directory\n"
+                             "%1\n\n"
+                             "\"Run Selection\" requires temporary files.").arg (QDir::tempPath ()));
+  }
+
   void octave_qscintilla::contextmenu_run (bool)
   {
-    QStringList commands = selectedText ().split (QRegExp ("[\r\n]"),
-                                                  QString::SkipEmptyParts);
-    for (int i = 0; i < commands.size (); i++)
-      emit execute_command_in_terminal_signal (commands.at (i));
+
+    // Create tmp file required for adding command to history
+    QPointer<QTemporaryFile> tmp_hist
+        = resource_manager::create_tmp_file (); // empty tmp file for history
+
+    // Create tmp file required for the script echoing and adding cmd to hist
+    QPointer<QTemporaryFile> tmp_script
+        = resource_manager::create_tmp_file ("m"); // tmp script file
+
+    bool tmp = (tmp_hist && tmp_hist->open () &&
+                tmp_script && tmp_script->open());
+    if (! tmp)
+      {
+        // tmp files not working: use old way to run selection
+        contextmenu_run_temp_error ();
+        return;
+      }
+
+    tmp_hist->close ();
+
+    QString tmp_hist_name = QFileInfo (tmp_hist->fileName ()).baseName ();
+    QString tmp_script_name = QFileInfo (tmp_script->fileName ()).baseName ();
+
+    // Create tmp file with script for echoing a command and adding
+    // the the history
+    QString echo_hist = QString (
+        "function cnt = %2 (oldcnt, i, command, line)\n"
+        "   cnt = oldcnt;\n"
+        "   if cnt < i\n"
+        "       cnt = i;\n"
+        "       disp ([PS1, command]);\n"
+        "       if (history_save ())\n"
+        "           fid = fopen ('%1','w');\n"
+        "           if (fid != -1)\n"
+        "               fprintf (fid, [line,'\\n']);\n"
+        "               fclose (fid);\n"
+        "           end;\n"
+        "           history -r '%1'\n"
+        "       end\n"
+        "   end\n"
+        " end\n").arg (tmp_hist->fileName ()).arg (tmp_script_name);
+
+    tmp_script->write (echo_hist.toUtf8 ());
+    tmp_script->close ();
+
+    // Take selected code and extend it by commands for echoing each
+    // evaluated line and for adding the line to the history (use script)
+
+    QString tmp_dir = QFileInfo (tmp_script->fileName ()).absolutePath ();
+    QString code = QString ("%1 = -1;\n\n").arg (tmp_hist_name);
+
+    // Split contents into single lines and complete commands
+    QStringList lines = selectedText ().split (QRegExp ("[\r\n]"),
+                                               QString::SkipEmptyParts);
+
+    for (int i = 0; i < lines.count (); i++)
+      {
+        QString line = lines.at (i);
+        line = line.replace (QString ("%"), QString ("%%"));
+
+        code += QString ("%1 = %2 (%1, %3, '%4', '%5');\n"
+                         "%4\n\n")
+                         .arg (tmp_hist_name)
+                         .arg (tmp_script_name)
+                         .arg (i)
+                         .arg (lines.at (i))
+                         .arg (line);
+      }
+
+    code += QString ("clear %1 %2_fcn;\n")
+                     .arg (tmp_hist_name).arg (tmp_script_name);
+
+    // Create tmp file with the code to be executed by the interpreter
+    QPointer<QTemporaryFile> tmp_file
+        = resource_manager::create_tmp_file ("m", code);
+
+    tmp = (tmp_file && tmp_file->open ());
+    if (! tmp)
+      {
+        // tmp files not working: use old way to run selection
+        contextmenu_run_temp_error ();
+        return;
+      }
+    tmp_file->close ();
+
+    // Disable opening a file at a breakpoint in case keyboard () is used
+    QSettings* settings = resource_manager::get_settings ();
+    bool show_dbg_file = settings->value (ed_show_dbg_file.key,
+                                       ed_show_dbg_file.def).toBool ();
+    settings->setValue (ed_show_dbg_file.key, false);
+
+    emit focus_console_after_command_signal ();
+
+    // Let the interpreter execute the tmp file
+    emit interpreter_event
+      ([this, tmp_file, tmp_hist, tmp_script, show_dbg_file] (interpreter& interp)
+       {
+         // INTERPRETER THREAD
+
+         std::string file = tmp_file->fileName ().toStdString ();
+
+         std::string pending_input = command_editor::get_current_line ();
+
+         // Add tmp dir to the path for echo/hist script
+         octave_value_list path =
+            ovl (QFileInfo (tmp_script->fileName ()).absolutePath ().toStdString ());
+         Faddpath (interp, path);
+         interp.source_file (file);
+         Frmpath (interp, path);
+
+         command_editor::replace_line ("");
+         command_editor::set_initial_input (pending_input);
+         command_editor::redisplay ();
+         command_editor::interrupt_event_loop ();
+         command_editor::accept_line ();
+
+         // Done, restore settings and remove tmp files
+         emit ctx_menu_run_finished_signal (show_dbg_file,
+                                            tmp_file, tmp_hist, tmp_script);
+       });
   }
 
+  void octave_qscintilla::ctx_menu_run_finished (bool show_dbg_file,
+                                                 QTemporaryFile* tmp_file,
+                                                 QTemporaryFile* tmp_hist,
+                                                 QTemporaryFile* tmp_script)
+  {
+    QSettings *settings = resource_manager::get_settings ();
+    settings->setValue (ed_show_dbg_file.key, show_dbg_file);
+    resource_manager::remove_tmp_file (tmp_file);
+    resource_manager::remove_tmp_file (tmp_hist);
+    resource_manager::remove_tmp_file (tmp_script);
+  }
+
+
   // wrappers for dbstop related context menu items
 
   // FIXME: Why can't the data be sent as the argument to the function???
--- a/libgui/src/m-editor/octave-qscintilla.h	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/m-editor/octave-qscintilla.h	Fri Oct 25 07:36:37 2019 +0200
@@ -29,8 +29,11 @@
 #include <QKeyEvent>
 #include <QLabel>
 #include <QMenu>
+#include <QSettings>
 #include <Qsci/qsciscintilla.h>
 
+#include "qt-interpreter-events.h"
+
 namespace octave
 {
   class octave_qscintilla : public QsciScintilla
@@ -83,14 +86,22 @@
     void show_doc_signal (const QString&);
     void context_menu_break_condition_signal (int);
     void context_menu_break_once (int);
+    void interpreter_event (const meth_callback& meth);
+    void ctx_menu_run_finished_signal (bool, QTemporaryFile*,
+                                       QTemporaryFile*, QTemporaryFile*);
+    void focus_console_after_command_signal (void);
 
   private slots:
 
+    void ctx_menu_run_finished (bool, QTemporaryFile*,
+                                QTemporaryFile*, QTemporaryFile*);
+
     void contextmenu_help (bool);
     void contextmenu_doc (bool);
     void contextmenu_help_doc (bool);
     void contextmenu_edit (bool);
     void contextmenu_run (bool);
+    void contextmenu_run_temp_error (void);
 
     void contextmenu_break_condition (bool);
     void contextmenu_break_once (const QPoint&);
--- a/libgui/src/main-window.cc	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/main-window.cc	Fri Oct 25 07:36:37 2019 +0200
@@ -1037,8 +1037,7 @@
          command_editor::accept_line ();
        });
 
-    if (focus_console_after_command ())
-      focus_command_window ();
+    focus_console_after_command ();
   }
 
   void main_window::run_file_in_terminal (const QFileInfo& info)
@@ -1082,8 +1081,7 @@
          command_editor::accept_line ();
        });
 
-    if (focus_console_after_command ())
-      focus_command_window ();
+    focus_console_after_command ();
   }
 
   void main_window::handle_new_figure_request (void)
@@ -2749,10 +2747,11 @@
     qt_link->wake_all ();
   }
 
-  bool main_window::focus_console_after_command (void)
+  void main_window::focus_console_after_command (void)
   {
     QSettings *settings = resource_manager::get_settings ();
-    return settings->value ("terminal/focus_after_command",false).toBool ();
+    if (settings->value ("terminal/focus_after_command",false).toBool ())
+      focus_command_window ();
   }
 
   void main_window::configure_shortcuts (void)
--- a/libgui/src/main-window.h	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/main-window.h	Fri Oct 25 07:36:37 2019 +0200
@@ -199,6 +199,7 @@
 
     void gui_preference (const QString& key, const QString& value,
                          QString* read_value);
+    void focus_console_after_command (void);
     void handle_show_doc (const QString& file);
     void handle_register_doc (const QString& file);
     void handle_unregister_doc (const QString& file);
@@ -283,8 +284,6 @@
 
     QString gui_preference_adjust (const QString& key, const QString& value);
 
-    bool focus_console_after_command (void);
-
     void configure_shortcuts (void);
 
     QList<octave_dock_widget *> dock_widget_list (void);
--- a/libgui/src/resource-manager.cc	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/resource-manager.cc	Fri Oct 25 07:36:37 2019 +0200
@@ -68,7 +68,7 @@
 
   resource_manager::resource_manager (void)
     : m_settings_directory (), m_settings_file (), m_settings (nullptr),
-      m_default_settings (nullptr)
+      m_default_settings (nullptr), m_temporary_files ()
   {
     // Let QSettings decide where to put the ini file with gui preferences
     m_default_settings
@@ -124,6 +124,9 @@
   {
     delete m_settings;
     delete m_default_settings;
+
+    for (int i = m_temporary_files.count () - 1; i >=0; i--)
+      remove_tmp_file (m_temporary_files.at (i));
   }
 
   QString resource_manager::get_gui_translation_dir (void)
@@ -487,4 +490,43 @@
 
     combo->setMaxVisibleItems (12);
   }
+
+  QPointer<QTemporaryFile>
+  resource_manager::do_create_tmp_file (const QString& extension,
+                                        const QString& contents)
+  {
+    QString ext = extension;
+    if ((! ext.isEmpty ()) && (! ext.startsWith ('.')))
+      ext = QString (".") + ext;
+
+    // Create octave dir within temp. dir
+    QString tmp_dir = QDir::tempPath () + QDir::separator() + "octave";
+    QDir::temp ().mkdir ("octave");
+
+    // Create temp. file
+    QPointer<QTemporaryFile> tmp_file
+        = new QTemporaryFile (tmp_dir + QDir::separator() +
+                              "octave_XXXXXX" + ext, this);
+
+    if (tmp_file->open ())
+    {
+      tmp_file->write (contents.toUtf8 ());
+      tmp_file->close ();
+
+      m_temporary_files << tmp_file;
+    }
+
+    return tmp_file;
+  }
+
+  void resource_manager::do_remove_tmp_file (QPointer<QTemporaryFile> tmp_file)
+  {
+    if (tmp_file)
+      {
+        if (tmp_file->exists ())
+          tmp_file->remove ();
+
+        m_temporary_files.removeAll (tmp_file);
+      }
+  }
 }
--- a/libgui/src/resource-manager.h	Thu Oct 24 22:33:37 2019 +0200
+++ b/libgui/src/resource-manager.h	Fri Oct 25 07:36:37 2019 +0200
@@ -27,8 +27,10 @@
 #include <QDesktopServices>
 #include <QIcon>
 #include <QMap>
+#include <QPointer>
 #include <QSettings>
 #include <QTranslator>
+#include <QTemporaryFile>
 
 namespace octave
 {
@@ -120,6 +122,19 @@
       return instance_ok () ? instance->do_is_first_run () : true;
     }
 
+    static QPointer<QTemporaryFile> create_tmp_file (
+                                          const QString& extension = QString (),
+                                          const QString& contents = QString ())
+    {
+      return instance_ok () ? instance->do_create_tmp_file (extension, contents) : QPointer<QTemporaryFile> ();
+    }
+
+    static void remove_tmp_file (QPointer<QTemporaryFile> tmp_file)
+    {
+      if (instance_ok ())
+        instance->do_remove_tmp_file (tmp_file);
+    }
+
     static QString storage_class_chars (void) { return "agp"; }
     static QStringList storage_class_names (void);
     static QList<QColor> storage_class_default_colors (void);
@@ -152,6 +167,11 @@
 
     QString do_get_default_font_family (void);
 
+    QPointer<QTemporaryFile> do_create_tmp_file (const QString& extension,
+                                                 const QString& contents);
+
+    void do_remove_tmp_file (QPointer<QTemporaryFile> tmp_file);
+
     void do_reload_settings (void);
 
     void do_set_settings (const QString& file);
@@ -174,6 +194,8 @@
     QSettings *m_settings;
 
     QSettings *m_default_settings;
+
+    QList<QTemporaryFile *> m_temporary_files;
   };
 }