1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2011-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #include <utility>
31 
32 #include <QAction>
33 #include <QApplication>
34 #include <QClipboard>
35 #include <QDateTime>
36 #include <QDebug>
37 #include <QDesktopServices>
38 #include <QDesktopWidget>
39 #include <QFileDialog>
40 #include <QIcon>
41 #include <QInputDialog>
42 #include <QKeySequence>
43 #include <QLabel>
44 #include <QMenu>
45 #include <QMenuBar>
46 #include <QMessageBox>
47 #include <QStyle>
48 #include <QStyleFactory>
49 #include <QStyleFactory>
50 #include <QTextBrowser>
51 #include <QTextCodec>
52 #include <QTextStream>
53 #include <QThread>
54 #include <QTimer>
55 #include <QToolBar>
56 
57 #if defined (HAVE_QSCINTILLA)
58 #  include "file-editor.h"
59 #endif
60 #include "gui-preferences-cs.h"
61 #include "gui-preferences-dw.h"
62 #include "gui-preferences-ed.h"
63 #include "gui-preferences-global.h"
64 #include "gui-preferences-mw.h"
65 #include "gui-preferences-nr.h"
66 #include "gui-preferences-sc.h"
67 #include "gui-settings.h"
68 #include "interpreter-qobject.h"
69 #include "main-window.h"
70 #include "news-reader.h"
71 #include "octave-qobject.h"
72 #include "settings-dialog.h"
73 #include "shortcut-manager.h"
74 #include "welcome-wizard.h"
75 
76 #include "Array.h"
77 #include "cmd-edit.h"
78 #include "oct-env.h"
79 #include "url-transfer.h"
80 
81 #include "builtin-defun-decls.h"
82 #include "defaults.h"
83 #include "defun.h"
84 #include "interpreter-private.h"
85 #include "interpreter.h"
86 #include "load-path.h"
87 #include "oct-map.h"
88 #include "octave.h"
89 #include "parse.h"
90 #include "syminfo.h"
91 #include "symscope.h"
92 #include "utils.h"
93 #include "version.h"
94 
95 namespace octave
96 {
97   static file_editor_interface *
create_default_editor(QWidget * p,base_qobject & oct_qobj)98   create_default_editor (QWidget *p, base_qobject& oct_qobj)
99   {
100 #if defined (HAVE_QSCINTILLA)
101     return new file_editor (p, oct_qobj);
102 #else
103     octave_unused_parameter (p);
104     octave_unused_parameter (oct_qobj);
105 
106     return 0;
107 #endif
108   }
109 
main_window(base_qobject & oct_qobj)110   main_window::main_window (base_qobject& oct_qobj)
111     : QMainWindow (), m_octave_qobj (oct_qobj),
112       m_workspace_model (nullptr),
113       m_status_bar (nullptr), m_command_window (nullptr),
114       m_history_window (nullptr), m_file_browser_window (nullptr),
115       m_doc_browser_window (nullptr), m_editor_window (nullptr),
116       m_workspace_window (nullptr), m_variable_editor_window (nullptr),
117       m_external_editor (new external_editor_interface (this, m_octave_qobj)),
118       m_active_editor (m_external_editor), m_settings_dlg (nullptr),
119       m_find_files_dlg (nullptr), m_set_path_dlg (nullptr),
120       m_release_notes_window (nullptr), m_community_news_window (nullptr),
121       m_clipboard (QApplication::clipboard ()),
122       m_prevent_readline_conflicts (true), m_suppress_dbg_location (true),
123       m_closing (false), m_file_encoding (QString ())
124   {
125     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
126 
127     if (rmgr.is_first_run ())
128       {
129         // Before wizard.
130         m_octave_qobj.config_translators ();
131 
132         welcome_wizard welcomeWizard (m_octave_qobj);
133 
134         if (welcomeWizard.exec () == QDialog::Rejected)
135           exit (1);
136 
137         // Install settings file.
138         rmgr.reload_settings ();
139       }
140     else
141       {
142         // Get settings file.
143         rmgr.reload_settings ();
144 
145         // After settings.
146         m_octave_qobj.config_translators ();
147       }
148 
149     rmgr.update_network_settings ();
150 
151     // We provide specific terminal capabilities, so ensure that
152     // TERM is always set appropriately.
153 
154 #if defined (OCTAVE_USE_WINDOWS_API)
155     sys::env::putenv ("TERM", "cygwin");
156 #else
157     sys::env::putenv ("TERM", "xterm");
158 #endif
159 
160     // FIXME: can we do this job when creating the shortcut manager?
161     // A quick look shows that it may require some coordination with the
162     // resource manager.  Startup is complicated, but maybe we can make
163     // it simpler?
164     shortcut_manager& scmgr = m_octave_qobj.get_shortcut_manager ();
165     scmgr.init_data ();
166 
167     construct_central_widget ();
168 
169     m_workspace_model = new workspace_model (m_octave_qobj);
170     m_status_bar = new QStatusBar ();
171     m_command_window = new terminal_dock_widget (this, m_octave_qobj);
172     m_history_window = new history_dock_widget (this, m_octave_qobj);
173     m_file_browser_window = new files_dock_widget (this, m_octave_qobj);
174     m_doc_browser_window = new documentation_dock_widget (this, m_octave_qobj);
175     m_editor_window = create_default_editor (this, m_octave_qobj);
176     m_variable_editor_window = new variable_editor (this, m_octave_qobj);
177     m_workspace_window = new workspace_view (this, m_octave_qobj);
178 
179     m_previous_dock = m_command_window;
180 
181     // Set active editor depending on editor window.  If the latter is
182     // not initialized (qscintilla not present), use the external editor.
183     if (m_editor_window)
184       m_active_editor = m_editor_window;
185     else
186       m_active_editor = m_external_editor;
187 
188 #if defined (HAVE_QGUIAPPLICATION_SETDESKTOPFILENAME)
189     QGuiApplication::setDesktopFileName ("org.octave.Octave.desktop");
190 #endif
191 
192     QApplication *qapp = m_octave_qobj.qapplication ();
193 
194     m_default_style = qapp->style ()->objectName ();
195 
196     gui_settings *settings = rmgr.get_settings ();
197 
198     bool connect_to_web = true;
199     QDateTime last_checked;
200     int serial = 0;
201     m_active_dock = nullptr;
202 
203     if (settings)
204       {
205         connect_to_web
206           = settings->value (nr_allow_connection).toBool ();
207 
208         last_checked
209           = settings->value (nr_last_time).toDateTime ();
210 
211         serial = settings->value (nr_last_news).toInt ();
212         m_default_encoding = settings->value (ed_default_enc).toString ();
213       }
214 
215     QDateTime current = QDateTime::currentDateTime ();
216     QDateTime one_day_ago = current.addDays (-1);
217 
218     if (connect_to_web
219         && (! last_checked.isValid () || one_day_ago > last_checked))
220       load_and_display_community_news (serial);
221 
222     construct_octave_qt_link ();
223 
224     // We have to set up all our windows, before we finally launch
225     // octave.
226 
227     construct ();
228 
229     read_settings ();
230 
231     init_terminal_size ();
232 
233     // Connect signals for changes in visibility now before window is
234     // shown.
235 
236     connect_visibility_changed ();
237 
238     focus_command_window ();
239   }
240 
~main_window(void)241   main_window::~main_window (void)
242   {
243     // Destroy the terminal first so that STDERR stream is redirected back
244     // to its original pipe to capture error messages at exit.
245 
246     delete m_editor_window;     // first one for dialogs of modified editor-tabs
247     delete m_external_editor;
248     delete m_command_window;
249     delete m_workspace_window;
250     delete m_doc_browser_window;
251     delete m_file_browser_window;
252     delete m_history_window;
253     delete m_status_bar;
254     delete m_workspace_model;
255     delete m_variable_editor_window;
256 
257     delete m_find_files_dlg;
258     delete m_release_notes_window;
259     delete m_community_news_window;
260   }
261 
command_window_has_focus(void) const262   bool main_window::command_window_has_focus (void) const
263   {
264     return m_command_window->has_focus ();
265   }
266 
focus_command_window(void)267   void main_window::focus_command_window (void)
268   {
269     m_command_window->activate ();
270   }
271 
focus_window(const QString & win_name)272   void main_window::focus_window (const QString& win_name)
273   {
274     if (win_name == "command")
275       m_command_window->activate ();
276     else if (win_name == "history")
277       m_history_window->activate ();
278     else if (win_name == "workspace")
279       m_workspace_window->activate ();
280     else if (win_name == "filebrowser")
281       m_file_browser_window->activate ();
282   }
283 
confirm_shutdown(void)284   bool main_window::confirm_shutdown (void)
285   {
286     bool closenow = true;
287 
288     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
289     gui_settings *settings = rmgr.get_settings ();
290 
291     if (settings->value (global_prompt_to_exit.key,
292                          global_prompt_to_exit.def).toBool ())
293       {
294         int ans = QMessageBox::question (this, tr ("Octave"),
295                                          tr ("Are you sure you want to exit Octave?"),
296                                          (QMessageBox::Ok
297                                           | QMessageBox::Cancel),
298                                          QMessageBox::Ok);
299 
300         if (ans != QMessageBox::Ok)
301           closenow = false;
302       }
303 
304 #if defined (HAVE_QSCINTILLA)
305     if (closenow)
306       closenow = m_editor_window->check_closing ();
307 #endif
308 
309     return closenow;
310   }
311 
312   // catch focus changes and determine the active dock widget
focus_changed(QWidget *,QWidget * new_widget)313   void main_window::focus_changed (QWidget *, QWidget *new_widget)
314   {
315     // If there is no new widget (e.g., when pressing <alt> and the global
316     // menu gets active), we can return immediately
317     if (! new_widget)
318       return;
319 
320     octave_dock_widget *dock = nullptr;
321     QWidget *w_new = new_widget;  // get a copy of new focus widget
322     QWidget *start = w_new;       // Save it as start of our search
323     int count = 0;                // fallback to prevent endless loop
324 
325     QList<octave_dock_widget *> w_list = dock_widget_list ();
326 
327     while (w_new && w_new != m_main_tool_bar && count < 100)
328       {
329         // Go through all dock widgets and check whether the current widget
330         // with focus is a child of one of them.
331         for (auto w : w_list)
332           {
333             if (w->isAncestorOf (w_new))
334               dock = w;
335           }
336 
337         if (dock)
338           break;
339 
340         // If not yet found (in case w_new is not a child of its dock widget),
341         // test next widget in the focus chain
342         w_new = qobject_cast<QWidget *> (w_new->previousInFocusChain ());
343 
344         // Measures preventing an endless loop
345         if (w_new == start)
346           break;  // We have arrived where we began ==> exit loop
347         count++;  // Limited number of trials
348       }
349 
350     // editor needs extra handling
351     octave_dock_widget *edit_dock_widget
352       = static_cast<octave_dock_widget *> (m_editor_window);
353     // if new dock has focus, emit signal and store active focus
354     // except editor changes to a dialog (dock=0)
355     if ((dock || m_active_dock != edit_dock_widget) && (dock != m_active_dock))
356       {
357         // signal to all dock widgets for updating the style
358         emit active_dock_changed (m_active_dock, dock);
359 
360         if (dock)
361           {
362             QList<QDockWidget *> tabbed = tabifiedDockWidgets (dock);
363             if (tabbed.contains (m_active_dock))
364               dock->set_predecessor_widget (m_active_dock);
365           }
366 
367         if (edit_dock_widget == dock)
368           emit editor_focus_changed (true);
369         else if (edit_dock_widget == m_active_dock)
370           emit editor_focus_changed (false);
371 
372         if (m_active_dock)
373           m_previous_dock = m_active_dock;
374         m_active_dock = dock;
375       }
376   }
377 
request_reload_settings(void)378   void main_window::request_reload_settings (void)
379   {
380     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
381     gui_settings *settings = rmgr.get_settings ();
382 
383     if (settings)
384       emit settings_changed (settings);
385   }
386 
report_status_message(const QString & statusMessage)387   void main_window::report_status_message (const QString& statusMessage)
388   {
389     m_status_bar->showMessage (statusMessage, 1000);
390   }
391 
handle_save_workspace_request(void)392   void main_window::handle_save_workspace_request (void)
393   {
394     // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
395     int opts = 0;  // No options by default.
396     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
397     gui_settings *settings = rmgr.get_settings ();
398     if (! settings->value (global_use_native_dialogs).toBool ())
399       opts = QFileDialog::DontUseNativeDialog;
400 
401     QString file
402       = QFileDialog::getSaveFileName (this, tr ("Save Workspace As"), ".",
403                                       nullptr, nullptr, QFileDialog::Option (opts));
404 
405     if (! file.isEmpty ())
406       {
407         emit interpreter_event
408           ([file] (interpreter& interp)
409            {
410              // INTERPRETER THREAD
411 
412              Fsave (interp, ovl (file.toStdString ()));
413            });
414       }
415   }
416 
handle_load_workspace_request(const QString & file_arg)417   void main_window::handle_load_workspace_request (const QString& file_arg)
418   {
419     // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
420     int opts = 0;  // No options by default.
421     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
422     gui_settings *settings = rmgr.get_settings ();
423     if (! settings->value (global_use_native_dialogs).toBool ())
424       opts = QFileDialog::DontUseNativeDialog;
425 
426     QString file = file_arg;
427 
428     if (file.isEmpty ())
429       file = QFileDialog::getOpenFileName (this, tr ("Load Workspace"), ".",
430                                            nullptr, nullptr, QFileDialog::Option (opts));
431 
432     if (! file.isEmpty ())
433       {
434         emit interpreter_event
435           ([file] (interpreter& interp)
436            {
437              // INTERPRETER THREAD
438 
439              Fload (interp, ovl (file.toStdString ()));
440 
441              tree_evaluator& tw = interp.get_evaluator ();
442 
443              event_manager& xevmgr = interp.get_event_manager ();
444 
445              xevmgr.set_workspace (true, tw.get_symbol_info ());
446            });
447       }
448   }
449 
handle_open_any_request(const QString & file_arg)450   void main_window::handle_open_any_request (const QString& file_arg)
451   {
452     if (! file_arg.isEmpty ())
453       {
454         std::string file = file_arg.toStdString ();
455 
456         emit interpreter_event
457           ([file] (interpreter& interp)
458            {
459              // INTERPRETER THREAD
460 
461              interp.feval ("open", ovl (file));
462 
463              // Update the workspace since open.m may have loaded new
464              // variables.
465              tree_evaluator& tw = interp.get_evaluator ();
466 
467              event_manager& xevmgr = interp.get_event_manager ();
468 
469              xevmgr.set_workspace (true, tw.get_symbol_info ());
470            });
471       }
472   }
473 
handle_clear_workspace_request(void)474   void main_window::handle_clear_workspace_request (void)
475   {
476     emit interpreter_event
477       ([] (interpreter& interp)
478        {
479          // INTERPRETER THREAD
480 
481          Fclear (interp);
482        });
483   }
484 
handle_clear_command_window_request(void)485   void main_window::handle_clear_command_window_request (void)
486   {
487     emit interpreter_event
488       ([] (void)
489        {
490          // INTERPRETER THREAD
491 
492          command_editor::kill_full_line ();
493          command_editor::clear_screen ();
494        });
495   }
496 
handle_clear_history_request(void)497   void main_window::handle_clear_history_request (void)
498   {
499     emit interpreter_event
500       ([] (interpreter& interp)
501        {
502          // INTERPRETER THREAD
503 
504          history_system& history_sys = interp.get_history_system ();
505 
506          history_sys.do_history (ovl ("-c"));
507        });
508   }
509 
handle_undo_request(void)510   void main_window::handle_undo_request (void)
511   {
512     if (command_window_has_focus ())
513       {
514         emit interpreter_event
515           ([] (void)
516            {
517              // INTERPRETER THREAD
518 
519              command_editor::undo ();
520              command_editor::redisplay ();
521            });
522       }
523     else
524       emit undo_signal ();
525   }
526 
handle_rename_variable_request(const QString & old_name_arg,const QString & new_name_arg)527   void main_window::handle_rename_variable_request (const QString& old_name_arg,
528                                                     const QString& new_name_arg)
529 
530   {
531     std::string old_name = old_name_arg.toStdString ();
532     std::string new_name = new_name_arg.toStdString ();
533 
534     emit interpreter_event
535       ([old_name, new_name] (interpreter& interp)
536        {
537          // INTERPRETER THREAD
538 
539          symbol_scope scope = interp.get_current_scope ();
540 
541          if (scope)
542            {
543              scope.rename (old_name, new_name);
544 
545              tree_evaluator& tw = interp.get_evaluator ();
546 
547              event_manager& xevmgr = interp.get_event_manager ();
548 
549              xevmgr.set_workspace (true, tw.get_symbol_info ());
550            }
551 
552          // FIXME: if this action fails, do we need a way to display that info
553          // in the GUI?
554        });
555   }
556 
modify_path(const octave_value_list & dir_list,bool rm,bool subdirs)557   void main_window::modify_path (const octave_value_list& dir_list,
558                                  bool rm, bool subdirs)
559   {
560     emit interpreter_event
561       ([dir_list, rm, subdirs, this] (interpreter& interp)
562       {
563         // INTERPRETER THREAD
564 
565         octave_value_list paths = ovl ();
566 
567         if (subdirs)
568           {
569             // Loop over all directories in order to get all subdirs
570             for (octave_idx_type i = 0; i < dir_list.length (); i++)
571               paths.append (Fgenpath (dir_list(i)));
572           }
573         else
574           paths = dir_list;
575 
576         if (rm)
577           Frmpath (interp, paths);
578         else
579           Faddpath (interp, paths);
580       });
581   }
582 
new_file(const QString & commands)583   void main_window::new_file (const QString& commands)
584   {
585     emit new_file_signal (commands);
586   }
587 
open_file(const QString & file_name,int line)588   void main_window::open_file (const QString& file_name, int line)
589   {
590     if (line < 0)
591       emit open_file_signal (file_name);
592     else
593       emit open_file_signal (file_name, QString (), line);
594   }
595 
edit_mfile(const QString & name,int line)596   void main_window::edit_mfile (const QString& name, int line)
597   {
598     handle_edit_mfile_request (name, QString (), QString (), line);
599   }
600 
file_remove_proxy(const QString & o,const QString & n)601   void main_window::file_remove_proxy (const QString& o, const QString& n)
602   {
603     interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj ();
604 
605     qt_interpreter_events *qt_link = interp_qobj->qt_link ();
606 
607     // Wait for worker to suspend
608     qt_link->lock ();
609 
610     // Close the file if opened
611 #if defined (HAVE_QSCINTILLA)
612     m_editor_window->handle_file_remove (o, n);
613 #else
614     octave_unused_parameter (o);
615     octave_unused_parameter (n);
616 #endif
617 
618     // We are done: Unlock and wake the worker thread
619     qt_link->unlock ();
620     qt_link->wake_all ();
621   }
622 
open_online_documentation_page(void)623   void main_window::open_online_documentation_page (void)
624   {
625     QDesktopServices::openUrl
626       (QUrl ("https://octave.org/doc/interpreter/index.html"));
627   }
628 
display_release_notes(void)629   void main_window::display_release_notes (void)
630   {
631     if (! m_release_notes_window)
632       {
633         std::string news_file = config::oct_etc_dir () + "/NEWS";
634 
635         QString news;
636 
637         QFile *file = new QFile (QString::fromStdString (news_file));
638         if (file->open (QFile::ReadOnly))
639           {
640             QTextStream *stream = new QTextStream (file);
641             news = stream->readAll ();
642             if (! news.isEmpty ())
643               {
644                 // Convert '<', '>' which would be interpreted as HTML
645                 news.replace ("<", "&lt;");
646                 news.replace (">", "&gt;");
647                 // Add HTML tags for pre-formatted text
648                 news.prepend ("<pre>");
649                 news.append ("</pre>");
650               }
651             else
652               news = (tr ("The release notes file '%1' is empty.")
653                       . arg (QString::fromStdString (news_file)));
654           }
655         else
656           news = (tr ("The release notes file '%1' cannot be read.")
657                   . arg (QString::fromStdString (news_file)));
658 
659         m_release_notes_window = new QWidget;
660 
661         QTextBrowser *browser = new QTextBrowser (m_release_notes_window);
662         browser->setText (news);
663 
664         QVBoxLayout *vlayout = new QVBoxLayout;
665         vlayout->addWidget (browser);
666 
667         m_release_notes_window->setLayout (vlayout);
668         m_release_notes_window->setWindowTitle (tr ("Octave Release Notes"));
669 
670         browser->document ()->adjustSize ();
671 
672         int win_x, win_y;
673         get_screen_geometry (&win_x, &win_y);
674 
675         m_release_notes_window->resize (win_x*2/5, win_y*2/3);
676         m_release_notes_window->move (20, 20);  // move to the top left corner
677       }
678 
679     if (! m_release_notes_window->isVisible ())
680       m_release_notes_window->show ();
681     else if (m_release_notes_window->isMinimized ())
682       m_release_notes_window->showNormal ();
683 
684     m_release_notes_window->setWindowIcon (QIcon (m_release_notes_icon));
685 
686     m_release_notes_window->raise ();
687     m_release_notes_window->activateWindow ();
688   }
689 
load_and_display_community_news(int serial)690   void main_window::load_and_display_community_news (int serial)
691   {
692     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
693     gui_settings *settings = rmgr.get_settings ();
694 
695     bool connect_to_web
696       = (settings
697          ? settings->value (nr_allow_connection).toBool ()
698          : true);
699 
700     QString base_url = "https://octave.org";
701     QString page = "community-news.html";
702 
703     QThread *worker_thread = new QThread;
704 
705     news_reader *reader = new news_reader (m_octave_qobj, base_url, page,
706                                            serial, connect_to_web);
707 
708     reader->moveToThread (worker_thread);
709 
710     connect (reader, SIGNAL (display_news_signal (const QString&)),
711              this, SLOT (display_community_news (const QString&)));
712 
713     connect (worker_thread, SIGNAL (started (void)),
714              reader, SLOT (process (void)));
715 
716     connect (reader, SIGNAL (finished (void)), worker_thread, SLOT (quit (void)));
717 
718     connect (reader, SIGNAL (finished (void)), reader, SLOT (deleteLater (void)));
719 
720     connect (worker_thread, SIGNAL (finished (void)),
721              worker_thread, SLOT (deleteLater (void)));
722 
723     worker_thread->start ();
724   }
725 
display_community_news(const QString & news)726   void main_window::display_community_news (const QString& news)
727   {
728     if (! m_community_news_window)
729       {
730         m_community_news_window = new QWidget;
731 
732         QTextBrowser *browser = new QTextBrowser (m_community_news_window);
733 
734         browser->setHtml (news);
735         browser->setObjectName ("OctaveNews");
736         browser->setOpenExternalLinks (true);
737 
738         QVBoxLayout *vlayout = new QVBoxLayout;
739 
740         vlayout->addWidget (browser);
741 
742         m_community_news_window->setLayout (vlayout);
743         m_community_news_window->setWindowTitle (tr ("Octave Community News"));
744 
745         int win_x, win_y;
746         get_screen_geometry (&win_x, &win_y);
747 
748         m_community_news_window->resize (win_x/2, win_y/2);
749         m_community_news_window->move ((win_x - m_community_news_window->width ())/2,
750                                        (win_y - m_community_news_window->height ())/2);
751       }
752     else
753       {
754         // Window already exists, just update the browser contents
755         QTextBrowser *browser
756 
757           = m_community_news_window->findChild<QTextBrowser *>("OctaveNews"
758 #if defined (QOBJECT_FINDCHILDREN_ACCEPTS_FINDCHILDOPTIONS)
759                                                                , Qt::FindDirectChildrenOnly
760 #endif
761                                                               );
762         if (browser)
763           browser->setHtml (news);
764       }
765 
766     if (! m_community_news_window->isVisible ())
767       m_community_news_window->show ();
768     else if (m_community_news_window->isMinimized ())
769       m_community_news_window->showNormal ();
770 
771     // same icon as release notes
772     m_community_news_window->setWindowIcon (QIcon (m_release_notes_icon));
773 
774     m_community_news_window->raise ();
775     m_community_news_window->activateWindow ();
776   }
777 
open_bug_tracker_page(void)778   void main_window::open_bug_tracker_page (void)
779   {
780     QDesktopServices::openUrl (QUrl ("https://octave.org/bugs.html"));
781   }
782 
open_octave_packages_page(void)783   void main_window::open_octave_packages_page (void)
784   {
785     QDesktopServices::openUrl (QUrl ("https://octave.org/packages.html"));
786   }
787 
open_contribute_page(void)788   void main_window::open_contribute_page (void)
789   {
790     QDesktopServices::openUrl (QUrl ("https://octave.org/contribute.html"));
791   }
792 
open_donate_page(void)793   void main_window::open_donate_page (void)
794   {
795     QDesktopServices::openUrl (QUrl ("https://octave.org/donate.html"));
796   }
797 
process_settings_dialog_request(const QString & desired_tab)798   void main_window::process_settings_dialog_request (const QString& desired_tab)
799   {
800     if (m_settings_dlg)  // m_settings_dlg is a guarded pointer!
801       {
802         // here the dialog is still open and called once again
803         if (! desired_tab.isEmpty ())
804           m_settings_dlg->show_tab (desired_tab);
805         return;
806       }
807 
808     m_settings_dlg = new settings_dialog (this, m_octave_qobj, desired_tab);
809 
810     connect (m_settings_dlg, SIGNAL (apply_new_settings (void)),
811              this, SLOT (request_reload_settings (void)));
812 
813     m_settings_dlg->setModal (false);
814     m_settings_dlg->setAttribute (Qt::WA_DeleteOnClose);
815     m_settings_dlg->show ();
816   }
817 
show_about_octave(void)818   void main_window::show_about_octave (void)
819   {
820     std::string message
821       = octave_name_version_copyright_copying_warranty_and_bugs (true);
822 
823     QMessageBox::about (this, tr ("About Octave"),
824                         QString::fromStdString (message));
825   }
826 
notice_settings(const gui_settings * settings,bool update_by_worker)827   void main_window::notice_settings (const gui_settings *settings,
828                                      bool update_by_worker)
829   {
830     if (! settings)
831       return;
832 
833     // Get desired style from preferences or take the default one if
834     // the desired one is not found
835     QString preferred_style = settings->value (global_style).toString ();
836 
837     if (preferred_style == global_style.def.toString ())
838       preferred_style = m_default_style;
839 
840     QStyle *new_style = QStyleFactory::create (preferred_style);
841     if (new_style)
842       {
843         QApplication *qapp = m_octave_qobj.qapplication ();
844 
845         qapp->setStyle (new_style);
846       }
847 
848     // the widget's icons (when floating)
849     QString icon_set
850       = settings->value (dw_icon_set).toString ();
851 
852     int count = 0;
853     int icon_set_found = 0; // default
854 
855     while (! dw_icon_set_names[count].name.isEmpty ())
856       {
857         // while not end of data
858         if (dw_icon_set_names[count].name == icon_set)
859           {
860             // data of desired icon set found
861             icon_set_found = count;
862             break;
863           }
864         count++;
865       }
866 
867     QString icon;
868     for (auto *widget : dock_widget_list ())
869       {
870         QString name = widget->objectName ();
871         if (! name.isEmpty ())
872           {
873             // if child has a name
874             icon = dw_icon_set_names[icon_set_found].path; // prefix | octave-logo
875             if (dw_icon_set_names[icon_set_found].name != "NONE")
876               icon += name + ".png"; // add widget name and ext.
877             widget->setWindowIcon (QIcon (icon));
878           }
879       }
880     if (dw_icon_set_names[icon_set_found].name != "NONE")
881       m_release_notes_icon = dw_icon_set_names[icon_set_found].path
882                              + "ReleaseWidget.png";
883     else
884       m_release_notes_icon = ":/actions/icons/logo.png";
885 
886     int size_idx = settings->value (global_icon_size).toInt ();
887     size_idx = (size_idx > 0) - (size_idx < 0) + 1;  // Make valid index from 0 to 2
888 
889     QStyle *st = style ();
890     int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
891     m_main_tool_bar->setIconSize (QSize (icon_size,icon_size));
892 
893     if (settings->value (global_status_bar.key, global_status_bar.def).toBool ())
894       m_status_bar->show ();
895     else
896       m_status_bar->hide ();
897 
898     m_prevent_readline_conflicts
899       = settings->value (sc_prevent_rl_conflicts.key,
900                          sc_prevent_rl_conflicts.def).toBool ();
901 
902     m_suppress_dbg_location
903       = ! settings->value (cs_dbg_location).toBool ();
904 
905     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
906     rmgr.update_network_settings ();
907 
908     emit active_dock_changed (nullptr, m_active_dock); // update dock widget styles
909 
910     configure_shortcuts ();
911     set_global_shortcuts (m_active_dock == m_command_window);
912     disable_menu_shortcuts (m_active_dock == m_editor_window);
913 
914     // Check whether some octave internal preferences have to be updated
915     QString new_default_encoding
916       = settings->value (ed_default_enc).toString ();
917     // Do not update internal pref only if a) this update was not initiated
918     // by the worker and b) the pref has really changes
919     if (! update_by_worker && (new_default_encoding != m_default_encoding))
920       update_default_encoding (new_default_encoding);
921 
922     // Set cursor blinking depending on the settings
923     // Cursor blinking: consider old terminal related setting if not yet set
924     // TODO: This pref. can be deprecated / removed if Qt adds support for
925     //       getting the cursor blink preferences from all OS environments
926     bool cursor_blinking;
927 
928     if (settings->contains (global_cursor_blinking.key))
929       cursor_blinking = settings->value (global_cursor_blinking).toBool ();
930     else
931       cursor_blinking = settings->value (cs_cursor_blinking).toBool ();
932 
933     if (cursor_blinking)
934       QApplication::setCursorFlashTime (1000);  // 1000 ms flash time
935     else
936       QApplication::setCursorFlashTime (0);  // no flashing
937 
938   }
939 
prepare_to_exit(void)940   void main_window::prepare_to_exit (void)
941   {
942     // Find files dialog is constructed dynamically, not at time of main_window
943     // construction.  Connecting it to qApp aboutToQuit signal would have
944     // caused it to run after gui_settings is deleted.
945     if (m_find_files_dlg)
946       m_find_files_dlg->save_settings ();
947 
948     if (m_set_path_dlg)
949       m_set_path_dlg->save_settings ();
950 
951     write_settings ();
952   }
953 
go_to_previous_widget(void)954   void main_window::go_to_previous_widget (void)
955   {
956     m_previous_dock->activate ();
957   }
958 
update_octave_directory(const QString & dir)959   void main_window::update_octave_directory (const QString& dir)
960   {
961     // Remove existing entry, if any, then add new directory at top and
962     // mark it as the current directory.  Finally, update the file list
963     // widget.
964 
965     int index = m_current_directory_combo_box->findText (dir);
966 
967     if (index >= 0)
968       m_current_directory_combo_box->removeItem (index);
969 
970     m_current_directory_combo_box->insertItem (0, dir);
971     m_current_directory_combo_box->setCurrentIndex (0);
972   }
973 
browse_for_directory(void)974   void main_window::browse_for_directory (void)
975   {
976     // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
977     int opts = QFileDialog::ShowDirsOnly;
978     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
979     gui_settings *settings = rmgr.get_settings ();
980     if (! settings->value (global_use_native_dialogs).toBool ())
981       opts = QFileDialog::DontUseNativeDialog;
982 
983     QString dir
984       = QFileDialog::getExistingDirectory (this, tr ("Browse directories"), nullptr,
985                                            QFileDialog::Option (opts));
986 
987     set_current_working_directory (dir);
988 
989     // FIXME: on Windows systems, the command window freezes after the
990     // previous actions.  Forcing the focus appears to unstick it.
991 
992     focus_command_window ();
993   }
994 
set_current_working_directory(const QString & dir)995   void main_window::set_current_working_directory (const QString& dir)
996   {
997     // Change to dir if it is an existing directory.
998 
999     QString xdir = (dir.isEmpty () ? "." : dir);
1000 
1001     QFileInfo fileInfo (xdir);
1002 
1003     if (fileInfo.exists () && fileInfo.isDir ())
1004       {
1005         emit interpreter_event
1006           ([xdir] (interpreter& interp)
1007            {
1008              // INTERPRETER THREAD
1009 
1010              interp.chdir (xdir.toStdString ());
1011            });
1012       }
1013   }
1014 
change_directory_up(void)1015   void main_window::change_directory_up (void)
1016   {
1017     set_current_working_directory ("..");
1018   }
1019 
1020   // Slot that is called if return is pressed in the line edit of the
1021   // combobox to change to a new directory or a directory that is already
1022   // in the drop down list.
1023 
accept_directory_line_edit(void)1024   void main_window::accept_directory_line_edit (void)
1025   {
1026     // Get new directory name, and change to it if it is new.  Otherwise,
1027     // the combo box will trigger the "activated" signal to change to the
1028     // directory.
1029 
1030     QString dir = m_current_directory_combo_box->currentText ();
1031 
1032     int index = m_current_directory_combo_box->findText (dir);
1033 
1034     if (index < 0)
1035       set_current_working_directory (dir);
1036   }
1037 
execute_command_in_terminal(const QString & command)1038   void main_window::execute_command_in_terminal (const QString& command)
1039   {
1040     emit interpreter_event
1041       ([command] (void)
1042        {
1043          // INTERPRETER THREAD
1044 
1045          std::string pending_input = command_editor::get_current_line ();
1046 
1047          command_editor::set_initial_input (pending_input);
1048          command_editor::replace_line (command.toStdString ());
1049          command_editor::redisplay ();
1050          command_editor::interrupt_event_loop ();
1051          command_editor::accept_line ();
1052        });
1053 
1054     focus_console_after_command ();
1055   }
1056 
run_file_in_terminal(const QFileInfo & info)1057   void main_window::run_file_in_terminal (const QFileInfo& info)
1058   {
1059     emit interpreter_event
1060       ([info] (interpreter& interp)
1061        {
1062          // INTERPRETER THREAD
1063 
1064          QString function_name = info.fileName ();
1065          function_name.chop (info.suffix ().length () + 1);
1066          std::string file_path = info.absoluteFilePath ().toStdString ();
1067 
1068          std::string pending_input = command_editor::get_current_line ();
1069 
1070          if (valid_identifier (function_name.toStdString ()))
1071            {
1072              // Valid identifier: call as function with possibility to
1073              // debug.
1074 
1075              load_path& lp = interp.get_load_path ();
1076 
1077              std::string path = info.absolutePath ().toStdString ();
1078 
1079              if (lp.contains_file_in_dir (file_path, path))
1080                command_editor::replace_line (function_name.toStdString ());
1081            }
1082          else
1083            {
1084              // No valid identifier: use equivalent of Fsource (), no
1085              // debug possible.
1086 
1087              interp.source_file (file_path);
1088 
1089              command_editor::replace_line ("");
1090            }
1091 
1092          command_editor::set_initial_input (pending_input);
1093          command_editor::redisplay ();
1094          command_editor::interrupt_event_loop ();
1095          command_editor::accept_line ();
1096        });
1097 
1098     focus_console_after_command ();
1099   }
1100 
handle_new_figure_request(void)1101   void main_window::handle_new_figure_request (void)
1102   {
1103     emit interpreter_event
1104       ([] (interpreter& interp)
1105        {
1106          // INTERPRETER THREAD
1107 
1108          Fbuiltin (interp, ovl ("figure"));
1109          Fdrawnow (interp);
1110        });
1111   }
1112 
handle_enter_debugger(void)1113   void main_window::handle_enter_debugger (void)
1114   {
1115     setWindowTitle ("Octave (Debugging)");
1116 
1117     m_debug_continue->setEnabled (true);
1118     m_debug_step_into->setEnabled (true);
1119     m_debug_step_over->setEnabled (true);
1120     m_debug_step_out->setEnabled (true);
1121     m_debug_quit->setEnabled (true);
1122 
1123 #if defined (HAVE_QSCINTILLA)
1124     m_editor_window->handle_enter_debug_mode ();
1125 #endif
1126   }
1127 
handle_exit_debugger(void)1128   void main_window::handle_exit_debugger (void)
1129   {
1130     setWindowTitle ("Octave");
1131 
1132     m_debug_continue->setEnabled (false);
1133     m_debug_step_into->setEnabled (false);
1134     m_debug_step_over->setEnabled (m_editor_has_tabs);
1135     m_debug_step_out->setEnabled (false);
1136     m_debug_quit->setEnabled (false);
1137 
1138 #if defined (HAVE_QSCINTILLA)
1139     m_editor_window->handle_exit_debug_mode ();
1140 #endif
1141   }
1142 
debug_continue(void)1143   void main_window::debug_continue (void)
1144   {
1145     emit interpreter_event
1146       ([this] (interpreter& interp)
1147        {
1148          // INTERPRETER THREAD
1149 
1150          F__db_next_breakpoint_quiet__ (interp, ovl (m_suppress_dbg_location));
1151          Fdbcont (interp);
1152 
1153          command_editor::interrupt (true);
1154        });
1155   }
1156 
debug_step_into(void)1157   void main_window::debug_step_into (void)
1158   {
1159     emit interpreter_event
1160       ([this] (interpreter& interp)
1161        {
1162          // INTERPRETER THREAD
1163 
1164          F__db_next_breakpoint_quiet__ (interp, ovl (m_suppress_dbg_location));
1165          Fdbstep (interp, ovl ("in"));
1166 
1167          command_editor::interrupt (true);
1168        });
1169   }
1170 
debug_step_over(void)1171   void main_window::debug_step_over (void)
1172   {
1173     if (m_debug_quit->isEnabled ())
1174       {
1175         // We are in debug mode, just call dbstep.
1176 
1177         emit interpreter_event
1178           ([this] (interpreter& interp)
1179            {
1180              // INTERPRETER THREAD
1181 
1182              F__db_next_breakpoint_quiet__ (interp,
1183                                             ovl (m_suppress_dbg_location));
1184              Fdbstep (interp);
1185 
1186              command_editor::interrupt (true);
1187            });
1188       }
1189     else
1190       {
1191         // Not in debug mode: "step into" the current editor file
1192         emit step_into_file_signal ();
1193       }
1194   }
1195 
debug_step_out(void)1196   void main_window::debug_step_out (void)
1197   {
1198     emit interpreter_event
1199       ([this] (interpreter& interp)
1200        {
1201          // INTERPRETER THREAD
1202 
1203          F__db_next_breakpoint_quiet__ (interp, ovl (m_suppress_dbg_location));
1204          Fdbstep (interp, ovl ("out"));
1205 
1206          command_editor::interrupt (true);
1207        });
1208   }
1209 
debug_quit(void)1210   void main_window::debug_quit (void)
1211   {
1212     emit interpreter_event
1213       ([] (interpreter& interp)
1214        {
1215          // INTERPRETER THREAD
1216 
1217          Fdbquit (interp);
1218 
1219          command_editor::interrupt (true);
1220        });
1221   }
1222 
1223   //
1224   // Functions related to file editing
1225   //
1226   // These are moved from editor to here for also using them when octave
1227   // is built without qscintilla
1228   //
request_open_file(void)1229   void main_window::request_open_file (void)
1230   {
1231     // Open file isn't a file_editor_tab or editor function since the file
1232     // might be opened in an external editor.  Hence, functionality is here.
1233 
1234     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1235     gui_settings *settings = rmgr.get_settings ();
1236     bool is_internal = m_editor_window
1237                        && ! settings->value (global_use_custom_editor.key,
1238                                              global_use_custom_editor.def).toBool ();
1239 
1240     // Create a NonModal message.
1241     QWidget *p = this;
1242     if (is_internal)
1243       p = m_editor_window;
1244     QFileDialog *fileDialog = new QFileDialog (p);
1245     fileDialog->setNameFilter (tr ("Octave Files (*.m);;All Files (*)"));
1246 
1247     fileDialog->setAcceptMode (QFileDialog::AcceptOpen);
1248     fileDialog->setViewMode (QFileDialog::Detail);
1249     fileDialog->setFileMode (QFileDialog::ExistingFiles);
1250     fileDialog->setDirectory (m_current_directory_combo_box->itemText (0));
1251 
1252     // FIXME: Remove, if for all common KDE versions (bug #54607) is resolved.
1253     if (! settings->value (global_use_native_dialogs).toBool ())
1254       fileDialog->setOption(QFileDialog::DontUseNativeDialog);
1255 
1256     connect (fileDialog, SIGNAL (filesSelected (const QStringList&)),
1257              this, SLOT (request_open_files (const QStringList&)));
1258 
1259     fileDialog->setWindowModality (Qt::NonModal);
1260     fileDialog->setAttribute (Qt::WA_DeleteOnClose);
1261     fileDialog->show ();
1262   }
1263 
1264   // Create a new script
request_new_script(const QString & commands)1265   void main_window::request_new_script (const QString& commands)
1266   {
1267     emit new_file_signal (commands);
1268   }
1269 
1270   // Create a new function and open it
request_new_function(bool)1271   void main_window::request_new_function (bool)
1272   {
1273     bool ok;
1274     // Get the name of the new function: Parent of the input dialog is the
1275     // editor window or the main window.  The latter is chosen, if a custom
1276     // editor is used or qscintilla is not available
1277     QWidget *p = m_editor_window;
1278     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1279     gui_settings *settings = rmgr.get_settings ();
1280     if (! p || settings->value (global_use_custom_editor.key,
1281                                 global_use_custom_editor.def).toBool ())
1282       p = this;
1283     QString new_name = QInputDialog::getText (p, tr ("New Function"),
1284                                               tr ("New function name:\n"), QLineEdit::Normal, "", &ok);
1285 
1286     if (ok && new_name.length () > 0)
1287       {
1288         // append suffix if it does not already exist
1289         if (new_name.rightRef (2) != ".m")
1290           new_name.append (".m");
1291         // check whether new files are created without prompt
1292         if (! settings->value (ed_create_new_file).toBool ())
1293           {
1294             // no, so enable this settings and wait for end of new file loading
1295             settings->setValue (ed_create_new_file.key, true);
1296             connect (m_editor_window, SIGNAL (file_loaded_signal (void)),
1297                      this, SLOT (restore_create_file_setting (void)));
1298           }
1299         // start the edit command
1300         execute_command_in_terminal ("edit " + new_name);
1301       }
1302   }
1303 
handle_edit_mfile_request(const QString & fname,const QString & ffile,const QString & curr_dir,int line)1304   void main_window::handle_edit_mfile_request (const QString& fname,
1305                                                const QString& ffile,
1306                                                const QString& curr_dir,
1307                                                int line)
1308   {
1309     emit interpreter_event
1310       ([this, fname, ffile, curr_dir, line] (interpreter& interp)
1311        {
1312          // INTERPRETER THREAD
1313 
1314          // Split possible subfunctions
1315          QStringList fcn_list = fname.split ('>');
1316          QString fcn_name = fcn_list.at (0) + ".m";
1317 
1318          // FIXME: could use symbol_exist directly, but we may also want
1319          // to fix that to be a member function in the interpreter
1320          // class?
1321 
1322          // Is it a regular function within the search path? (Call Fexist)
1323          octave_value_list fct = Fexist (interp, ovl (fname.toStdString ()),0);
1324          int type = fct (0).int_value ();
1325 
1326          QString message = QString ();
1327          QString filename = QString ();
1328 
1329          switch (type)
1330            {
1331            case 3:
1332            case 5:
1333            case 103:
1334              message = tr ("%1 is a built-in, compiled or inline\n"
1335                            "function and can not be edited.");
1336              break;
1337 
1338            case 2:
1339              // FIXME: could use a load_path function directly.
1340              octave_value_list file_path
1341                = Ffile_in_loadpath (interp, ovl (fcn_name.toStdString ()), 0);
1342              if (file_path.length () > 0)
1343                filename = QString::fromStdString (file_path (0).string_value ());
1344              break;
1345            }
1346 
1347          if (filename.isEmpty () && message.isEmpty ())
1348            {
1349              // No error so far, but function still not known
1350              // -> try directory of edited file
1351              // get directory
1352              QDir dir;
1353              if (ffile.isEmpty ())
1354                {
1355                  if (curr_dir.isEmpty ())
1356                    dir = QDir (m_current_directory_combo_box->itemText (0));
1357                  else
1358                    dir = QDir (curr_dir);
1359                }
1360              else
1361                dir = QDir (QFileInfo (ffile).canonicalPath ());
1362 
1363              QFileInfo file = QFileInfo (dir, fcn_name);
1364              if (file.exists ())
1365                filename = file.canonicalFilePath (); // local file exists
1366              else
1367                {
1368                  // local file does not exist -> try private directory
1369                  file = QFileInfo (ffile);
1370                  file = QFileInfo (QDir (file.canonicalPath () + "/private"),
1371                                    fcn_name);
1372                  if (file.exists ())
1373                    filename = file.canonicalFilePath ();  // private function exists
1374                  else
1375                    message = tr ("Can not find function %1");  // no file found
1376 
1377                }
1378            }
1379 
1380          if (! message.isEmpty ())
1381            {
1382              emit warning_function_not_found_signal (message.arg (fname));
1383              return;
1384            }
1385 
1386          if (! filename.endsWith (".m"))
1387            filename.append (".m");
1388 
1389          // default encoding
1390          emit open_file_signal (filename, QString (), line);
1391        });
1392   }
1393 
warning_function_not_found(const QString & message)1394   void main_window::warning_function_not_found (const QString& message)
1395   {
1396     QMessageBox *msgBox = new QMessageBox (QMessageBox::Critical,
1397                                            tr ("Octave Editor"),
1398                                            message, QMessageBox::Ok, this);
1399     msgBox->setWindowModality (Qt::NonModal);
1400     msgBox->setAttribute (Qt::WA_DeleteOnClose);
1401     msgBox->show ();
1402   }
1403 
handle_insert_debugger_pointer_request(const QString & file,int line)1404   void main_window::handle_insert_debugger_pointer_request (const QString& file,
1405                                                             int line)
1406   {
1407     bool cmd_focus = command_window_has_focus ();
1408 
1409     emit insert_debugger_pointer_signal (file, line);
1410 
1411     if (cmd_focus)
1412       focus_command_window ();
1413   }
1414 
handle_delete_debugger_pointer_request(const QString & file,int line)1415   void main_window::handle_delete_debugger_pointer_request (const QString& file,
1416                                                             int line)
1417   {
1418     bool cmd_focus = command_window_has_focus ();
1419 
1420     emit delete_debugger_pointer_signal (file, line);
1421 
1422     if (cmd_focus)
1423       focus_command_window ();
1424   }
1425 
handle_update_breakpoint_marker_request(bool insert,const QString & file,int line,const QString & cond)1426   void main_window::handle_update_breakpoint_marker_request (bool insert,
1427                                                              const QString& file,
1428                                                              int line,
1429                                                              const QString& cond)
1430   {
1431     bool cmd_focus = command_window_has_focus ();
1432 
1433     emit update_breakpoint_marker_signal (insert, file, line, cond);
1434 
1435     if (cmd_focus)
1436       focus_command_window ();
1437   }
1438 
read_settings(void)1439   void main_window::read_settings (void)
1440   {
1441     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1442     gui_settings *settings = rmgr.get_settings ();
1443 
1444     if (! settings)
1445       {
1446         qDebug ("Error: gui_settings pointer from resource manager is NULL.");
1447         return;
1448       }
1449 
1450     set_window_layout (settings);
1451 
1452     // restore the list of the last directories
1453     QStringList curr_dirs = settings->value (mw_dir_list).toStringList ();
1454     for (int i=0; i < curr_dirs.size (); i++)
1455       {
1456         m_current_directory_combo_box->addItem (curr_dirs.at (i));
1457       }
1458     emit settings_changed (settings);
1459   }
1460 
init_terminal_size(void)1461   void main_window::init_terminal_size (void)
1462   {
1463     emit init_terminal_size_signal ();
1464   }
1465 
set_window_layout(gui_settings * settings)1466   void main_window::set_window_layout (gui_settings *settings)
1467   {
1468     // For resetting from some inconsistent state, first reset layout
1469     // without saving or showing it
1470     do_reset_windows (true, false);
1471 
1472     // Restore main window state and geometry from settings file or, in case
1473     // of an error (no pref values yet), from the default layout.
1474     if (! restoreGeometry (settings->value (mw_geometry).toByteArray ()))
1475       {
1476         do_reset_windows (true);
1477         return;
1478       }
1479 
1480     if (isMaximized())
1481       {
1482         setGeometry( QApplication::desktop ()->availableGeometry (this));
1483       }
1484 
1485     if (! restoreState (settings->value (mw_state).toByteArray ()))
1486       {
1487         do_reset_windows (true);
1488         return;
1489       }
1490 
1491     // Restore the geometry of all dock-widgets
1492     for (auto *widget : dock_widget_list ())
1493       {
1494         QString name = widget->objectName ();
1495 
1496         if (! name.isEmpty ())
1497           {
1498             bool floating = false;
1499             bool visible = true;
1500 
1501             floating = settings->value
1502                 (dw_is_floating.key.arg (name), dw_is_floating.def).toBool ();
1503             visible = settings->value
1504                 (dw_is_visible.key.arg (name), dw_is_visible.def).toBool ();
1505 
1506             // If floating, make window from widget.
1507             if (floating)
1508               {
1509                 widget->make_window ();
1510 
1511                 if (visible)
1512                   {
1513                     if (settings->value (dw_is_minimized.key.arg (name),
1514                                          dw_is_minimized.def).toBool ())
1515                       widget->showMinimized ();
1516                     else
1517                       widget->setVisible (true);
1518                   }
1519                 else
1520                   widget->setVisible (false);
1521               }
1522             else  // not floating
1523               {
1524                 if (! widget->parent ())        // should not be floating but is
1525                   widget->make_widget (false);  // no docking, just reparent
1526 
1527                 widget->make_widget ();
1528                 widget->setVisible (visible);   // not floating -> show
1529               }
1530           }
1531       }
1532 
1533     show ();
1534   }
1535 
write_settings(void)1536   void main_window::write_settings (void)
1537   {
1538     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1539     gui_settings *settings = rmgr.get_settings ();
1540     if (! settings)
1541       {
1542         qDebug ("Error: gui_settings pointer from resource manager is NULL.");
1543         return;
1544       }
1545 
1546     settings->setValue (mw_geometry.key, saveGeometry ());
1547     settings->setValue (mw_state.key, saveState ());
1548     // write the list of recently used directories
1549     QStringList curr_dirs;
1550     for (int i=0; i<m_current_directory_combo_box->count (); i++)
1551       {
1552         curr_dirs.append (m_current_directory_combo_box->itemText (i));
1553       }
1554     settings->setValue (mw_dir_list.key, curr_dirs);
1555     settings->sync ();
1556   }
1557 
1558   // Connecting the signals emitted when the visibility of a widget changes.
1559   // This has to be done after the window is shown (see octave-gui.cc)
connect_visibility_changed(void)1560   void main_window::connect_visibility_changed (void)
1561   {
1562     for (auto *widget : dock_widget_list ())
1563       widget->connect_visibility_changed ();
1564 
1565 #if defined (HAVE_QSCINTILLA)
1566     m_editor_window->enable_menu_shortcuts (false);
1567 #endif
1568   }
1569 
copyClipboard(void)1570   void main_window::copyClipboard (void)
1571   {
1572     if (m_current_directory_combo_box->hasFocus ())
1573       {
1574         QLineEdit *edit = m_current_directory_combo_box->lineEdit ();
1575         if (edit && edit->hasSelectedText ())
1576           {
1577             QClipboard *clipboard = QApplication::clipboard ();
1578             clipboard->setText (edit->selectedText ());
1579           }
1580       }
1581     else
1582       emit copyClipboard_signal ();
1583   }
1584 
pasteClipboard(void)1585   void main_window::pasteClipboard (void)
1586   {
1587     if (m_current_directory_combo_box->hasFocus ())
1588       {
1589         QLineEdit *edit = m_current_directory_combo_box->lineEdit ();
1590         QClipboard *clipboard = QApplication::clipboard ();
1591         QString str = clipboard->text ();
1592         if (edit && str.length () > 0)
1593           {
1594             edit->insert (str);
1595           }
1596       }
1597     else
1598       emit pasteClipboard_signal ();
1599   }
1600 
selectAll(void)1601   void main_window::selectAll (void)
1602   {
1603     if (m_current_directory_combo_box->hasFocus ())
1604       {
1605         QLineEdit *edit = m_current_directory_combo_box->lineEdit ();
1606         if (edit)
1607           {
1608             edit->selectAll ();
1609           }
1610       }
1611     else
1612       emit selectAll_signal ();
1613   }
1614 
handle_show_doc(const QString & file)1615   void main_window::handle_show_doc (const QString& file)
1616   {
1617     m_doc_browser_window->setVisible (true);
1618     emit show_doc_signal (file);
1619   }
1620 
handle_register_doc(const QString & file)1621   void main_window::handle_register_doc (const QString& file)
1622   {
1623     emit register_doc_signal (file);
1624   }
1625 
handle_unregister_doc(const QString & file)1626   void main_window::handle_unregister_doc (const QString& file)
1627   {
1628     emit unregister_doc_signal (file);
1629   }
1630 
handle_octave_ready(void)1631   void main_window::handle_octave_ready (void)
1632   {
1633     // actions after the startup files are executed
1634     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1635     gui_settings *settings = rmgr.get_settings ();
1636 
1637     QDir startup_dir = QDir ();    // current octave dir after startup
1638 
1639     if (settings)
1640       {
1641         if (settings->value (global_restore_ov_dir).toBool ())
1642           {
1643             // restore last dir from previous session
1644             QStringList curr_dirs
1645               = settings->value (mw_dir_list).toStringList ();
1646             if (curr_dirs.length () > 0)
1647               startup_dir = QDir (curr_dirs.at (0));  // last dir prev. session
1648           }
1649         else if (! settings->value (global_ov_startup_dir).toString ().isEmpty ())
1650           {
1651             // do not restore but there is a startup dir configured
1652             startup_dir
1653               = QDir (settings->value (global_ov_startup_dir).toString ());
1654           }
1655 
1656         update_default_encoding (settings->value (ed_default_enc).toString ());
1657       }
1658 
1659     if (! startup_dir.exists ())
1660       {
1661         // the configured startup dir does not exist, take actual one
1662         startup_dir = QDir ();
1663       }
1664 
1665     set_current_working_directory (startup_dir.absolutePath ());
1666 
1667     if (m_editor_window)
1668       {
1669 #if defined (HAVE_QSCINTILLA)
1670         // Octave ready, determine whether to create an empty script.
1671         // This can not be done when the editor is created because all functions
1672         // must be known for the lexer's auto completion information
1673         m_editor_window->empty_script (true, false);
1674         m_editor_window->restore_session (settings);
1675 #endif
1676       }
1677 
1678     focus_command_window ();  // make sure that the command window has focus
1679   }
1680 
handle_set_path_dialog_request(void)1681   void main_window::handle_set_path_dialog_request (void)
1682   {
1683     if (m_set_path_dlg)  // m_set_path_dlg is a guarded pointer!
1684       return;
1685 
1686     m_set_path_dlg = new set_path_dialog (this, m_octave_qobj);
1687 
1688     m_set_path_dlg->setModal (false);
1689     m_set_path_dlg->setAttribute (Qt::WA_DeleteOnClose);
1690     m_set_path_dlg->show ();
1691 
1692     // Any interpreter_event signal from a set_path_dialog object is
1693     // handled the same as for the main_window object.
1694 
1695     connect (m_set_path_dlg, SIGNAL (interpreter_event (const fcn_callback&)),
1696              this, SIGNAL (interpreter_event (const fcn_callback&)));
1697 
1698     connect (m_set_path_dlg, SIGNAL (interpreter_event (const meth_callback&)),
1699              this, SIGNAL (interpreter_event (const meth_callback&)));
1700 
1701     connect (m_set_path_dlg,
1702              SIGNAL (modify_path_signal (const octave_value_list&, bool, bool)),
1703              this, SLOT (modify_path (const octave_value_list&, bool, bool)));
1704 
1705     interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj ();
1706 
1707     qt_interpreter_events *qt_link = interp_qobj->qt_link ();
1708 
1709     connect (qt_link, SIGNAL (update_path_dialog_signal (void)),
1710              m_set_path_dlg, SLOT (update_model (void)));
1711 
1712     // Now that all the signal connections are in place for the dialog
1713     // we can set the initial value of the path in the model.
1714 
1715     m_set_path_dlg->update_model ();
1716   }
1717 
find_files(const QString & start_dir)1718   void main_window::find_files (const QString& start_dir)
1719   {
1720 
1721     if (! m_find_files_dlg)
1722       {
1723         m_find_files_dlg = new find_files_dialog (this, m_octave_qobj);
1724 
1725         connect (m_find_files_dlg, SIGNAL (finished (int)),
1726                  this, SLOT (find_files_finished (int)));
1727 
1728         connect (m_find_files_dlg, SIGNAL (dir_selected (const QString &)),
1729                  m_file_browser_window,
1730                  SLOT (set_current_directory (const QString&)));
1731 
1732         connect (m_find_files_dlg, SIGNAL (file_selected (const QString &)),
1733                  this, SLOT (open_file (const QString &)));
1734 
1735         m_find_files_dlg->setWindowModality (Qt::NonModal);
1736       }
1737 
1738     if (! m_find_files_dlg->isVisible ())
1739       {
1740         m_find_files_dlg->show ();
1741       }
1742 
1743     m_find_files_dlg->set_search_dir (start_dir);
1744 
1745     m_find_files_dlg->activateWindow ();
1746 
1747   }
1748 
set_global_shortcuts(bool set_shortcuts)1749   void main_window::set_global_shortcuts (bool set_shortcuts)
1750   {
1751     // this slot is called when the terminal gets/loses focus
1752 
1753     // return if the user doesn't want to use readline shortcuts
1754     if (! m_prevent_readline_conflicts)
1755       return;
1756 
1757     if (set_shortcuts)
1758       {
1759         // terminal loses focus: set the global shortcuts
1760         configure_shortcuts ();
1761       }
1762     else
1763       {
1764         // terminal gets focus: disable some shortcuts
1765         QKeySequence no_key = QKeySequence ();
1766 
1767         // file menu
1768         m_open_action->setShortcut (no_key);
1769         m_new_script_action->setShortcut (no_key);
1770         m_new_function_action->setShortcut (no_key);
1771         m_new_figure_action->setShortcut (no_key);
1772         m_load_workspace_action->setShortcut (no_key);
1773         m_save_workspace_action->setShortcut (no_key);
1774         m_preferences_action->setShortcut (no_key);
1775         m_set_path_action->setShortcut (no_key);
1776         m_exit_action->setShortcut (no_key);
1777 
1778         // edit menu
1779         m_select_all_action->setShortcut (no_key);
1780         m_clear_clipboard_action->setShortcut (no_key);
1781         m_find_files_action->setShortcut (no_key);
1782         m_clear_command_history_action->setShortcut (no_key);
1783         m_clear_command_window_action->setShortcut (no_key);
1784         m_clear_workspace_action->setShortcut (no_key);
1785 
1786         // window menu
1787         m_reset_windows_action->setShortcut (no_key);
1788 
1789         // help menu
1790         m_ondisk_doc_action->setShortcut (no_key);
1791         m_online_doc_action->setShortcut (no_key);
1792         m_report_bug_action->setShortcut (no_key);
1793         m_octave_packages_action->setShortcut (no_key);
1794         m_contribute_action->setShortcut (no_key);
1795         m_developer_action->setShortcut (no_key);
1796         m_about_octave_action->setShortcut (no_key);
1797 
1798         // news menu
1799         m_release_notes_action->setShortcut (no_key);
1800         m_current_news_action->setShortcut (no_key);
1801       }
1802   }
1803 
set_screen_size(int ht,int wd)1804   void main_window::set_screen_size (int ht, int wd)
1805   {
1806     emit interpreter_event
1807       ([ht, wd] (void)
1808        {
1809          // INTERPRETER THREAD
1810 
1811          command_editor::set_screen_size (ht, wd);
1812        });
1813   }
1814 
clipboard_has_changed(void)1815   void main_window::clipboard_has_changed (void)
1816   {
1817     if (m_clipboard->text ().isEmpty ())
1818       {
1819         m_paste_action->setEnabled (false);
1820         m_clear_clipboard_action->setEnabled (false);
1821       }
1822     else
1823       {
1824         m_paste_action->setEnabled (true);
1825         m_clear_clipboard_action->setEnabled (true);
1826       }
1827   }
1828 
clear_clipboard(void)1829   void main_window::clear_clipboard (void)
1830   {
1831     m_clipboard->clear (QClipboard::Clipboard);
1832   }
1833 
disable_menu_shortcuts(bool disable)1834   void main_window::disable_menu_shortcuts (bool disable)
1835   {
1836     QHash<QMenu*, QStringList>::const_iterator i = m_hash_menu_text.constBegin ();
1837 
1838     while (i != m_hash_menu_text.constEnd ())
1839       {
1840         i.key ()->setTitle (i.value ().at (disable));
1841         ++i;
1842       }
1843   }
1844 
restore_create_file_setting(void)1845   void main_window::restore_create_file_setting (void)
1846   {
1847     // restore the new files creation setting
1848     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1849     gui_settings *settings = rmgr.get_settings ();
1850     settings->setValue (ed_create_new_file.key, false);
1851     disconnect (m_editor_window, SIGNAL (file_loaded_signal (void)),
1852                 this, SLOT (restore_create_file_setting (void)));
1853   }
1854 
set_file_encoding(const QString & new_encoding)1855   void main_window::set_file_encoding (const QString& new_encoding)
1856   {
1857     m_file_encoding = new_encoding;
1858   }
1859 
1860   // The following slot is called after files have been selected in the
1861   // open file dialog, possibly with a new selected encoding stored in
1862   // m_file_encoding
request_open_files(const QStringList & open_file_names)1863   void main_window::request_open_files (const QStringList& open_file_names)
1864   {
1865     for (int i = 0; i < open_file_names.count (); i++)
1866       emit open_file_signal (open_file_names.at (i), m_file_encoding, -1);
1867   }
1868 
edit_variable(const QString & expr,const octave_value & val)1869   void main_window::edit_variable (const QString &expr, const octave_value& val)
1870   {
1871     m_variable_editor_window->edit_variable (expr, val);
1872 
1873     if (! m_variable_editor_window->isVisible ())
1874       {
1875         m_variable_editor_window->show ();
1876         m_variable_editor_window->raise ();
1877       }
1878 
1879   }
1880 
refresh_variable_editor(void)1881   void main_window::refresh_variable_editor (void)
1882   {
1883     m_variable_editor_window->refresh ();
1884   }
1885 
handle_variable_editor_update(void)1886   void main_window::handle_variable_editor_update (void)
1887   {
1888     // Called when the variable editor emits the updated signal.  The size
1889     // of a variable may have changed, so we refresh the workspace in the
1890     // interpreter.  That will eventually cause the workspace view in the
1891     // GUI to be updated.
1892 
1893     emit interpreter_event
1894       ([] (interpreter& interp)
1895        {
1896          // INTERPRETER THREAD
1897 
1898          tree_evaluator& tw = interp.get_evaluator ();
1899 
1900          event_manager& xevmgr = interp.get_event_manager ();
1901 
1902          xevmgr.set_workspace (true, tw.get_symbol_info (), false);
1903        });
1904   }
1905 
closeEvent(QCloseEvent * e)1906   void main_window::closeEvent (QCloseEvent *e)
1907   {
1908     if (confirm_shutdown ())
1909       {
1910         // FIXME: Instead of ignoring the event and posting an
1911         // interpreter event, should we just accept the event and
1912         // shutdown and clean up the interprter as part of closing the
1913         // GUI?  Going that route might make it easier to close the GUI
1914         // without having to stop the interpreter, for example, if the
1915         // GUI is started from the interpreter command line.
1916 
1917         e->ignore ();
1918 
1919         emit interpreter_event
1920           ([] (interpreter& interp)
1921            {
1922              // INTERPRETER THREAD
1923 
1924              interp.quit (0, false, false);
1925            });
1926       }
1927     else
1928       e->ignore ();
1929   }
1930 
construct_central_widget(void)1931   void main_window::construct_central_widget (void)
1932   {
1933     // Create and set the central widget.  QMainWindow takes ownership of
1934     // the widget (pointer) so there is no need to delete the object upon
1935     // destroying this main_window.
1936 
1937     QWidget *dummyWidget = new QWidget ();
1938     dummyWidget->setObjectName ("CentralDummyWidget");
1939     dummyWidget->resize (10, 10);
1940     dummyWidget->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
1941     dummyWidget->hide ();
1942     setCentralWidget (dummyWidget);
1943   }
1944 
1945 // Main subroutine of the constructor
1946 
construct(void)1947   void main_window::construct (void)
1948   {
1949     setWindowIcon (QIcon (":/actions/icons/logo.png"));
1950 
1951     m_workspace_window->setModel (m_workspace_model);
1952 
1953     connect (m_workspace_model, SIGNAL (model_changed (void)),
1954              m_workspace_window, SLOT (handle_model_changed (void)));
1955 
1956     interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj ();
1957 
1958     qt_interpreter_events *qt_link = interp_qobj->qt_link ();
1959 
1960     connect (qt_link,
1961              SIGNAL (edit_variable_signal (const QString&,
1962                                            const octave_value&)),
1963              this,
1964              SLOT (edit_variable (const QString&, const octave_value&)));
1965 
1966     connect (qt_link, SIGNAL (refresh_variable_editor_signal (void)),
1967              this, SLOT (refresh_variable_editor (void)));
1968 
1969     connect (m_workspace_window,
1970              SIGNAL (rename_variable_signal (const QString&, const QString&)),
1971              this,
1972              SLOT (handle_rename_variable_request (const QString&,
1973                                                    const QString&)));
1974 
1975     connect (m_variable_editor_window, SIGNAL (updated (void)),
1976              this, SLOT (handle_variable_editor_update (void)));
1977 
1978     construct_menu_bar ();
1979 
1980     construct_tool_bar ();
1981 
1982     // Order is important.  Deleting gui_settings must be last.
1983     connect (qApp, SIGNAL (aboutToQuit (void)),
1984              m_command_window, SLOT (save_settings (void)));
1985 
1986     connect (qApp, SIGNAL (aboutToQuit (void)),
1987              m_history_window, SLOT (save_settings (void)));
1988 
1989     connect (qApp, SIGNAL (aboutToQuit (void)),
1990              m_file_browser_window, SLOT (save_settings (void)));
1991 
1992     connect (qApp, SIGNAL (aboutToQuit (void)),
1993              m_doc_browser_window, SLOT (save_settings (void)));
1994 
1995     connect (qApp, SIGNAL (aboutToQuit (void)),
1996              m_workspace_window, SLOT (save_settings (void)));
1997 
1998     connect (qApp, SIGNAL (aboutToQuit (void)),
1999              m_editor_window, SLOT (save_settings (void)));
2000 
2001     connect (qApp, SIGNAL (aboutToQuit (void)),
2002              m_variable_editor_window, SLOT (save_settings (void)));
2003 
2004     connect (qApp, SIGNAL (aboutToQuit (void)),
2005              this, SLOT (prepare_to_exit (void)));
2006 
2007     connect (qApp, SIGNAL (focusChanged (QWidget*, QWidget*)),
2008              this, SLOT (focus_changed (QWidget*, QWidget*)));
2009 
2010     connect (this, SIGNAL (settings_changed (const gui_settings *)),
2011              this, SLOT (notice_settings (const gui_settings *)));
2012 
2013     connect (this, SIGNAL (editor_focus_changed (bool)),
2014              this, SLOT (disable_menu_shortcuts (bool)));
2015 
2016     connect (this, SIGNAL (editor_focus_changed (bool)),
2017              m_editor_window, SLOT (enable_menu_shortcuts (bool)));
2018 
2019     connect (this, SIGNAL (step_into_file_signal (void)),
2020              m_editor_window, SLOT (request_step_into_file (void)));
2021 
2022     connect (m_editor_window, SIGNAL (editor_tabs_changed_signal (bool)),
2023              this, SLOT (editor_tabs_changed (bool)));
2024 
2025     connect (m_editor_window,
2026              SIGNAL (request_open_file_external (const QString&, int)),
2027              m_external_editor,
2028              SLOT (call_custom_editor (const QString&, int)));
2029 
2030     connect (m_external_editor,
2031              SIGNAL (request_settings_dialog (const QString&)),
2032              this, SLOT (process_settings_dialog_request (const QString&)));
2033 
2034     connect (m_file_browser_window, SIGNAL (load_file_signal (const QString&)),
2035              this, SLOT (handle_load_workspace_request (const QString&)));
2036 
2037     connect (m_file_browser_window, SIGNAL (open_any_signal (const QString&)),
2038              this, SLOT (handle_open_any_request (const QString&)));
2039 
2040     connect (m_file_browser_window, SIGNAL (find_files_signal (const QString&)),
2041              this, SLOT (find_files (const QString&)));
2042 
2043     // Connections for signals from the interpreter thread where the slot
2044     // should be executed by the gui thread
2045 
2046     connect (this, SIGNAL (warning_function_not_found_signal (const QString&)),
2047              this, SLOT (warning_function_not_found (const QString&)));
2048 
2049     setWindowTitle ("Octave");
2050 
2051     setStatusBar (m_status_bar);
2052 
2053 #if defined (HAVE_QSCINTILLA)
2054     connect (this,
2055              SIGNAL (insert_debugger_pointer_signal (const QString&, int)),
2056              m_editor_window,
2057              SLOT (handle_insert_debugger_pointer_request (const QString&,
2058                                                            int)));
2059 
2060     connect (this,
2061              SIGNAL (delete_debugger_pointer_signal (const QString&, int)),
2062              m_editor_window,
2063              SLOT (handle_delete_debugger_pointer_request (const QString&,
2064                                                            int)));
2065 
2066     connect (this,
2067              SIGNAL (update_breakpoint_marker_signal (bool, const QString&,
2068                                                       int, const QString&)),
2069              m_editor_window,
2070              SLOT (handle_update_breakpoint_marker_request (bool,
2071                                                             const QString&,
2072                                                             int,
2073                                                             const QString&)));
2074 
2075     // Signals for removing/renaming files/dirs in the file browser
2076     connect (m_file_browser_window,
2077              SIGNAL (file_remove_signal (const QString&, const QString&)),
2078              m_editor_window,
2079              SLOT (handle_file_remove (const QString&, const QString&)));
2080 
2081     connect (m_file_browser_window, SIGNAL (file_renamed_signal (bool)),
2082              m_editor_window, SLOT (handle_file_renamed (bool)));
2083 
2084     // Signals for removing/renaming files/dirs in the terminal window
2085     connect (qt_link, SIGNAL (file_renamed_signal (bool)),
2086              m_editor_window, SLOT (handle_file_renamed (bool)));
2087 #endif
2088 
2089     // Signals for removing/renaming files/dirs in the temrinal window
2090     connect (qt_link,
2091              SIGNAL (file_remove_signal (const QString&, const QString&)),
2092              this, SLOT (file_remove_proxy (const QString&, const QString&)));
2093 
2094     connect (this, SIGNAL (interpreter_event (const fcn_callback&)),
2095              &m_octave_qobj, SLOT (interpreter_event (const fcn_callback&)));
2096 
2097     connect (this, SIGNAL (interpreter_event (const meth_callback&)),
2098              &m_octave_qobj, SLOT (interpreter_event (const meth_callback&)));
2099 
2100     configure_shortcuts ();
2101   }
2102 
construct_octave_qt_link(void)2103   void main_window::construct_octave_qt_link (void)
2104   {
2105     interpreter_qobject *interp_qobj = m_octave_qobj.interpreter_qobj ();
2106 
2107     qt_interpreter_events *qt_link = interp_qobj->qt_link ();
2108 
2109     connect (qt_link, SIGNAL (settings_changed (const gui_settings *, bool)),
2110              this, SLOT (notice_settings (const gui_settings *, bool)));
2111 
2112     connect (qt_link, SIGNAL (apply_new_settings (void)),
2113              this, SLOT (request_reload_settings (void)));
2114 
2115     connect (qt_link,
2116              SIGNAL (set_workspace_signal (bool, bool, const symbol_info_list&)),
2117              m_workspace_model,
2118              SLOT (set_workspace (bool, bool, const symbol_info_list&)));
2119 
2120     connect (qt_link, SIGNAL (clear_workspace_signal (void)),
2121              m_workspace_model, SLOT (clear_workspace (void)));
2122 
2123     connect (qt_link, SIGNAL (directory_changed_signal (QString)),
2124              this, SLOT (update_octave_directory (QString)));
2125 
2126     connect (qt_link, SIGNAL (directory_changed_signal (QString)),
2127              m_file_browser_window, SLOT (update_octave_directory (QString)));
2128 
2129     connect (qt_link, SIGNAL (directory_changed_signal (QString)),
2130              m_editor_window, SLOT (update_octave_directory (QString)));
2131 
2132     connect (qt_link,
2133              SIGNAL (execute_command_in_terminal_signal (QString)),
2134              this, SLOT (execute_command_in_terminal (QString)));
2135 
2136     connect (qt_link,
2137              SIGNAL (set_history_signal (const QStringList&)),
2138              m_history_window, SLOT (set_history (const QStringList&)));
2139 
2140     connect (qt_link,
2141              SIGNAL (append_history_signal (const QString&)),
2142              m_history_window, SLOT (append_history (const QString&)));
2143 
2144     connect (qt_link,
2145              SIGNAL (clear_history_signal (void)),
2146              m_history_window, SLOT (clear_history (void)));
2147 
2148     connect (qt_link, SIGNAL (enter_debugger_signal (void)),
2149              this, SLOT (handle_enter_debugger (void)));
2150 
2151     connect (qt_link, SIGNAL (exit_debugger_signal (void)),
2152              this, SLOT (handle_exit_debugger (void)));
2153 
2154     connect (qt_link,
2155              SIGNAL (show_preferences_signal (void)),
2156              this, SLOT (process_settings_dialog_request (void)));
2157 
2158     connect (qt_link,
2159              SIGNAL (edit_file_signal (const QString&)),
2160              m_active_editor,
2161              SLOT (handle_edit_file_request (const QString&)));
2162 
2163     connect (qt_link,
2164              SIGNAL (insert_debugger_pointer_signal (const QString&, int)),
2165              this,
2166              SLOT (handle_insert_debugger_pointer_request (const QString&,
2167                                                            int)));
2168 
2169     connect (qt_link,
2170              SIGNAL (delete_debugger_pointer_signal (const QString&, int)),
2171              this,
2172              SLOT (handle_delete_debugger_pointer_request (const QString&,
2173                                                            int)));
2174 
2175     connect (qt_link,
2176              SIGNAL (update_breakpoint_marker_signal (bool, const QString&,
2177                                                       int, const QString&)),
2178              this,
2179              SLOT (handle_update_breakpoint_marker_request (bool, const QString&,
2180                                                             int, const QString&)));
2181 
2182     connect (qt_link,
2183              SIGNAL (show_doc_signal (const QString &)),
2184              this, SLOT (handle_show_doc (const QString &)));
2185 
2186     connect (qt_link,
2187              SIGNAL (register_doc_signal (const QString &)),
2188              this, SLOT (handle_register_doc (const QString &)));
2189 
2190     connect (qt_link,
2191              SIGNAL (unregister_doc_signal (const QString &)),
2192              this, SLOT (handle_unregister_doc (const QString &)));
2193   }
2194 
add_action(QMenu * menu,const QIcon & icon,const QString & text,const char * member,const QWidget * receiver)2195   QAction* main_window::add_action (QMenu *menu, const QIcon& icon,
2196                                     const QString& text, const char *member,
2197                                     const QWidget *receiver)
2198   {
2199     QAction *a;
2200 
2201     if (receiver)
2202       a = menu->addAction (icon, text, receiver, member);
2203     else
2204       a = menu->addAction (icon, text, this, member);
2205 
2206     addAction (a);  // important for shortcut context
2207     a->setShortcutContext (Qt::ApplicationShortcut);
2208     return a;
2209   }
2210 
m_add_menu(QMenuBar * p,QString name)2211   QMenu* main_window::m_add_menu (QMenuBar *p, QString name)
2212   {
2213     QMenu *menu = p->addMenu (name);
2214 
2215     QString base_name = name;  // get a copy
2216     // replace intended '&' ("&&") by a temp. string
2217     base_name.replace ("&&", "___octave_amp_replacement___");
2218     // remove single '&' (shortcut)
2219     base_name.remove ("&");
2220     // restore intended '&'
2221     base_name.replace ("___octave_amp_replacement___", "&&");
2222 
2223     // remember names with and without shortcut
2224     m_hash_menu_text[menu] = QStringList () << name << base_name;
2225 
2226     return menu;
2227   }
2228 
construct_menu_bar(void)2229   void main_window::construct_menu_bar (void)
2230   {
2231     QMenuBar *menu_bar = menuBar ();
2232 
2233     construct_file_menu (menu_bar);
2234 
2235     construct_edit_menu (menu_bar);
2236 
2237     construct_debug_menu (menu_bar);
2238 
2239     construct_window_menu (menu_bar);
2240 
2241     construct_help_menu (menu_bar);
2242 
2243     construct_news_menu (menu_bar);
2244 
2245 #if defined (HAVE_QSCINTILLA)
2246     // call the editor to add actions which should also be available in the
2247     // editor's menu and tool bar
2248     QList<QAction*> shared_actions;
2249     shared_actions << m_new_script_action
2250                    << m_new_function_action
2251                    << m_open_action
2252                    << m_find_files_action
2253                    << m_undo_action
2254                    << m_copy_action
2255                    << m_paste_action
2256                    <<m_select_all_action;
2257     m_editor_window->insert_global_actions (shared_actions);
2258 #endif
2259   }
2260 
construct_file_menu(QMenuBar * p)2261   void main_window::construct_file_menu (QMenuBar *p)
2262   {
2263     QMenu *file_menu = m_add_menu (p, tr ("&File"));
2264 
2265     construct_new_menu (file_menu);
2266 
2267     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2268     m_open_action
2269       = file_menu->addAction (rmgr.icon ("document-open"), tr ("Open..."));
2270     m_open_action->setShortcutContext (Qt::ApplicationShortcut);
2271     m_open_action->setToolTip (tr ("Open an existing file in editor"));
2272 
2273 #if defined (HAVE_QSCINTILLA)
2274     file_menu->addMenu (m_editor_window->get_mru_menu ());
2275 #endif
2276 
2277     file_menu->addSeparator ();
2278 
2279     m_load_workspace_action
2280       = file_menu->addAction (tr ("Load Workspace..."));
2281 
2282     m_save_workspace_action
2283       = file_menu->addAction (tr ("Save Workspace As..."));
2284 
2285     file_menu->addSeparator ();
2286 
2287     m_exit_action = file_menu->addAction (tr ("Exit"));
2288     m_exit_action->setMenuRole (QAction::QuitRole);
2289     m_exit_action->setShortcutContext (Qt::ApplicationShortcut);
2290 
2291     connect (m_open_action, SIGNAL (triggered (void)),
2292              this, SLOT (request_open_file (void)));
2293 
2294     connect (m_load_workspace_action, SIGNAL (triggered (void)),
2295              this, SLOT (handle_load_workspace_request (void)));
2296 
2297     connect (m_save_workspace_action, SIGNAL (triggered (void)),
2298              this, SLOT (handle_save_workspace_request (void)));
2299 
2300     connect (m_exit_action, SIGNAL (triggered (void)),
2301              this, SLOT (close (void)));
2302   }
2303 
construct_new_menu(QMenu * p)2304   void main_window::construct_new_menu (QMenu *p)
2305   {
2306     QMenu *new_menu = p->addMenu (tr ("New"));
2307 
2308     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2309     m_new_script_action
2310       = new_menu->addAction (rmgr.icon ("document-new"), tr ("New Script"));
2311     m_new_script_action->setShortcutContext (Qt::ApplicationShortcut);
2312 
2313     m_new_function_action = new_menu->addAction (tr ("New Function..."));
2314     m_new_function_action->setEnabled (true);
2315     m_new_function_action->setShortcutContext (Qt::ApplicationShortcut);
2316 
2317     m_new_figure_action = new_menu->addAction (tr ("New Figure"));
2318     m_new_figure_action->setEnabled (true);
2319 
2320     connect (m_new_script_action, SIGNAL (triggered (void)),
2321              this, SLOT (request_new_script (void)));
2322 
2323     connect (m_new_function_action, SIGNAL (triggered (void)),
2324              this, SLOT (request_new_function (void)));
2325 
2326     connect (this, SIGNAL (new_file_signal (const QString&)),
2327              m_active_editor, SLOT (request_new_file (const QString&)));
2328 
2329     connect (this, SIGNAL (open_file_signal (const QString&)),
2330              m_active_editor, SLOT (request_open_file (const QString&)));
2331 
2332     connect (this,
2333              SIGNAL (open_file_signal (const QString&, const QString&, int)),
2334              m_active_editor,
2335              SLOT (request_open_file (const QString&, const QString&, int)));
2336 
2337     connect (m_new_figure_action, SIGNAL (triggered (void)),
2338              this, SLOT (handle_new_figure_request (void)));
2339   }
2340 
construct_edit_menu(QMenuBar * p)2341   void main_window::construct_edit_menu (QMenuBar *p)
2342   {
2343     QMenu *edit_menu = m_add_menu (p, tr ("&Edit"));
2344 
2345     QKeySequence ctrl_shift = Qt::ControlModifier + Qt::ShiftModifier;
2346 
2347     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2348     m_undo_action
2349       = edit_menu->addAction (rmgr.icon ("edit-undo"), tr ("Undo"));
2350     m_undo_action->setShortcutContext (Qt::ApplicationShortcut);
2351 
2352     edit_menu->addSeparator ();
2353 
2354     m_copy_action
2355       = edit_menu->addAction (rmgr.icon ("edit-copy"), tr ("Copy"), this,
2356                               SLOT (copyClipboard (void)));
2357     m_copy_action->setShortcutContext (Qt::ApplicationShortcut);
2358 
2359     m_paste_action
2360       = edit_menu->addAction (rmgr.icon ("edit-paste"), tr ("Paste"), this,
2361                               SLOT (pasteClipboard (void)));
2362     m_paste_action->setShortcutContext (Qt::ApplicationShortcut);
2363 
2364     m_select_all_action
2365       = edit_menu->addAction (tr ("Select All"), this, SLOT (selectAll (void)));
2366     m_select_all_action->setShortcutContext (Qt::ApplicationShortcut);
2367 
2368     m_clear_clipboard_action
2369       = edit_menu->addAction (tr ("Clear Clipboard"), this,
2370                               SLOT (clear_clipboard (void)));
2371 
2372     edit_menu->addSeparator ();
2373 
2374     m_find_files_action
2375       = edit_menu->addAction (rmgr.icon ("edit-find"), tr ("Find Files..."));
2376 
2377     edit_menu->addSeparator ();
2378 
2379     m_clear_command_window_action
2380       = edit_menu->addAction (tr ("Clear Command Window"));
2381 
2382     m_clear_command_history_action
2383       = edit_menu->addAction (tr ("Clear Command History"));
2384 
2385     m_clear_workspace_action
2386       = edit_menu->addAction (tr ("Clear Workspace"));
2387 
2388     edit_menu->addSeparator ();
2389 
2390     m_set_path_action
2391       = edit_menu->addAction (tr ("Set Path"));
2392 
2393     m_preferences_action
2394       = edit_menu->addAction (rmgr.icon ("preferences-system"),
2395                               tr ("Preferences..."));
2396 
2397     connect (m_find_files_action, SIGNAL (triggered (void)),
2398              this, SLOT (find_files (void)));
2399 
2400     connect (m_clear_command_window_action, SIGNAL (triggered (void)),
2401              this, SLOT (handle_clear_command_window_request (void)));
2402 
2403     connect (m_clear_command_history_action, SIGNAL (triggered (void)),
2404              this, SLOT (handle_clear_history_request (void)));
2405 
2406     connect (m_clear_workspace_action, SIGNAL (triggered (void)),
2407              this, SLOT (handle_clear_workspace_request (void)));
2408 
2409     connect (m_clipboard, SIGNAL (dataChanged (void)),
2410              this, SLOT (clipboard_has_changed (void)));
2411     clipboard_has_changed ();
2412 #if defined (Q_OS_WIN32)
2413     // Always enable paste action (unreliable clipboard signals in windows)
2414     // FIXME: This has to be removed, when the clipboard signals in windows
2415     //        are working again
2416     m_paste_action->setEnabled (true);
2417     m_clear_clipboard_action->setEnabled (true);
2418 #endif
2419 
2420     connect (m_preferences_action, SIGNAL (triggered (void)),
2421              this, SLOT (process_settings_dialog_request (void)));
2422 
2423     connect (m_set_path_action, SIGNAL (triggered (void)),
2424              this, SLOT (handle_set_path_dialog_request (void)));
2425 
2426   }
2427 
construct_debug_menu_item(const char * icon,const QString & item,const char * member)2428   QAction * main_window::construct_debug_menu_item (const char *icon,
2429                                                     const QString& item,
2430                                                     const char *member)
2431   {
2432     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2433     QAction *action = add_action (m_debug_menu, rmgr.icon (QString (icon)),
2434                                   item, member);
2435 
2436     action->setEnabled (false);
2437 
2438 #if defined (HAVE_QSCINTILLA)
2439     m_editor_window->debug_menu ()->addAction (action);
2440     m_editor_window->toolbar ()->addAction (action);
2441 #endif
2442 
2443     return action;
2444   }
2445 
construct_debug_menu(QMenuBar * p)2446   void main_window::construct_debug_menu (QMenuBar *p)
2447   {
2448     m_debug_menu = m_add_menu (p, tr ("De&bug"));
2449 
2450     m_debug_step_over
2451       = construct_debug_menu_item ("db-step", tr ("Step"),
2452                                    SLOT (debug_step_over (void)));
2453 
2454     m_debug_step_into
2455       = construct_debug_menu_item ("db-step-in", tr ("Step In"),
2456                                    SLOT (debug_step_into (void)));
2457 
2458     m_debug_step_out
2459       = construct_debug_menu_item ("db-step-out", tr ("Step Out"),
2460                                    SLOT (debug_step_out (void)));
2461 
2462     m_debug_continue
2463       = construct_debug_menu_item ("db-cont", tr ("Continue"),
2464                                    SLOT (debug_continue (void)));
2465 
2466     m_debug_menu->addSeparator ();
2467 #if defined (HAVE_QSCINTILLA)
2468     m_editor_window->debug_menu ()->addSeparator ();
2469 #endif
2470 
2471     m_debug_quit
2472       = construct_debug_menu_item ("db-stop", tr ("Quit Debug Mode"),
2473                                    SLOT (debug_quit (void)));
2474   }
2475 
editor_tabs_changed(bool have_tabs)2476   void main_window::editor_tabs_changed (bool have_tabs)
2477   {
2478     // Set state of actions which depend on the existence of editor tabs
2479     m_editor_has_tabs = have_tabs;
2480     m_debug_step_over->setEnabled (have_tabs);
2481   }
2482 
construct_window_menu_item(QMenu * p,const QString & item,bool checkable,QWidget * widget)2483   QAction * main_window::construct_window_menu_item (QMenu *p,
2484                                                      const QString& item,
2485                                                      bool checkable,
2486                                                      QWidget *widget)
2487   {
2488     QAction *action = p->addAction (QIcon (), item);
2489 
2490     addAction (action);  // important for shortcut context
2491     action->setCheckable (checkable);
2492     action->setShortcutContext (Qt::ApplicationShortcut);
2493 
2494     if (widget)  // might be zero for m_editor_window
2495       {
2496         if (checkable)
2497           {
2498             // action for visibility of dock widget
2499             connect (action, SIGNAL (toggled (bool)),
2500                      widget, SLOT (setVisible (bool)));
2501 
2502             connect (widget, SIGNAL (active_changed (bool)),
2503                      action, SLOT (setChecked (bool)));
2504           }
2505         else
2506           {
2507             // action for focus of dock widget
2508             connect (action, SIGNAL (triggered (void)), widget, SLOT (activate (void)));
2509           }
2510       }
2511     else
2512       {
2513         action->setEnabled (false);
2514       }
2515 
2516     return action;
2517   }
2518 
construct_window_menu(QMenuBar * p)2519   void main_window::construct_window_menu (QMenuBar *p)
2520   {
2521     QMenu *window_menu = m_add_menu (p, tr ("&Window"));
2522 
2523     m_show_command_window_action = construct_window_menu_item
2524       (window_menu, tr ("Show Command Window"), true, m_command_window);
2525 
2526     m_show_history_action = construct_window_menu_item
2527       (window_menu, tr ("Show Command History"), true, m_history_window);
2528 
2529     m_show_file_browser_action = construct_window_menu_item
2530       (window_menu, tr ("Show File Browser"), true, m_file_browser_window);
2531 
2532     m_show_workspace_action = construct_window_menu_item
2533       (window_menu, tr ("Show Workspace"), true, m_workspace_window);
2534 
2535     m_show_editor_action = construct_window_menu_item
2536       (window_menu, tr ("Show Editor"), true, m_editor_window);
2537 
2538     m_show_documentation_action = construct_window_menu_item
2539       (window_menu, tr ("Show Documentation"), true, m_doc_browser_window);
2540 
2541     m_show_variable_editor_action = construct_window_menu_item
2542       (window_menu, tr ("Show Variable Editor"), true, m_variable_editor_window);
2543 
2544     window_menu->addSeparator ();
2545 
2546     m_command_window_action = construct_window_menu_item
2547       (window_menu, tr ("Command Window"), false, m_command_window);
2548 
2549     m_history_action = construct_window_menu_item
2550       (window_menu, tr ("Command History"), false, m_history_window);
2551 
2552     m_file_browser_action = construct_window_menu_item
2553       (window_menu, tr ("File Browser"), false, m_file_browser_window);
2554 
2555     m_workspace_action = construct_window_menu_item
2556       (window_menu, tr ("Workspace"), false, m_workspace_window);
2557 
2558     m_editor_action = construct_window_menu_item
2559       (window_menu, tr ("Editor"), false, m_editor_window);
2560 
2561     m_documentation_action = construct_window_menu_item
2562       (window_menu, tr ("Documentation"), false, m_doc_browser_window);
2563 
2564     m_variable_editor_action = construct_window_menu_item
2565       (window_menu, tr ("Variable Editor"), false, m_variable_editor_window);
2566 
2567     window_menu->addSeparator ();
2568 
2569     m_previous_dock_action = add_action (window_menu, QIcon (),
2570                                            tr ("Previous Widget"), SLOT (go_to_previous_widget (void)));
2571 
2572     window_menu->addSeparator ();
2573 
2574     m_reset_windows_action = add_action (window_menu, QIcon (),
2575                                          tr ("Reset Default Window Layout"), SLOT (reset_windows (void)));
2576   }
2577 
construct_help_menu(QMenuBar * p)2578   void main_window::construct_help_menu (QMenuBar *p)
2579   {
2580     QMenu *help_menu = m_add_menu (p, tr ("&Help"));
2581 
2582     construct_documentation_menu (help_menu);
2583 
2584     help_menu->addSeparator ();
2585 
2586     m_report_bug_action = add_action (help_menu, QIcon (),
2587                                       tr ("Report Bug"), SLOT (open_bug_tracker_page ()));
2588 
2589     m_octave_packages_action = add_action (help_menu, QIcon (),
2590                                            tr ("Octave Packages"), SLOT (open_octave_packages_page ()));
2591 
2592     m_contribute_action = add_action (help_menu, QIcon (),
2593                                       tr ("Contribute"), SLOT (open_contribute_page ()));
2594 
2595     m_developer_action = add_action (help_menu, QIcon (),
2596                                      tr ("Donate to Octave"), SLOT (open_donate_page ()));
2597 
2598     help_menu->addSeparator ();
2599 
2600     m_about_octave_action = add_action (help_menu, QIcon (),
2601                                         tr ("About Octave"), SLOT (show_about_octave ()));
2602   }
2603 
construct_documentation_menu(QMenu * p)2604   void main_window::construct_documentation_menu (QMenu *p)
2605   {
2606     QMenu *doc_menu = p->addMenu (tr ("Documentation"));
2607 
2608     m_ondisk_doc_action = add_action (doc_menu, QIcon (),
2609                                       tr ("On Disk"), SLOT (activate ()), m_doc_browser_window);
2610 
2611     m_online_doc_action = add_action (doc_menu, QIcon (),
2612                                       tr ("Online"), SLOT (open_online_documentation_page ()));
2613   }
2614 
construct_news_menu(QMenuBar * p)2615   void main_window::construct_news_menu (QMenuBar *p)
2616   {
2617     QMenu *news_menu = m_add_menu (p, tr ("&News"));
2618 
2619     m_release_notes_action = add_action (news_menu, QIcon (),
2620                                          tr ("Release Notes"), SLOT (display_release_notes ()));
2621 
2622     m_current_news_action = add_action (news_menu, QIcon (),
2623                                         tr ("Community News"), SLOT (load_and_display_community_news ()));
2624   }
2625 
construct_tool_bar(void)2626   void main_window::construct_tool_bar (void)
2627   {
2628     m_main_tool_bar = addToolBar (tr ("Toolbar"));
2629     m_main_tool_bar->setStyleSheet (m_main_tool_bar->styleSheet ()
2630                                     + global_toolbar_style);
2631 
2632     m_main_tool_bar->setObjectName ("MainToolBar");
2633     m_main_tool_bar->addAction (m_new_script_action);
2634     m_main_tool_bar->addAction (m_open_action);
2635 
2636     m_main_tool_bar->addSeparator ();
2637 
2638     m_main_tool_bar->addAction (m_copy_action);
2639     m_main_tool_bar->addAction (m_paste_action);
2640     m_main_tool_bar->addAction (m_undo_action);
2641 
2642     m_main_tool_bar->addSeparator ();
2643 
2644     m_current_directory_combo_box = new QComboBox (this);
2645     QFontMetrics fm = m_current_directory_combo_box->fontMetrics ();
2646     m_current_directory_combo_box->setFixedWidth (48*fm.averageCharWidth ());
2647     m_current_directory_combo_box->setEditable (true);
2648     m_current_directory_combo_box->setInsertPolicy (QComboBox::NoInsert);
2649     m_current_directory_combo_box->setToolTip (tr ("Enter directory name"));
2650     m_current_directory_combo_box->setMaxVisibleItems (current_directory_max_visible);
2651     m_current_directory_combo_box->setMaxCount (current_directory_max_count);
2652     QSizePolicy sizePol (QSizePolicy::Preferred, QSizePolicy::Preferred);
2653     m_current_directory_combo_box->setSizePolicy (sizePol);
2654 
2655     // addWidget takes ownership of the objects so there is no
2656     // need to delete these upon destroying this main_window.
2657     m_main_tool_bar->addWidget (new QLabel (tr ("Current Directory: ")));
2658     m_main_tool_bar->addWidget (m_current_directory_combo_box);
2659     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2660     QAction *current_dir_up
2661       = m_main_tool_bar->addAction (rmgr.icon ("go-up"),
2662                                     tr ("One directory up"));
2663     QAction *current_dir_search
2664       = m_main_tool_bar->addAction (rmgr.icon ("folder"),
2665                                     tr ("Browse directories"));
2666 
2667     connect (m_current_directory_combo_box, SIGNAL (activated (QString)),
2668              this, SLOT (set_current_working_directory (QString)));
2669 
2670     connect (m_current_directory_combo_box->lineEdit (), SIGNAL (returnPressed (void)),
2671              this, SLOT (accept_directory_line_edit (void)));
2672 
2673     connect (current_dir_search, SIGNAL (triggered (void)),
2674              this, SLOT (browse_for_directory (void)));
2675 
2676     connect (current_dir_up, SIGNAL (triggered (void)),
2677              this, SLOT (change_directory_up (void)));
2678 
2679     connect (m_undo_action, SIGNAL (triggered (void)),
2680              this, SLOT (handle_undo_request (void)));
2681   }
2682 
focus_console_after_command(void)2683   void main_window::focus_console_after_command (void)
2684   {
2685     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2686     gui_settings *settings = rmgr.get_settings ();
2687     if (settings->value (cs_focus_cmd).toBool ())
2688       focus_command_window ();
2689   }
2690 
configure_shortcuts(void)2691   void main_window::configure_shortcuts (void)
2692   {
2693     shortcut_manager& scmgr = m_octave_qobj.get_shortcut_manager ();
2694 
2695     // file menu
2696     scmgr.set_shortcut (m_open_action, sc_main_file_open_file);
2697     scmgr.set_shortcut (m_new_script_action, sc_main_file_new_file);
2698     scmgr.set_shortcut (m_new_function_action, sc_main_file_new_function);
2699     scmgr.set_shortcut (m_new_figure_action, sc_main_file_new_figure);
2700     scmgr.set_shortcut (m_load_workspace_action, sc_main_file_load_workspace);
2701     scmgr.set_shortcut (m_save_workspace_action, sc_main_file_save_workspace);
2702     scmgr.set_shortcut (m_exit_action, sc_main_file_exit);
2703 
2704     // edit menu
2705     scmgr.set_shortcut (m_copy_action, sc_main_edit_copy);
2706     scmgr.set_shortcut (m_paste_action, sc_main_edit_paste);
2707     scmgr.set_shortcut (m_undo_action, sc_main_edit_undo);
2708     scmgr.set_shortcut (m_select_all_action, sc_main_edit_select_all);
2709     scmgr.set_shortcut (m_clear_clipboard_action, sc_main_edit_clear_clipboard);
2710     scmgr.set_shortcut (m_find_files_action, sc_main_edit_find_in_files);
2711     scmgr.set_shortcut (m_clear_command_history_action, sc_main_edit_clear_history);
2712     scmgr.set_shortcut (m_clear_command_window_action, sc_main_edit_clear_command_window);
2713     scmgr.set_shortcut (m_clear_workspace_action, sc_main_edit_clear_workspace);
2714     scmgr.set_shortcut (m_set_path_action, sc_main_edit_set_path);
2715     scmgr.set_shortcut (m_preferences_action, sc_main_edit_preferences);
2716 
2717     // debug menu
2718     scmgr.set_shortcut (m_debug_step_over, sc_main_debug_step_over);
2719     scmgr.set_shortcut (m_debug_step_into, sc_main_debug_step_into);
2720     scmgr.set_shortcut (m_debug_step_out, sc_main_debug_step_out);
2721     scmgr.set_shortcut (m_debug_continue, sc_main_debug_continue);
2722     scmgr.set_shortcut (m_debug_quit, sc_main_debug_quit);
2723 
2724     // window menu
2725     scmgr.set_shortcut (m_show_command_window_action, sc_main_window_show_command);
2726     scmgr.set_shortcut (m_show_history_action, sc_main_window_show_history);
2727     scmgr.set_shortcut (m_show_workspace_action, sc_main_window_show_workspace);
2728     scmgr.set_shortcut (m_show_file_browser_action, sc_main_window_show_file_browser);
2729     scmgr.set_shortcut (m_show_editor_action, sc_main_window_show_editor);
2730     scmgr.set_shortcut (m_show_documentation_action, sc_main_window_show_doc);
2731     scmgr.set_shortcut (m_show_variable_editor_action, sc_main_window_show_variable_editor);
2732     scmgr.set_shortcut (m_command_window_action, sc_main_window_command);
2733     scmgr.set_shortcut (m_history_action, sc_main_window_history);
2734     scmgr.set_shortcut (m_workspace_action, sc_main_window_workspace);
2735     scmgr.set_shortcut (m_file_browser_action, sc_main_window_file_browser);
2736     scmgr.set_shortcut (m_editor_action, sc_main_window_editor);
2737     scmgr.set_shortcut (m_documentation_action, sc_main_window_doc);
2738     scmgr.set_shortcut (m_variable_editor_action, sc_main_window_variable_editor);
2739     scmgr.set_shortcut (m_previous_dock_action, sc_main_window_previous_dock);
2740     scmgr.set_shortcut (m_reset_windows_action, sc_main_window_reset);
2741 
2742     // help menu
2743     scmgr.set_shortcut (m_ondisk_doc_action, sc_main_help_ondisk_doc);
2744     scmgr.set_shortcut (m_online_doc_action, sc_main_help_online_doc);
2745     scmgr.set_shortcut (m_report_bug_action, sc_main_help_report_bug);
2746     scmgr.set_shortcut (m_octave_packages_action, sc_main_help_packages);
2747     scmgr.set_shortcut (m_contribute_action, sc_main_help_contribute);
2748     scmgr.set_shortcut (m_developer_action, sc_main_help_developer);
2749     scmgr.set_shortcut (m_about_octave_action, sc_main_help_about);
2750 
2751     // news menu
2752     scmgr.set_shortcut (m_release_notes_action, sc_main_news_release_notes);
2753     scmgr.set_shortcut (m_current_news_action, sc_main_news_community_news);
2754   }
2755 
dock_widget_list(void)2756   QList<octave_dock_widget *> main_window::dock_widget_list (void)
2757   {
2758     QList<octave_dock_widget *> list = QList<octave_dock_widget *> ();
2759     list.append (static_cast<octave_dock_widget *> (m_command_window));
2760     list.append (static_cast<octave_dock_widget *> (m_history_window));
2761     list.append (static_cast<octave_dock_widget *> (m_file_browser_window));
2762     list.append (static_cast<octave_dock_widget *> (m_doc_browser_window));
2763 #if defined (HAVE_QSCINTILLA)
2764     list.append (static_cast<octave_dock_widget *> (m_editor_window));
2765 #endif
2766     list.append (static_cast<octave_dock_widget *> (m_workspace_window));
2767     list.append (static_cast<octave_dock_widget *> (m_variable_editor_window));
2768     return list;
2769   }
2770 
update_default_encoding(const QString & default_encoding)2771   void main_window::update_default_encoding (const QString& default_encoding)
2772   {
2773     m_default_encoding = default_encoding;
2774     std::string mfile_encoding = m_default_encoding.toStdString ();
2775     if (m_default_encoding.startsWith ("SYSTEM", Qt::CaseInsensitive))
2776       mfile_encoding = "SYSTEM";
2777 
2778     emit interpreter_event
2779       ([mfile_encoding] (interpreter& interp)
2780        {
2781          // INTERPRETER THREAD
2782 
2783          F__mfile_encoding__ (interp, ovl (mfile_encoding));
2784        });
2785   }
2786 
2787   // Get size of screen where the main window is located
get_screen_geometry(int * width,int * height)2788   void main_window::get_screen_geometry (int *width, int *height)
2789   {
2790     QRect screen_geometry
2791         = QApplication::desktop ()->availableGeometry (this);
2792 
2793     *width = screen_geometry.width ();
2794     *height = screen_geometry.height ();
2795   }
2796 
resize_dock(QDockWidget * dw,int width,int height)2797   void main_window::resize_dock (QDockWidget *dw, int width, int height)
2798   {
2799 #if defined (HAVE_QMAINWINDOW_RESIZEDOCKS)
2800     // resizeDockWidget was added to Qt in Qt 5.6
2801     if (width >= 0)
2802       resizeDocks ({dw},{width},Qt::Horizontal);
2803     if (height >= 0)
2804       resizeDocks ({dw},{height},Qt::Vertical);
2805 #else
2806     // This replacement of resizeDockWidget is not very reliable.
2807     // But even if Qt4 is not yet
2808     QSize s = dw->widget ()->size ();
2809     if (width >= 0)
2810       s.setWidth (width);
2811     if (height >= 0)
2812       s.setHeight (height);
2813     dw->widget ()->resize (s);
2814     dw->adjustSize ();
2815 #endif
2816   }
2817 
2818   // The default main window size relative to the desktop size
set_default_geometry()2819   void main_window::set_default_geometry ()
2820   {
2821     int win_x, win_y;
2822     get_screen_geometry (&win_x, &win_y);
2823 
2824     move (0, 0);
2825     resize (2*win_x/3, 7*win_y/8);
2826   }
2827 
reset_windows(void)2828   void main_window::reset_windows (void)
2829   {
2830     // Slot for resetting the window layout to the default one
2831     hide ();
2832     showNormal ();              // Unmaximize
2833     do_reset_windows (false);   // Add all widgets
2834     // Re-add after giving time: This seems to be a reliable way to
2835     // reset the main window's layout
2836     QTimer::singleShot (250, this, SLOT (do_reset_windows (void)));
2837   }
2838 
2839   // Create the default layout of the main window. Do not use
2840   // restoreState () and restoreGeometry () with default values since
2841   // this might lead to problems when the Qt version changes
do_reset_windows(bool show,bool save)2842   void main_window::do_reset_windows (bool show, bool save)
2843   {
2844     // Set main window default geometry and store its width for
2845     // later resizing the command window
2846     set_default_geometry ();
2847     int win_x = geometry ().width ();
2848 
2849     // Resize command window, the important one in the default layout
2850     resize_dock (m_command_window, 7*win_x/8, -1);
2851 
2852     // See Octave bug #53409 and https://bugreports.qt.io/browse/QTBUG-55357
2853 #if (QT_VERSION < 0x050601) || (QT_VERSION >= 0x050701)
2854     setDockOptions (QMainWindow::AnimatedDocks
2855                     | QMainWindow::AllowNestedDocks
2856                     | QMainWindow::AllowTabbedDocks);
2857 #else
2858     setDockNestingEnabled (true);
2859 #endif
2860 
2861     // Add the dock widgets and show them
2862     addDockWidget (Qt::LeftDockWidgetArea, m_file_browser_window);
2863     addDockWidget (Qt::LeftDockWidgetArea, m_workspace_window);
2864     addDockWidget (Qt::LeftDockWidgetArea, m_history_window);
2865 
2866     addDockWidget (Qt::RightDockWidgetArea, m_command_window);
2867 
2868     addDockWidget (Qt::RightDockWidgetArea, m_doc_browser_window);
2869     tabifyDockWidget (m_command_window, m_doc_browser_window);
2870 
2871     addDockWidget (Qt::RightDockWidgetArea, m_variable_editor_window);
2872     tabifyDockWidget (m_command_window, m_variable_editor_window);
2873 
2874 #if defined (HAVE_QSCINTILLA)
2875     addDockWidget (Qt::RightDockWidgetArea, m_editor_window);
2876     tabifyDockWidget (m_command_window, m_editor_window);
2877 #endif
2878 
2879     // Resize command window, the important one in the default layout
2880     resize_dock (m_command_window, 2*win_x/3, -1);
2881 
2882     // Show main wibdow, save state and geometry of main window and
2883     // all dock widgets
2884     if (show)
2885       {
2886         // Show all dock widgets
2887         for (auto *widget : dock_widget_list ())
2888           widget->show ();
2889 
2890         // Show main window and store size and state
2891         showNormal ();
2892 
2893         if (save)
2894           {
2895             resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2896             gui_settings *settings = rmgr.get_settings ();
2897 
2898             settings->setValue (mw_geometry.key, saveGeometry ());
2899             settings->setValue (mw_state.key, saveState ());
2900           }
2901 
2902         focus_command_window ();
2903       }
2904   }
2905 }
2906