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 #if defined (HAVE_QSCINTILLA)
31 
32 #include <algorithm>
33 
34 #include <QApplication>
35 #include <QFile>
36 #include <QFileDialog>
37 #include <QFont>
38 #include <QMessageBox>
39 #include <QMimeData>
40 #include <QProcess>
41 #include <QPushButton>
42 #include <QStyle>
43 #include <QTabBar>
44 #include <QTextStream>
45 #include <QVBoxLayout>
46 #include <Qsci/qscicommandset.h>
47 
48 #include "file-editor.h"
49 #include "gui-preferences-ed.h"
50 #include "gui-preferences-sc.h"
51 #include "gui-preferences-global.h"
52 #include "main-window.h"
53 #include "octave-qobject.h"
54 #include "shortcut-manager.h"
55 
56 #include "oct-env.h"
57 
58 #include "event-manager.h"
59 #include "interpreter.h"
60 #include "oct-map.h"
61 #include "pt-eval.h"
62 #include "utils.h"
63 
64 namespace octave
65 {
66   // Functions of the the reimplemented tab widget
67 
file_editor_tab_widget(QWidget * p)68   file_editor_tab_widget::file_editor_tab_widget (QWidget *p)
69     : QTabWidget (p)
70   {
71     tab_bar *bar = new tab_bar (this);
72 
73     connect (bar, SIGNAL (close_current_tab_signal (bool)),
74              p->parent (), SLOT (request_close_file (bool)));
75 
76     this->setTabBar (bar);
77 
78     setTabsClosable (true);
79     setUsesScrollButtons (true);
80 #if defined (HAVE_QTABWIDGET_SETMOVABLE)
81     setMovable (true);
82 #endif
83   }
84 
get_tab_bar(void) const85   tab_bar * file_editor_tab_widget::get_tab_bar (void) const
86   {
87     return qobject_cast<tab_bar *> (tabBar ());
88   }
89 
90   std::list<file_editor_tab *>
tab_list(void) const91   file_editor_tab_widget::tab_list (void) const
92   {
93     std::list<file_editor_tab *> retval;
94     for (int i = 0; i < count (); i++)
95       retval.push_back (static_cast<file_editor_tab *> (widget (i)));
96     return retval;
97   }
98 
99 
100   // File editor
101 
file_editor(QWidget * p,base_qobject & oct_qobj)102   file_editor::file_editor (QWidget *p, base_qobject& oct_qobj)
103     : file_editor_interface (p, oct_qobj)
104   {
105     // Set current editing directory before construction because loaded
106     // files will change ced accordingly.
107     m_ced = QDir::currentPath ();
108 
109     // Set actions that are later added by the main window to null,
110     // preventing access to them when they are still undefined.
111     m_undo_action = nullptr;
112     m_copy_action = nullptr;
113     m_paste_action = nullptr;
114     m_selectall_action = nullptr;
115 
116     m_find_dialog = nullptr;
117 
118     m_closed = false;
119     m_no_focus = false;
120 
121     m_copy_action_enabled = false;
122     m_undo_action_enabled = false;
123 
124     construct ();
125 
126     setVisible (false);
127     setAcceptDrops (true);
128     setFocusPolicy (Qt::StrongFocus);
129   }
130 
~file_editor(void)131   file_editor::~file_editor (void)
132   {
133     delete m_mru_file_menu;
134   }
135 
focusInEvent(QFocusEvent * e)136   void file_editor::focusInEvent (QFocusEvent *e)
137   {
138     // The focus is transferred to the active tab and its edit
139     // area in this focus in event handler. This is to avoid
140     // using focus proxies with conflicts in the proxy change
141     // presumably introduced by bug
142     // https://bugreports.qt.io/browse/QTBUG-61092
143     reset_focus (); // Make sure editor tab with edit area get focus
144 
145     QDockWidget::focusInEvent (e);
146   }
147 
148   // insert global actions, that should also be displayed in the editor window,
149   // into the editor's menu and/or toolbar
insert_global_actions(QList<QAction * > shared_actions)150   void file_editor::insert_global_actions (QList<QAction*> shared_actions)
151   {
152     // actions/menus that have to be added to the toolbar or the menu
153     QAction *open_action = shared_actions.at (OPEN_ACTION);
154     QAction *new_action = shared_actions.at (NEW_SCRIPT_ACTION);
155     QAction *new_fcn_action = shared_actions.at (NEW_FUNCTION_ACTION);
156     m_fileMenu->insertAction (m_mru_file_menu->menuAction (), open_action);
157     m_fileMenu->insertAction (open_action, new_fcn_action);
158     m_fileMenu->insertAction (new_fcn_action, new_action);
159     m_tool_bar->insertAction (m_popdown_mru_action, open_action);
160     m_tool_bar->insertAction (open_action, new_action);
161 
162     // actions that are additionally enabled/disabled later by the editor
163     // undo
164     m_undo_action = shared_actions.at (UNDO_ACTION);
165     m_tool_bar->insertAction (m_redo_action,m_undo_action);
166     m_edit_menu->insertAction (m_redo_action,m_undo_action);
167     // select all
168     m_selectall_action = shared_actions.at (SELECTALL_ACTION);
169     m_edit_menu->insertAction (m_find_action,m_selectall_action);
170     m_edit_menu->insertSeparator (m_find_action);
171     // paste
172     m_paste_action = shared_actions.at (PASTE_ACTION);
173     m_tool_bar->insertAction (m_find_action,m_paste_action);
174     m_edit_menu->insertAction (m_selectall_action,m_paste_action);
175     m_edit_menu->insertSeparator (m_selectall_action);
176     // copy
177     m_copy_action = shared_actions.at (COPY_ACTION);
178     m_tool_bar->insertAction (m_paste_action,m_copy_action);
179     m_edit_menu->insertAction (m_paste_action,m_copy_action);
180     // find files
181     m_find_files_action = shared_actions.at (FIND_FILES_ACTION);
182     m_edit_menu->insertAction (m_find_action, m_find_files_action);
183   }
184 
handle_enter_debug_mode(void)185   void file_editor::handle_enter_debug_mode (void)
186   {
187     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
188     gui_settings *settings = rmgr.get_settings ();
189     QString sc_run = settings->sc_value (sc_edit_run_run_file);
190     QString sc_cont = settings->sc_value (sc_main_debug_continue);
191 
192     if (sc_run == sc_cont)
193       m_run_action->setShortcut (QKeySequence ());  // prevent ambiguous shortcuts
194 
195     m_run_action->setToolTip (tr ("Continue"));   // update tool tip
196   }
197 
handle_exit_debug_mode(void)198   void file_editor::handle_exit_debug_mode (void)
199   {
200     shortcut_manager& scmgr = m_octave_qobj.get_shortcut_manager ();
201     scmgr.set_shortcut (m_run_action, sc_edit_run_run_file);
202     m_run_action->setToolTip (tr ("Save File and Run"));  // update tool tip
203   }
204 
check_actions(void)205   void file_editor::check_actions (void)
206   {
207     bool have_tabs = m_tab_widget->count () > 0;
208 
209     m_edit_cmd_menu->setEnabled (have_tabs);
210     m_edit_fmt_menu->setEnabled (have_tabs);
211     m_edit_nav_menu->setEnabled (have_tabs);
212 
213     m_comment_selection_action->setEnabled (have_tabs);
214     m_uncomment_selection_action->setEnabled (have_tabs);
215     m_comment_var_selection_action->setEnabled (have_tabs);
216     m_indent_selection_action->setEnabled (have_tabs);
217     m_unindent_selection_action->setEnabled (have_tabs);
218     m_smart_indent_line_or_selection_action->setEnabled (have_tabs);
219 
220     m_context_help_action->setEnabled (have_tabs);
221     m_context_doc_action->setEnabled (have_tabs);
222 
223     m_view_editor_menu->setEnabled (have_tabs);
224     m_zoom_in_action->setEnabled (have_tabs);
225     m_zoom_out_action->setEnabled (have_tabs);
226     m_zoom_normal_action->setEnabled (have_tabs);
227 
228     m_find_action->setEnabled (have_tabs);
229     m_find_next_action->setEnabled (have_tabs);
230     m_find_previous_action->setEnabled (have_tabs);
231     m_print_action->setEnabled (have_tabs);
232     m_run_action->setEnabled (have_tabs);
233 
234     m_edit_function_action->setEnabled (have_tabs);
235     m_save_action->setEnabled (have_tabs);
236     m_save_as_action->setEnabled (have_tabs);
237     m_close_action->setEnabled (have_tabs);
238     m_close_all_action->setEnabled (have_tabs);
239     m_close_others_action->setEnabled (have_tabs && m_tab_widget->count () > 1);
240     m_sort_tabs_action->setEnabled (have_tabs && m_tab_widget->count () > 1);
241 
242     emit editor_tabs_changed_signal (have_tabs);
243   }
244 
245   // empty_script determines whether we have to create an empty script
246   // 1. At startup, when the editor has to be (really) visible
247   //    (Here we can not use the visibility changed signal)
248   // 2. When the editor becomes visible when octave is running
empty_script(bool startup,bool visible)249   void file_editor::empty_script (bool startup, bool visible)
250   {
251     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
252     gui_settings *settings = rmgr.get_settings ();
253     if (settings->value (global_use_custom_editor.key,
254                          global_use_custom_editor.def).toBool ())
255       return;  // do not open an empty script in the external editor
256 
257     bool real_visible;
258 
259     if (startup)
260       real_visible = isVisible ();
261     else
262       real_visible = visible;
263 
264     if (! real_visible || m_tab_widget->count () > 0)
265       return;
266 
267     if (startup && ! isFloating ())
268       {
269         // check if editor is really visible or hidden between tabbed widgets
270         QList<QTabBar *> tab_list = main_win ()->findChildren<QTabBar *>();
271 
272         bool in_tab = false;
273         int i = 0;
274         while ((i < tab_list.count ()) && (! in_tab))
275           {
276             QTabBar *tab = tab_list.at (i);
277             i++;
278 
279             int j = 0;
280             while ((j < tab->count ()) && (! in_tab))
281               {
282                 // check all tabs for the editor
283                 if (tab->tabText (j) == windowTitle ())
284                   {
285                     // editor is in this tab widget
286                     in_tab = true;
287                     int top = tab->currentIndex ();
288                     if (! (top > -1 && tab->tabText (top) == windowTitle ()))
289                       return; // not current tab -> not visible
290                   }
291                 j++;
292               }
293           }
294       }
295 
296     request_new_file ("");
297   }
298 
restore_session(gui_settings * settings)299   void file_editor::restore_session (gui_settings *settings)
300   {
301     //restore previous session
302     if (! settings->value (ed_restore_session).toBool ())
303       return;
304 
305     // get the data from the settings file
306     QStringList sessionFileNames
307       = settings->value (ed_session_names).toStringList ();
308 
309     QStringList session_encodings
310       = settings->value (ed_session_enc).toStringList ();
311 
312     QStringList session_index
313       = settings->value (ed_session_ind).toStringList ();
314 
315     QStringList session_lines
316       = settings->value (ed_session_lines).toStringList ();
317 
318     // fill a list of the struct and sort it (depending on index)
319     QList<session_data> s_data;
320 
321     bool do_encoding = (session_encodings.count () == sessionFileNames.count ());
322     bool do_index = (session_index.count () == sessionFileNames.count ());
323     bool do_lines = (session_lines.count () == sessionFileNames.count ());
324 
325     for (int n = 0; n < sessionFileNames.count (); ++n)
326       {
327         QFileInfo file = QFileInfo (sessionFileNames.at (n));
328         if (! file.exists ())
329           continue;
330 
331         session_data item = { 0, -1, sessionFileNames.at (n),
332                               QString (), QString ()};
333         if (do_lines)
334           item.line = session_lines.at (n).toInt ();
335         if (do_index)
336           item.index = session_index.at (n).toInt ();
337         if (do_encoding)
338           item.encoding = session_encodings.at (n);
339 
340         s_data << item;
341       }
342 
343     std::sort (s_data.begin (), s_data.end ());
344 
345     // finally open the files with the desired encoding in the desired order
346     for (int n = 0; n < s_data.count (); ++n)
347       request_open_file (s_data.at (n).file_name, s_data.at (n).encoding,
348                          s_data.at (n).line);
349   }
350 
activate(void)351   void file_editor::activate (void)
352   {
353     if (m_no_focus)
354       return;  // No focus for the editor if external open/close request
355 
356     octave_dock_widget::activate ();
357 
358     // set focus to current tab
359     reset_focus ();
360   }
361 
set_focus(QWidget * fet)362   void file_editor::set_focus (QWidget *fet)
363   {
364     setFocus ();
365 
366     // set focus to desired tab
367     if (fet)
368       m_tab_widget->setCurrentWidget (fet);
369   }
370 
371   // function enabling/disabling the menu accelerators depending on the
372   // focus of the editor
enable_menu_shortcuts(bool enable)373   void file_editor::enable_menu_shortcuts (bool enable)
374   {
375     // Hide or show the find dialog together with the focus of the
376     // editor widget depending on the overall visibility of the find dialog.
377     // Do not change internal visibility state.
378     if (m_find_dialog)
379       m_find_dialog->set_visible (enable);
380 
381     // Take care of the shortcuts
382     QHash<QMenu*, QStringList>::const_iterator i = m_hash_menu_text.constBegin ();
383 
384     while (i != m_hash_menu_text.constEnd ())
385       {
386         i.key ()->setTitle (i.value ().at (! enable));
387         ++i;
388       }
389 
390     // when editor loses focus, enable the actions, which are always active
391     // in the main window due to missing info on selected text and undo actions
392     if (m_copy_action && m_undo_action)
393       {
394         if (enable)
395           {
396             m_copy_action->setEnabled (m_copy_action_enabled);
397             m_undo_action->setEnabled (m_undo_action_enabled);
398           }
399         else
400           {
401             m_copy_action_enabled = m_copy_action->isEnabled ();
402             m_undo_action_enabled = m_undo_action->isEnabled ();
403             m_copy_action->setEnabled (true);
404             m_undo_action->setEnabled (true);
405           }
406       }
407   }
408 
409   // Save open files for restoring in next session
410   // (even if last session will not be restored next time)
411   // together with encoding and the tab index
save_session(void)412   void file_editor::save_session (void)
413   {
414     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
415     gui_settings *settings = rmgr.get_settings ();
416 
417     QStringList fetFileNames;
418     QStringList fet_encodings;
419     QStringList fet_index;
420     QStringList fet_lines;
421 
422     std::list<file_editor_tab *> editor_tab_lst = m_tab_widget->tab_list ();
423 
424     for (auto editor_tab : editor_tab_lst)
425       {
426         QString file_name = editor_tab->file_name ();
427 
428         // Don't append unnamed files.
429 
430         if (! file_name.isEmpty ())
431           {
432             fetFileNames.append (file_name);
433             fet_encodings.append (editor_tab->encoding ());
434 
435             QString index;
436             fet_index.append (index.setNum (m_tab_widget->indexOf (editor_tab)));
437 
438             int l, c;
439             editor_tab->qsci_edit_area ()->getCursorPosition (&l, &c);
440             fet_lines.append (index.setNum (l + 1));
441           }
442       }
443 
444     settings->setValue (ed_session_names.key, fetFileNames);
445     settings->setValue (ed_session_enc.key, fet_encodings);
446     settings->setValue (ed_session_ind.key, fet_index);
447     settings->setValue (ed_session_lines.key, fet_lines);
448     settings->sync ();
449   }
450 
check_closing(void)451   bool file_editor::check_closing (void)
452   {
453     // When the application or the editor is closing and the user wants to
454     // close all files, in the latter case all editor tabs are checked whether
455     // they need to be saved.  During these checks tabs are not closed since
456     // the user might cancel closing Octave during one of these saving dialogs.
457     // Therefore, saving the session for restoring at next start is not done
458     // before the application is definitely closing.
459 
460     // Save the session. Even is closing is cancelled, this would be
461     // overwritten by the next attempt to close the editor
462     save_session ();
463 
464     std::list<file_editor_tab *> fe_tab_lst = m_tab_widget->tab_list ();
465     m_number_of_tabs = fe_tab_lst.size ();
466 
467     for (auto fe_tab : fe_tab_lst)
468       {
469         // Wait for all editor tabs to have saved their files if required
470 
471         connect (fe_tab, SIGNAL (tab_ready_to_close (void)),
472                  this, SLOT (handle_tab_ready_to_close (void)),
473                  Qt::UniqueConnection);
474       }
475 
476     m_closing_canceled = false;
477 
478     for (auto fe_tab : fe_tab_lst)
479       {
480         // If there was a cancellation, make the already saved/discarded tabs
481         // recover from the exit by removing the read-only state and by
482         // recovering the debugger breakpoints.  Finally return false in order
483         // to cancel closing the application or the editor.
484 
485         if (fe_tab->check_file_modified (false) == QMessageBox::Cancel)
486           {
487             emit fetab_recover_from_exit ();
488 
489             m_closing_canceled = true;
490 
491             for (auto fet : fe_tab_lst)
492               disconnect (fet, SIGNAL (tab_ready_to_close (void)), 0, 0 );
493 
494             return false;
495           }
496       }
497 
498     return true;
499   }
500 
handle_tab_ready_to_close(void)501   void file_editor::handle_tab_ready_to_close (void)
502   {
503     if (m_closing_canceled)
504       return;
505 
506     // FIXME: Why count down to zero here before doing anything?  Why
507     // not remove and delete each tab that is ready to be closed, one
508     // per invocation?
509 
510     m_number_of_tabs--;
511 
512     if (m_number_of_tabs > 0)
513       return;
514 
515     // Here, the application or the editor will be closed -> store the session
516 
517     // Take care of the find dialog
518     if (m_find_dialog)
519       m_find_dialog->close ();
520 
521     // Finally close all the tabs and return indication that we can exit
522     // the application or close the editor.
523     // Closing and deleting the tabs makes the editor visible.  In case it was
524     // hidden before, this state has to be restored afterwards.
525     bool vis = isVisible ();
526 
527     std::list<file_editor_tab *> editor_tab_lst = m_tab_widget->tab_list ();
528     for (auto editor_tab : editor_tab_lst)
529       editor_tab->deleteLater ();
530 
531     m_tab_widget->clear ();
532 
533     setVisible (vis);
534   }
535 
request_new_file(const QString & commands)536   void file_editor::request_new_file (const QString& commands)
537   {
538     // Custom editor? If yes, we can only call the editor without passing
539     // some initial contents and even without being sure a new file is opened
540     if (call_custom_editor ())
541       return;
542 
543     // New file isn't a file_editor_tab function since the file
544     // editor tab has yet to be created and there is no object to
545     // pass a signal to.  Hence, functionality is here.
546 
547     file_editor_tab *fileEditorTab = make_file_editor_tab (m_ced);
548     add_file_editor_tab (fileEditorTab, "");  // new tab with empty title
549     fileEditorTab->new_file (commands);       // title is updated here
550     activate ();                              // focus editor and new tab
551   }
552 
request_close_file(bool)553   void file_editor::request_close_file (bool)
554   {
555     file_editor_tab *editor_tab
556       = static_cast<file_editor_tab *> (m_tab_widget->currentWidget ());
557     editor_tab->conditional_close ();
558   }
559 
request_close_all_files(bool)560   void file_editor::request_close_all_files (bool)
561   {
562     file_editor_tab *editor_tab;
563 
564     // loop over all tabs starting from last one otherwise deletion changes index
565     for (int index = m_tab_widget->count ()-1; index >= 0; index--)
566       {
567         editor_tab = static_cast<file_editor_tab *> (m_tab_widget->widget (index));
568         editor_tab->conditional_close ();
569       }
570   }
571 
request_close_other_files(bool)572   void file_editor::request_close_other_files (bool)
573   {
574     file_editor_tab *editor_tab;
575     QWidget *tabID = m_tab_widget->currentWidget ();
576 
577     // loop over all tabs starting from last one otherwise deletion changes index
578     for (int index = m_tab_widget->count ()-1; index >= 0; index--)
579       {
580         if (tabID != m_tab_widget->widget (index))
581           {
582             editor_tab
583               = static_cast<file_editor_tab *> (m_tab_widget->widget (index));
584             editor_tab->conditional_close ();
585           }
586       }
587   }
588 
589   // open a file from the mru list
request_mru_open_file(QAction * action)590   void file_editor::request_mru_open_file (QAction *action)
591   {
592     if (action)
593       {
594         request_open_file (action->data ().toStringList ().at (0),
595                            action->data ().toStringList ().at (1));
596       }
597   }
598 
request_print_file(bool)599   void file_editor::request_print_file (bool)
600   {
601     emit fetab_print_file (m_tab_widget->currentWidget ());
602   }
603 
request_redo(bool)604   void file_editor::request_redo (bool)
605   {
606     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
607                                   QsciScintillaBase::SCI_REDO);
608   }
609 
request_cut(bool)610   void file_editor::request_cut (bool)
611   {
612     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
613                                   QsciScintillaBase::SCI_CUT);
614   }
615 
request_context_help(bool)616   void file_editor::request_context_help (bool)
617   {
618     emit fetab_context_help (m_tab_widget->currentWidget (), false);
619   }
620 
request_context_doc(bool)621   void file_editor::request_context_doc (bool)
622   {
623     emit fetab_context_help (m_tab_widget->currentWidget (), true);
624   }
625 
request_context_edit(bool)626   void file_editor::request_context_edit (bool)
627   {
628     emit fetab_context_edit (m_tab_widget->currentWidget ());
629   }
630 
request_save_file(bool)631   void file_editor::request_save_file (bool)
632   {
633     emit fetab_save_file (m_tab_widget->currentWidget ());
634   }
635 
request_save_file_as(bool)636   void file_editor::request_save_file_as (bool)
637   {
638     emit fetab_save_file_as (m_tab_widget->currentWidget ());
639   }
640 
request_run_file(bool)641   void file_editor::request_run_file (bool)
642   {
643     emit interpreter_event
644       ([this] (interpreter& interp)
645        {
646          // INTERPRETER THREAD
647 
648          tree_evaluator& tw = interp.get_evaluator ();
649 
650          if (tw.in_debug_repl ())
651            emit request_dbcont_signal ();
652          else
653            emit fetab_run_file (m_tab_widget->currentWidget ());
654        });
655   }
656 
request_step_into_file()657   void file_editor::request_step_into_file ()
658   {
659     emit fetab_run_file (m_tab_widget->currentWidget (), true);
660   }
661 
request_context_run(bool)662   void file_editor::request_context_run (bool)
663   {
664     emit fetab_context_run (m_tab_widget->currentWidget ());
665   }
666 
request_toggle_bookmark(bool)667   void file_editor::request_toggle_bookmark (bool)
668   {
669     emit fetab_toggle_bookmark (m_tab_widget->currentWidget ());
670   }
671 
request_next_bookmark(bool)672   void file_editor::request_next_bookmark (bool)
673   {
674     emit fetab_next_bookmark (m_tab_widget->currentWidget ());
675   }
676 
request_previous_bookmark(bool)677   void file_editor::request_previous_bookmark (bool)
678   {
679     emit fetab_previous_bookmark (m_tab_widget->currentWidget ());
680   }
681 
request_remove_bookmark(bool)682   void file_editor::request_remove_bookmark (bool)
683   {
684     emit fetab_remove_bookmark (m_tab_widget->currentWidget ());
685   }
686 
request_move_match_brace(bool)687   void file_editor::request_move_match_brace (bool)
688   {
689     emit fetab_move_match_brace (m_tab_widget->currentWidget (), false);
690   }
691 
request_sel_match_brace(bool)692   void file_editor::request_sel_match_brace (bool)
693   {
694     emit fetab_move_match_brace (m_tab_widget->currentWidget (), true);
695   }
696 
697   // FIXME: What should this do with conditional breakpoints?
request_toggle_breakpoint(bool)698   void file_editor::request_toggle_breakpoint (bool)
699   {
700     emit fetab_toggle_breakpoint (m_tab_widget->currentWidget ());
701   }
702 
request_next_breakpoint(bool)703   void file_editor::request_next_breakpoint (bool)
704   {
705     emit fetab_next_breakpoint (m_tab_widget->currentWidget ());
706   }
707 
request_previous_breakpoint(bool)708   void file_editor::request_previous_breakpoint (bool)
709   {
710     emit fetab_previous_breakpoint (m_tab_widget->currentWidget ());
711   }
712 
request_remove_breakpoint(bool)713   void file_editor::request_remove_breakpoint (bool)
714   {
715     emit fetab_remove_all_breakpoints (m_tab_widget->currentWidget ());
716   }
717 
718   // slots for Edit->Commands actions
request_delete_start_word(bool)719   void file_editor::request_delete_start_word (bool)
720   {
721     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
722                                   QsciScintillaBase::SCI_DELWORDLEFT);
723   }
724 
request_delete_end_word(bool)725   void file_editor::request_delete_end_word (bool)
726   {
727     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
728                                   QsciScintillaBase::SCI_DELWORDRIGHT);
729   }
730 
request_delete_start_line(bool)731   void file_editor::request_delete_start_line (bool)
732   {
733     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
734                                   QsciScintillaBase::SCI_DELLINELEFT);
735   }
736 
request_delete_end_line(bool)737   void file_editor::request_delete_end_line (bool)
738   {
739     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
740                                   QsciScintillaBase::SCI_DELLINERIGHT);
741   }
742 
request_delete_line(bool)743   void file_editor::request_delete_line (bool)
744   {
745     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
746                                   QsciScintillaBase::SCI_LINEDELETE);
747   }
748 
request_copy_line(bool)749   void file_editor::request_copy_line (bool)
750   {
751     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
752                                   QsciScintillaBase::SCI_LINECOPY);
753   }
754 
request_cut_line(bool)755   void file_editor::request_cut_line (bool)
756   {
757     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
758                                   QsciScintillaBase::SCI_LINECUT);
759   }
760 
request_duplicate_selection(bool)761   void file_editor::request_duplicate_selection (bool)
762   {
763     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
764                                   QsciScintillaBase::SCI_SELECTIONDUPLICATE);
765   }
766 
request_transpose_line(bool)767   void file_editor::request_transpose_line (bool)
768   {
769     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
770                                   QsciScintillaBase::SCI_LINETRANSPOSE);
771   }
772 
request_comment_selected_text(bool)773   void file_editor::request_comment_selected_text (bool)
774   {
775     emit fetab_comment_selected_text (m_tab_widget->currentWidget (), false);
776   }
777 
request_uncomment_selected_text(bool)778   void file_editor::request_uncomment_selected_text (bool)
779   {
780     emit fetab_uncomment_selected_text (m_tab_widget->currentWidget ());
781   }
782 
request_comment_var_selected_text(bool)783   void file_editor::request_comment_var_selected_text (bool)
784   {
785     emit fetab_comment_selected_text (m_tab_widget->currentWidget (), true);
786   }
787 
788   // slots for Edit->Format actions
request_upper_case(bool)789   void file_editor::request_upper_case (bool)
790   {
791     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
792                                   QsciScintillaBase::SCI_UPPERCASE);
793   }
794 
request_lower_case(bool)795   void file_editor::request_lower_case (bool)
796   {
797     emit fetab_scintilla_command (m_tab_widget->currentWidget (),
798                                   QsciScintillaBase::SCI_LOWERCASE);
799   }
800 
request_indent_selected_text(bool)801   void file_editor::request_indent_selected_text (bool)
802   {
803     emit fetab_indent_selected_text (m_tab_widget->currentWidget ());
804   }
805 
request_unindent_selected_text(bool)806   void file_editor::request_unindent_selected_text (bool)
807   {
808     emit fetab_unindent_selected_text (m_tab_widget->currentWidget ());
809   }
810 
request_smart_indent_line_or_selected_text()811   void file_editor::request_smart_indent_line_or_selected_text ()
812   {
813     emit fetab_smart_indent_line_or_selected_text (m_tab_widget->currentWidget ());
814   }
815 
request_conv_eol_windows(bool)816   void file_editor::request_conv_eol_windows (bool)
817   {
818     emit fetab_convert_eol (m_tab_widget->currentWidget (),
819                             QsciScintilla::EolWindows);
820   }
821   void
request_conv_eol_unix(bool)822   file_editor::request_conv_eol_unix (bool)
823   {
824     emit fetab_convert_eol (m_tab_widget->currentWidget (),
825                             QsciScintilla::EolUnix);
826   }
827 
request_conv_eol_mac(bool)828   void file_editor::request_conv_eol_mac (bool)
829   {
830     emit fetab_convert_eol (m_tab_widget->currentWidget (),
831                             QsciScintilla::EolMac);
832   }
833 
834   // Slot for initially creating and showing the find dialog
request_find(bool)835   void file_editor::request_find (bool)
836   {
837     // Create the dialog
838     find_create ();
839 
840     // Since find_create shows the dialog without activating the widget
841     // (which is reuqired in other cases) do this manually here
842     m_find_dialog->activateWindow ();
843 
844     // Initiate search text from possible selection and save the initial
845     // data from the dialog on the defined structure
846     m_find_dialog->init_search_text ();
847   }
848 
849   // This method creates the find dialog.
850 
find_create()851   void file_editor::find_create ()
852   {
853     if (m_find_dialog)
854       m_find_dialog->close ();
855 
856     if (isFloating ())
857       m_find_dialog = new find_dialog (m_octave_qobj, this, this);
858     else
859       m_find_dialog = new find_dialog (m_octave_qobj, this, main_win ());
860 
861     // Add required actions
862     m_find_dialog->addAction (m_find_next_action);
863     m_find_dialog->addAction (m_find_previous_action);
864 
865     // Update edit area
866     file_editor_tab* fet
867       = static_cast<file_editor_tab *> (m_tab_widget->currentWidget ());
868     m_find_dialog->update_edit_area (fet->qsci_edit_area ());
869 
870     // Icon is the same as the editor
871     m_find_dialog->setWindowIcon (windowIcon ());
872 
873     // Position:  lower right of editor's position
874     int xp = x () + frameGeometry ().width ();
875     int yp = y () + frameGeometry ().height ();
876 
877     if (! isFloating ())
878       {
879         // Fix position if editor is docked
880         xp = xp + main_win ()->x();
881         yp = yp + main_win ()->y();
882       }
883 
884     if (yp < 0)
885       yp = 0;
886 
887     // The size of the find dialog is considered in restore_settings
888     // since its size might change depending on the options
889     m_find_dialog->restore_settings (QPoint (xp, yp));
890 
891     // Set visible
892     m_find_dialog->set_visible (true);
893   }
894 
request_find_next(bool)895   void file_editor::request_find_next (bool)
896   {
897     if (m_find_dialog)
898       m_find_dialog->find_next ();
899   }
900 
request_find_previous(bool)901   void file_editor::request_find_previous (bool)
902   {
903     if (m_find_dialog)
904       m_find_dialog->find_prev ();
905   }
906 
request_goto_line(bool)907   void file_editor::request_goto_line (bool)
908   {
909     emit fetab_goto_line (m_tab_widget->currentWidget ());
910   }
911 
request_completion(bool)912   void file_editor::request_completion (bool)
913   {
914     emit fetab_completion (m_tab_widget->currentWidget ());
915   }
916 
handle_file_name_changed(const QString & fname,const QString & tip,bool modified)917   void file_editor::handle_file_name_changed (const QString& fname,
918                                               const QString& tip,
919                                               bool modified)
920   {
921     QObject *fileEditorTab = sender ();
922     if (fileEditorTab)
923       {
924         resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
925 
926         for (int i = 0; i < m_tab_widget->count (); i++)
927           {
928             if (m_tab_widget->widget (i) == fileEditorTab)
929               {
930                 m_tab_widget->setTabText (i, fname);
931                 m_tab_widget->setTabToolTip (i, tip);
932                 if (modified)
933                   m_tab_widget->setTabIcon (i, rmgr.icon ("document-save"));
934                 else
935                   m_tab_widget->setTabIcon (i, QIcon ());
936               }
937           }
938       }
939   }
940 
handle_tab_close_request(int index)941   void file_editor::handle_tab_close_request (int index)
942   {
943     file_editor_tab *editor_tab
944       = static_cast<file_editor_tab *> (m_tab_widget->widget (index));
945     editor_tab->conditional_close ();
946   }
947 
948   void
handle_tab_remove_request(void)949   file_editor::handle_tab_remove_request (void)
950   {
951     QObject *fileEditorTab = sender ();
952     if (fileEditorTab)
953       {
954         for (int i = 0; i < m_tab_widget->count (); i++)
955           {
956             if (m_tab_widget->widget (i) == fileEditorTab)
957               {
958                 m_tab_widget->removeTab (i);
959 
960                 // Deleting the sender (even with deleteLater) seems a
961                 // bit strange.  Is there a better way?
962                 fileEditorTab->deleteLater ();
963                 break;
964               }
965           }
966       }
967     check_actions ();
968 
969     activate ();     // focus stays in editor when tab is closed
970 
971   }
972 
973   // context menu of edit area
active_tab_changed(int index)974   void file_editor::active_tab_changed (int index)
975   {
976     emit fetab_change_request (m_tab_widget->widget (index));
977     activate ();
978   }
979 
handle_editor_state_changed(bool copy_available,bool is_octave_file)980   void file_editor::handle_editor_state_changed (bool copy_available,
981                                                  bool is_octave_file)
982   {
983     // In case there is some scenario where traffic could be coming from
984     // all the file editor tabs, just process info from the current active tab.
985     if (sender () == m_tab_widget->currentWidget ())
986       {
987         if (m_copy_action)
988           m_copy_action->setEnabled (copy_available);
989         m_cut_action->setEnabled (copy_available);
990         m_run_selection_action->setEnabled (copy_available);
991         m_run_action->setEnabled (is_octave_file);
992       }
993 
994     m_copy_action_enabled = m_copy_action->isEnabled ();
995     m_undo_action_enabled = m_undo_action->isEnabled ();
996   }
997 
handle_mru_add_file(const QString & file_name,const QString & encoding)998   void file_editor::handle_mru_add_file (const QString& file_name,
999                                          const QString& encoding)
1000   {
1001     int index;
1002     while ((index = m_mru_files.indexOf (file_name)) >= 0)
1003       {
1004         m_mru_files.removeAt (index);
1005         m_mru_files_encodings.removeAt (index);
1006       }
1007 
1008     m_mru_files.prepend (file_name);
1009     m_mru_files_encodings.prepend (encoding);
1010 
1011     mru_menu_update ();
1012   }
1013 
check_conflict_save(const QString & saveFileName,bool remove_on_success)1014   void file_editor::check_conflict_save (const QString& saveFileName,
1015                                          bool remove_on_success)
1016   {
1017     // Check whether this file is already open in the editor.
1018     file_editor_tab *tab = find_tab_widget (saveFileName);
1019 
1020     if (tab)
1021       {
1022         // Note: to overwrite the contents of some other file editor tab
1023         // with the same name requires identifying which file editor tab
1024         // that is (not too difficult) then closing that tab.  Of course,
1025         // that could trigger another dialog box if the file editor tab
1026         // with the same name has modifications in it.  This could become
1027         // somewhat confusing to the user.  For now, opt to do nothing.
1028 
1029         // Create a NonModal message about error.
1030         QMessageBox *msgBox
1031           = new QMessageBox (QMessageBox::Critical, tr ("Octave Editor"),
1032                              tr ("File not saved! A file with the selected name\n%1\n"
1033                                  "is already open in the editor").
1034                              arg (saveFileName),
1035                              QMessageBox::Ok, nullptr);
1036 
1037         msgBox->setWindowModality (Qt::NonModal);
1038         msgBox->setAttribute (Qt::WA_DeleteOnClose);
1039         msgBox->show ();
1040 
1041         return;
1042       }
1043 
1044     QObject *saveFileObject = sender ();
1045     QWidget *saveFileWidget = nullptr;
1046 
1047     for (int i = 0; i < m_tab_widget->count (); i++)
1048       {
1049         if (m_tab_widget->widget (i) == saveFileObject)
1050           {
1051             saveFileWidget = m_tab_widget->widget (i);
1052             break;
1053           }
1054       }
1055     if (! saveFileWidget)
1056       {
1057         // Create a NonModal message about error.
1058         QMessageBox *msgBox
1059           = new QMessageBox (QMessageBox::Critical, tr ("Octave Editor"),
1060                              tr ("The associated file editor tab has disappeared."),
1061                              QMessageBox::Ok, nullptr);
1062 
1063         msgBox->setWindowModality (Qt::NonModal);
1064         msgBox->setAttribute (Qt::WA_DeleteOnClose);
1065         msgBox->show ();
1066 
1067         return;
1068       }
1069 
1070     // Can save without conflict, have the file editor tab do so.
1071     emit fetab_save_file (saveFileWidget, saveFileName, remove_on_success);
1072   }
1073 
handle_insert_debugger_pointer_request(const QString & file,int line)1074   void file_editor::handle_insert_debugger_pointer_request (const QString& file,
1075                                                             int line)
1076   {
1077     request_open_file (file, QString (), line, true); // default encoding
1078   }
1079 
handle_delete_debugger_pointer_request(const QString & file,int line)1080   void file_editor::handle_delete_debugger_pointer_request (const QString& file,
1081                                                             int line)
1082   {
1083     if (! file.isEmpty ())
1084       {
1085         // Check whether this file is already open in the editor.
1086         file_editor_tab *tab = find_tab_widget (file);
1087 
1088         if (tab)
1089           {
1090             m_tab_widget->setCurrentWidget (tab);
1091 
1092             if (line > 0)
1093               emit fetab_delete_debugger_pointer (tab, line);
1094 
1095             emit fetab_set_focus (tab);
1096           }
1097       }
1098   }
1099 
handle_update_breakpoint_marker_request(bool insert,const QString & file,int line,const QString & cond)1100   void file_editor::handle_update_breakpoint_marker_request (bool insert,
1101                                                              const QString& file,
1102                                                              int line,
1103                                                              const QString& cond)
1104   {
1105     request_open_file (file, QString (), line, false, true, insert, cond);
1106   }
1107 
handle_edit_file_request(const QString & file)1108   void file_editor::handle_edit_file_request (const QString& file)
1109   {
1110     request_open_file (file);
1111   }
1112 
1113   // Slot used for signals indicating that a file was changed/renamed or
1114   // is going to be deleted/renamed
handle_file_remove(const QString & old_name,const QString & new_name)1115   void file_editor::handle_file_remove (const QString& old_name,
1116                                         const QString& new_name)
1117   {
1118     // Clear old list of file data and declare a structure for file data
1119     m_tmp_closed_files.clear ();
1120     session_data f_data;
1121 
1122     // Preprocessing old name(s)
1123     QString old_name_clean = old_name.trimmed ();
1124     int s = old_name_clean.size ();
1125 
1126     if (s > 1 && old_name_clean.at (0) == QChar ('\"')
1127         && old_name_clean.at (s - 1) == QChar ('\"'))
1128       old_name_clean = old_name_clean.mid (1, s - 2);
1129 
1130     QStringList old_names = old_name_clean.split ("\" \"");
1131 
1132     // Check if new name is a file or directory
1133     QFileInfo newf (new_name);
1134     bool new_is_dir = newf.isDir ();
1135 
1136     // Now loop over all old files/dirs (several files by movefile ())
1137     for (int i = 0; i < old_names.count (); i++)
1138       {
1139         // Check if old name is a file or directory
1140         QFileInfo old (old_names.at (i));
1141 
1142         if (old.isDir ())
1143           {
1144             // Call the function which handles directories and return
1145             handle_dir_remove (old_names.at (i), new_name);
1146           }
1147         else
1148           {
1149             // It is a single file.  Is it open?
1150             file_editor_tab *editor_tab = find_tab_widget (old_names.at (i));
1151 
1152             if (editor_tab)
1153               {
1154                 // Get index and line.
1155 
1156                 f_data.encoding = editor_tab->encoding ();
1157                 f_data.index = m_tab_widget->indexOf (editor_tab);
1158                 int l, c;
1159                 editor_tab->qsci_edit_area ()->getCursorPosition (&l, &c);
1160                 f_data.line = l + 1;
1161 
1162                 // Close it silently
1163                 m_no_focus = true;  // Remember for not focussing editor
1164                 editor_tab->file_has_changed (QString (), true);  // Close the tab
1165                 m_no_focus = false;  // Back to normal
1166 
1167                 // For reloading old file if error while removing
1168                 f_data.file_name = old_names.at (i);
1169                 // For reloading new file (if new_file is not empty)
1170                 if (new_is_dir)
1171                   {
1172                     std::string ndir = new_name.toStdString ();
1173                     std::string ofile = old.fileName ().toStdString ();
1174                     f_data.new_file_name
1175                       = QString::fromStdString (sys::env::make_absolute (ofile, ndir));
1176                   }
1177                 else
1178                   f_data.new_file_name = new_name;
1179 
1180                 // Add file data to list
1181                 m_tmp_closed_files << f_data;
1182               }
1183           }
1184       }
1185   }
1186 
1187   // Slot for signal indicating that a file was renamed
handle_file_renamed(bool load_new)1188   void file_editor::handle_file_renamed (bool load_new)
1189   {
1190     m_no_focus = true;  // Remember for not focussing editor
1191 
1192     // Loop over all files that have to be reloaded.  Start at the end of the
1193     // list, otherwise the stored indexes are not correct.
1194     for (int i = m_tmp_closed_files.count () - 1; i >= 0; i--)
1195       {
1196         // Load old or new file
1197         if (load_new)
1198           {
1199             if (! m_tmp_closed_files.at (i).new_file_name.isEmpty ())
1200               request_open_file (m_tmp_closed_files.at (i).new_file_name,
1201                                  m_tmp_closed_files.at (i).encoding,
1202                                  m_tmp_closed_files.at (i).line,
1203                                  false, false, true, "",
1204                                  m_tmp_closed_files.at (i).index);
1205           }
1206         else
1207           {
1208             request_open_file (m_tmp_closed_files.at (i).file_name,
1209                                m_tmp_closed_files.at (i).encoding,
1210                                m_tmp_closed_files.at (i).line,
1211                                false, false, true, "",
1212                                m_tmp_closed_files.at (i).index);
1213           }
1214 
1215       }
1216 
1217     m_no_focus = false;  // Back to normal focus
1218 
1219     // Clear the list of file data
1220     m_tmp_closed_files.clear ();
1221   }
1222 
notice_settings(const gui_settings * settings)1223   void file_editor::notice_settings (const gui_settings *settings)
1224   {
1225     int size_idx = settings->value (global_icon_size).toInt ();
1226     size_idx = (size_idx > 0) - (size_idx < 0) + 1;  // Make valid index from 0 to 2
1227 
1228     QStyle *st = style ();
1229     int icon_size = st->pixelMetric (global_icon_sizes[size_idx]);
1230     m_tool_bar->setIconSize (QSize (icon_size, icon_size));
1231 
1232     // Tab position
1233     QTabWidget::TabPosition pos
1234       = static_cast<QTabWidget::TabPosition> (settings->value (ed_tab_position).toInt ());
1235 
1236     m_tab_widget->setTabPosition (pos);
1237 
1238     // Update style sheet properties depending on position
1239     QString width_str ("width");
1240     QString height_str ("height");
1241     if (pos == QTabWidget::West || pos == QTabWidget::East)
1242       {
1243         width_str = QString ("height");
1244         height_str = QString ("width");
1245       }
1246 
1247     // Min and max width for full path titles
1248     int tab_width_min = settings->value (ed_notebook_tab_width_min)
1249                         .toInt ();
1250     int tab_width_max = settings->value (ed_notebook_tab_width_max)
1251                         .toInt ();
1252 
1253     // Get suitable height of a tab related to font and icon size
1254     int height = 1.5*QFontMetrics (m_tab_widget->font ()).height ();
1255     int is = 1.5*m_tab_widget->iconSize ().height ();
1256     if (is > height)
1257       height = is;
1258 
1259     // Style sheet for tab height
1260     QString style_sheet = QString ("QTabBar::tab {max-" + height_str + ": %1px;}")
1261                           .arg (height);
1262 
1263     // Style sheet for tab height together with width
1264     if (settings->value (ed_long_window_title).toBool ())
1265       {
1266         style_sheet = QString ("QTabBar::tab "
1267                                " {max-" + height_str + ": %1px;"
1268                                "  min-" + width_str + ": %2px;"
1269                                "  max-" + width_str + ": %3px;}")
1270                       .arg (height).arg (tab_width_min).arg (tab_width_max);
1271         m_tab_widget->setElideMode (Qt::ElideLeft);
1272       }
1273     else
1274       {
1275         m_tab_widget->setElideMode (Qt::ElideNone);
1276       }
1277 
1278 #if defined (Q_OS_MAC)
1279     // FIXME: This is a workaround for missing tab close buttons on MacOS
1280     // in several Qt versions (https://bugreports.qt.io/browse/QTBUG-61092)
1281     QString close_button_css
1282       ("QTabBar::close-button"
1283        "  { width: 6px; image: url(:/actions/icons/widget-close.png);}\n"
1284        "QTabBar::close-button:hover"
1285        "  { background-color: #cccccc; }");
1286 
1287     style_sheet = style_sheet + close_button_css;
1288 #endif
1289 
1290     m_tab_widget->setStyleSheet (style_sheet);
1291 
1292     bool show_it;
1293     show_it = settings->value (ed_show_line_numbers).toBool ();
1294     m_show_linenum_action->setChecked (show_it);
1295     show_it = settings->value (ed_show_white_space).toBool ();
1296     m_show_whitespace_action->setChecked (show_it);
1297     show_it = settings->value (ed_show_eol_chars).toBool ();
1298     m_show_eol_action->setChecked (show_it);
1299     show_it = settings->value (ed_show_indent_guides).toBool ();
1300     m_show_indguide_action->setChecked (show_it);
1301     show_it = settings->value (ed_long_line_marker).toBool ();
1302     m_show_longline_action->setChecked (show_it);
1303 
1304     show_it = settings->value (ed_show_toolbar).toBool ();
1305     m_show_toolbar_action->setChecked (show_it);
1306     m_tool_bar->setVisible (show_it);
1307     show_it = settings->value (ed_show_edit_status_bar).toBool ();
1308     m_show_statusbar_action->setChecked (show_it);
1309     show_it = settings->value (ed_show_hscroll_bar).toBool ();
1310     m_show_hscrollbar_action->setChecked (show_it);
1311 
1312     set_shortcuts ();
1313 
1314     // Find dialog with the same icon as the editor
1315     if (m_find_dialog)
1316       m_find_dialog->setWindowIcon (windowIcon ());
1317 
1318     // Relay signal to file editor tabs.
1319     emit fetab_settings_changed (settings);
1320   }
1321 
set_shortcuts(void)1322   void file_editor::set_shortcuts (void)
1323   {
1324     // Shortcuts also available in the main window, as well as the related
1325     // shortcuts, are defined in main_window and added to the editor
1326 
1327     shortcut_manager& scmgr = m_octave_qobj.get_shortcut_manager ();
1328 
1329     // File menu
1330     scmgr.set_shortcut (m_edit_function_action, sc_edit_file_edit_function);
1331     scmgr.set_shortcut (m_save_action, sc_edit_file_save);
1332     scmgr.set_shortcut (m_save_as_action, sc_edit_file_save_as);
1333     scmgr.set_shortcut (m_close_action, sc_edit_file_close);
1334     scmgr.set_shortcut (m_close_all_action, sc_edit_file_close_all);
1335     scmgr.set_shortcut (m_close_others_action, sc_edit_file_close_other);
1336     scmgr.set_shortcut (m_print_action, sc_edit_file_print);
1337 
1338     // Edit menu
1339     scmgr.set_shortcut (m_redo_action, sc_edit_edit_redo);
1340     scmgr.set_shortcut (m_cut_action, sc_edit_edit_cut);
1341     scmgr.set_shortcut (m_find_action, sc_edit_edit_find_replace);
1342     scmgr.set_shortcut (m_find_next_action, sc_edit_edit_find_next);
1343     scmgr.set_shortcut (m_find_previous_action, sc_edit_edit_find_previous);
1344 
1345     scmgr.set_shortcut (m_delete_start_word_action, sc_edit_edit_delete_start_word);
1346     scmgr.set_shortcut (m_delete_end_word_action, sc_edit_edit_delete_end_word);
1347     scmgr.set_shortcut (m_delete_start_line_action, sc_edit_edit_delete_start_line);
1348     scmgr.set_shortcut (m_delete_end_line_action, sc_edit_edit_delete_end_line);
1349     scmgr.set_shortcut (m_delete_line_action, sc_edit_edit_delete_line);
1350     scmgr.set_shortcut (m_copy_line_action, sc_edit_edit_copy_line);
1351     scmgr.set_shortcut (m_cut_line_action, sc_edit_edit_cut_line);
1352     scmgr.set_shortcut (m_duplicate_selection_action, sc_edit_edit_duplicate_selection);
1353     scmgr.set_shortcut (m_transpose_line_action, sc_edit_edit_transpose_line);
1354     scmgr.set_shortcut (m_comment_selection_action, sc_edit_edit_comment_selection);
1355     scmgr.set_shortcut (m_uncomment_selection_action, sc_edit_edit_uncomment_selection);
1356     scmgr.set_shortcut (m_comment_var_selection_action, sc_edit_edit_comment_var_selection);
1357 
1358     scmgr.set_shortcut (m_upper_case_action, sc_edit_edit_upper_case);
1359     scmgr.set_shortcut (m_lower_case_action, sc_edit_edit_lower_case);
1360     scmgr.set_shortcut (m_indent_selection_action, sc_edit_edit_indent_selection);
1361     scmgr.set_shortcut (m_unindent_selection_action, sc_edit_edit_unindent_selection);
1362     scmgr.set_shortcut (m_smart_indent_line_or_selection_action, sc_edit_edit_smart_indent_line_or_selection);
1363     scmgr.set_shortcut (m_completion_action, sc_edit_edit_completion_list);
1364     scmgr.set_shortcut (m_goto_line_action, sc_edit_edit_goto_line);
1365     scmgr.set_shortcut (m_move_to_matching_brace, sc_edit_edit_move_to_brace);
1366     scmgr.set_shortcut (m_sel_to_matching_brace, sc_edit_edit_select_to_brace);
1367     scmgr.set_shortcut (m_toggle_bookmark_action, sc_edit_edit_toggle_bookmark);
1368     scmgr.set_shortcut (m_next_bookmark_action, sc_edit_edit_next_bookmark);
1369     scmgr.set_shortcut (m_previous_bookmark_action, sc_edit_edit_previous_bookmark);
1370     scmgr.set_shortcut (m_remove_bookmark_action, sc_edit_edit_remove_bookmark);
1371     scmgr.set_shortcut (m_preferences_action, sc_edit_edit_preferences);
1372     scmgr.set_shortcut (m_styles_preferences_action, sc_edit_edit_styles_preferences);
1373 
1374     scmgr.set_shortcut (m_conv_eol_windows_action, sc_edit_edit_conv_eol_winows);
1375     scmgr.set_shortcut (m_conv_eol_unix_action,    sc_edit_edit_conv_eol_unix);
1376     scmgr.set_shortcut (m_conv_eol_mac_action,     sc_edit_edit_conv_eol_mac);
1377 
1378     // View menu
1379     scmgr.set_shortcut (m_show_linenum_action, sc_edit_view_show_line_numbers);
1380     scmgr.set_shortcut (m_show_whitespace_action, sc_edit_view_show_white_spaces);
1381     scmgr.set_shortcut (m_show_eol_action, sc_edit_view_show_eol_chars);
1382     scmgr.set_shortcut (m_show_indguide_action, sc_edit_view_show_ind_guides);
1383     scmgr.set_shortcut (m_show_longline_action, sc_edit_view_show_long_line);
1384     scmgr.set_shortcut (m_show_toolbar_action, sc_edit_view_show_toolbar);
1385     scmgr.set_shortcut (m_show_statusbar_action, sc_edit_view_show_statusbar);
1386     scmgr.set_shortcut (m_show_hscrollbar_action, sc_edit_view_show_hscrollbar);
1387     scmgr.set_shortcut (m_zoom_in_action, sc_edit_view_zoom_in);
1388     scmgr.set_shortcut (m_zoom_out_action, sc_edit_view_zoom_out);
1389     scmgr.set_shortcut (m_zoom_normal_action, sc_edit_view_zoom_normal);
1390     scmgr.set_shortcut (m_sort_tabs_action, sc_edit_view_sort_tabs);
1391 
1392     // Debug menu
1393     scmgr.set_shortcut (m_toggle_breakpoint_action, sc_edit_debug_toggle_breakpoint);
1394     scmgr.set_shortcut (m_next_breakpoint_action, sc_edit_debug_next_breakpoint);
1395     scmgr.set_shortcut (m_previous_breakpoint_action, sc_edit_debug_previous_breakpoint);
1396     scmgr.set_shortcut (m_remove_all_breakpoints_action, sc_edit_debug_remove_breakpoints);
1397 
1398     // Run menu
1399     scmgr.set_shortcut (m_run_action, sc_edit_run_run_file);
1400     scmgr.set_shortcut (m_run_selection_action, sc_edit_run_run_selection);
1401 
1402     // Help menu
1403     scmgr.set_shortcut (m_context_help_action, sc_edit_help_help_keyword);
1404     scmgr.set_shortcut (m_context_doc_action,  sc_edit_help_doc_keyword);
1405 
1406     // Tab navigation without menu entries
1407     scmgr.set_shortcut (m_switch_left_tab_action, sc_edit_tabs_switch_left_tab);
1408     scmgr.set_shortcut (m_switch_right_tab_action, sc_edit_tabs_switch_right_tab);
1409     scmgr.set_shortcut (m_move_tab_left_action, sc_edit_tabs_move_tab_left);
1410     scmgr.set_shortcut (m_move_tab_right_action, sc_edit_tabs_move_tab_right);
1411 
1412   }
1413 
1414   // This slot is a reimplementation of the virtual slot in octave_dock_widget.
1415   // We need this for creating an empty script when the editor has no open
1416   // files and is made visible.
handle_visibility(bool visible)1417   void file_editor::handle_visibility (bool visible)
1418   {
1419     if (m_closed && visible)
1420       {
1421         m_closed = false;
1422         resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1423         gui_settings *settings = rmgr.get_settings ();
1424         restore_session (settings);
1425       }
1426 
1427     empty_script (false, visible);
1428 
1429     if (visible && ! isFloating ())
1430       setFocus ();
1431   }
1432 
1433   // This slot is a reimplementation of the virtual slot in octave_dock_widget.
1434   // We need this for updating the parent of the find dialog
toplevel_change(bool)1435   void file_editor::toplevel_change (bool)
1436   {
1437     if (m_find_dialog)
1438       {
1439         // close current dialog
1440         m_find_dialog->close ();
1441 
1442         // re-create dialog with the new parent (editor or main-win)
1443         find_create ();
1444         m_find_dialog->activateWindow ();
1445       }
1446   }
1447 
update_octave_directory(const QString & dir)1448   void file_editor::update_octave_directory (const QString& dir)
1449   {
1450     m_ced = dir;
1451     emit fetab_set_directory (m_ced);  // for save dialog
1452   }
1453 
copyClipboard(void)1454   void file_editor::copyClipboard (void)
1455   {
1456     if (editor_tab_has_focus ())
1457       emit fetab_scintilla_command (m_tab_widget->currentWidget (),
1458                                     QsciScintillaBase::SCI_COPY);
1459   }
1460 
pasteClipboard(void)1461   void file_editor::pasteClipboard (void)
1462   {
1463     if (editor_tab_has_focus ())
1464       emit fetab_scintilla_command (m_tab_widget->currentWidget (),
1465                                     QsciScintillaBase::SCI_PASTE);
1466   }
1467 
selectAll(void)1468   void file_editor::selectAll (void)
1469   {
1470     if (editor_tab_has_focus ())
1471       emit fetab_scintilla_command (m_tab_widget->currentWidget (),
1472                                     QsciScintillaBase::SCI_SELECTALL);
1473   }
1474 
do_undo(void)1475   void file_editor::do_undo (void)
1476   {
1477     if (editor_tab_has_focus ())
1478       emit fetab_scintilla_command (m_tab_widget->currentWidget (),
1479                                     QsciScintillaBase::SCI_UNDO);
1480   }
1481 
1482   // Open a file, if not already open, and mark the current execution location
1483   // and/or a breakpoint with condition cond.
request_open_file(const QString & openFileName,const QString & encoding,int line,bool debug_pointer,bool breakpoint_marker,bool insert,const QString & cond,int index)1484   void file_editor::request_open_file (const QString& openFileName,
1485                                        const QString& encoding,
1486                                        int line, bool debug_pointer,
1487                                        bool breakpoint_marker, bool insert,
1488                                        const QString& cond, int index)
1489   {
1490     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1491     gui_settings *settings = rmgr.get_settings ();
1492 
1493     if (settings->value (global_use_custom_editor).toBool ())
1494       {
1495         // Custom editor
1496         if (debug_pointer || breakpoint_marker)
1497           return;   // Do not call custom editor during debugging
1498 
1499         if (call_custom_editor (openFileName, line))
1500           return;   // Custom editor called
1501       }
1502 
1503     bool show_dbg_file
1504       = settings->value (ed_show_dbg_file).toBool ();
1505 
1506     if (openFileName.isEmpty ())
1507       {
1508         // This happens if edit is called without an argument
1509         // Open editor with empty edit area instead (as new file would do)
1510         request_new_file ("");
1511       }
1512     else
1513       {
1514         // Check whether this file is already open in the editor.
1515         file_editor_tab *tab = find_tab_widget (openFileName);
1516 
1517         if (tab)
1518           {
1519             m_tab_widget->setCurrentWidget (tab);
1520 
1521             if (line > 0)
1522               {
1523                 if (insert)
1524                   emit fetab_goto_line (tab, line);
1525 
1526                 if (debug_pointer)
1527                   emit fetab_insert_debugger_pointer (tab, line);
1528 
1529                 if (breakpoint_marker)
1530                   emit fetab_do_breakpoint_marker (insert, tab, line, cond);
1531               }
1532 
1533             if (show_dbg_file && ! ((breakpoint_marker || debug_pointer)
1534                                     && is_editor_console_tabbed ()))
1535               {
1536                 emit fetab_set_focus (tab);
1537                 activate ();
1538               }
1539           }
1540         else
1541           {
1542             if (! show_dbg_file && (breakpoint_marker  || debug_pointer))
1543               return;   // Do not open a file for showing dbg markers
1544 
1545             if (breakpoint_marker && ! insert)
1546               return;   // Never open a file when removing breakpoints
1547 
1548             file_editor_tab *fileEditorTab = nullptr;
1549             // Reuse <unnamed> tab if it hasn't yet been modified.
1550             bool reusing = false;
1551             tab = find_tab_widget ("");
1552             if (tab)
1553               {
1554                 fileEditorTab = tab;
1555                 if (fileEditorTab->qsci_edit_area ()->isModified ())
1556                   fileEditorTab = nullptr;
1557                 else
1558                   reusing = true;
1559               }
1560 
1561             // If <unnamed> was absent or modified, create a new tab.
1562             if (! fileEditorTab)
1563               fileEditorTab = make_file_editor_tab ();
1564 
1565             fileEditorTab->set_encoding (encoding);
1566             QString result = fileEditorTab->load_file (openFileName);
1567             if (result == "")
1568               {
1569                 // Supply empty title then have the file_editor_tab update
1570                 // with full or short name.
1571                 if (! reusing)
1572                   add_file_editor_tab (fileEditorTab, "", index);
1573                 fileEditorTab->update_window_title (false);
1574                 // file already loaded, add file to mru list here
1575                 QFileInfo file_info = QFileInfo (openFileName);
1576                 handle_mru_add_file (file_info.canonicalFilePath (),
1577                                      encoding);
1578 
1579                 if (line > 0)
1580                   {
1581                     if (insert)
1582                       emit fetab_goto_line (fileEditorTab, line);
1583 
1584                     if (debug_pointer)
1585                       emit fetab_insert_debugger_pointer (fileEditorTab,
1586                                                           line);
1587                     if (breakpoint_marker)
1588                       emit fetab_do_breakpoint_marker (insert, fileEditorTab,
1589                                                        line, cond);
1590                   }
1591               }
1592             else
1593               {
1594                 delete fileEditorTab;
1595                 fileEditorTab = nullptr;
1596 
1597                 if (QFile::exists (openFileName))
1598                   {
1599                     // File not readable:
1600                     // create a NonModal message about error.
1601                     QMessageBox *msgBox
1602                       = new QMessageBox (QMessageBox::Critical,
1603                                          tr ("Octave Editor"),
1604                                          tr ("Could not open file\n%1\nfor read: %2.").
1605                                          arg (openFileName).arg (result),
1606                                          QMessageBox::Ok, this);
1607 
1608                     msgBox->setWindowModality (Qt::NonModal);
1609                     msgBox->setAttribute (Qt::WA_DeleteOnClose);
1610                     msgBox->show ();
1611                   }
1612                 else
1613                   {
1614                     // File does not exist, should it be created?
1615                     bool create_file = true;
1616                     QMessageBox *msgBox;
1617 
1618                     if (! settings->value (ed_create_new_file).toBool ())
1619                       {
1620                         msgBox = new QMessageBox (QMessageBox::Question,
1621                                                   tr ("Octave Editor"),
1622                                                   tr ("File\n%1\ndoes not exist. "
1623                                                       "Do you want to create it?").arg (openFileName),
1624                                                   QMessageBox::NoButton,nullptr);
1625                         QPushButton *create_button =
1626                           msgBox->addButton (tr ("Create"), QMessageBox::YesRole);
1627                         msgBox->addButton (tr ("Cancel"), QMessageBox::RejectRole);
1628                         msgBox->setDefaultButton (create_button);
1629                         msgBox->exec ();
1630 
1631                         QAbstractButton *clicked_button = msgBox->clickedButton ();
1632                         if (clicked_button != create_button)
1633                           create_file = false;
1634 
1635                         delete msgBox;
1636                       }
1637 
1638                     if (create_file)
1639                       {
1640                         // create the file and call the editor again
1641                         QFile file (openFileName);
1642                         if (! file.open (QIODevice::WriteOnly))
1643                           {
1644                             // error opening the file
1645                             msgBox = new QMessageBox (QMessageBox::Critical,
1646                                                       tr ("Octave Editor"),
1647                                                       tr ("Could not open file\n%1\nfor write: %2.").
1648                                                       arg (openFileName).arg (file.errorString ()),
1649                                                       QMessageBox::Ok, this);
1650 
1651                             msgBox->setWindowModality (Qt::NonModal);
1652                             msgBox->setAttribute (Qt::WA_DeleteOnClose);
1653                             msgBox->show ();
1654                           }
1655                         else
1656                           {
1657                             file.close ();
1658                             request_open_file (openFileName);
1659                           }
1660                       }
1661                   }
1662               }
1663 
1664             if (! ((breakpoint_marker || debug_pointer) && is_editor_console_tabbed ()))
1665               {
1666                 // update breakpoint pointers, really show editor
1667                 // and the current editor tab
1668                 if (fileEditorTab)
1669                   fileEditorTab->update_breakpoints ();
1670                 activate ();
1671                 emit file_loaded_signal ();
1672               }
1673           }
1674       }
1675   }
1676 
request_preferences(bool)1677   void file_editor::request_preferences (bool)
1678   {
1679     emit request_settings_dialog ("editor");
1680   }
1681 
request_styles_preferences(bool)1682   void file_editor::request_styles_preferences (bool)
1683   {
1684     emit request_settings_dialog ("editor_styles");
1685   }
1686 
show_line_numbers(bool)1687   void file_editor::show_line_numbers (bool)
1688   {
1689     toggle_preference (ed_show_line_numbers);
1690   }
1691 
show_white_space(bool)1692   void file_editor::show_white_space (bool)
1693   {
1694     toggle_preference (ed_show_white_space);
1695   }
1696 
show_eol_chars(bool)1697   void file_editor::show_eol_chars (bool)
1698   {
1699     toggle_preference (ed_show_eol_chars);
1700   }
1701 
show_indent_guides(bool)1702   void file_editor::show_indent_guides (bool)
1703   {
1704     toggle_preference (ed_show_indent_guides);
1705   }
1706 
show_long_line(bool)1707   void file_editor::show_long_line (bool)
1708   {
1709     toggle_preference (ed_long_line_marker);
1710   }
1711 
show_toolbar(bool)1712   void file_editor::show_toolbar (bool)
1713   {
1714     toggle_preference (ed_show_toolbar);
1715   }
1716 
show_statusbar(bool)1717   void file_editor::show_statusbar (bool)
1718   {
1719     toggle_preference (ed_show_edit_status_bar);
1720   }
1721 
show_hscrollbar(bool)1722   void file_editor::show_hscrollbar (bool)
1723   {
1724     toggle_preference (ed_show_hscroll_bar);
1725   }
1726 
zoom_in(bool)1727   void file_editor::zoom_in (bool)
1728   {
1729     emit fetab_zoom_in (m_tab_widget->currentWidget ());
1730   }
1731 
zoom_out(bool)1732   void file_editor::zoom_out (bool)
1733   {
1734     emit fetab_zoom_out (m_tab_widget->currentWidget ());
1735   }
1736 
zoom_normal(bool)1737   void file_editor::zoom_normal (bool)
1738   {
1739     emit fetab_zoom_normal (m_tab_widget->currentWidget ());
1740   }
1741 
create_context_menu(QMenu * menu)1742   void file_editor::create_context_menu (QMenu *menu)
1743   {
1744     // remove all standard actions from scintilla
1745     QList<QAction *> all_actions = menu->actions ();
1746 
1747     for (auto *a : all_actions)
1748       menu->removeAction (a);
1749 
1750     // add editor's actions with icons and customized shortcuts
1751     menu->addAction (m_cut_action);
1752     menu->addAction (m_copy_action);
1753     menu->addAction (m_paste_action);
1754     menu->addSeparator ();
1755     menu->addAction (m_selectall_action);
1756     menu->addSeparator ();
1757     menu->addAction (m_find_files_action);
1758     menu->addAction (m_find_action);
1759     menu->addAction (m_find_next_action);
1760     menu->addAction (m_find_previous_action);
1761     menu->addSeparator ();
1762     menu->addMenu (m_edit_cmd_menu);
1763     menu->addMenu (m_edit_fmt_menu);
1764     menu->addMenu (m_edit_nav_menu);
1765     menu->addSeparator ();
1766     menu->addAction (m_run_selection_action);
1767   }
1768 
edit_status_update(bool undo,bool redo)1769   void file_editor::edit_status_update (bool undo, bool redo)
1770   {
1771     if (m_undo_action)
1772       m_undo_action->setEnabled (undo);
1773     m_redo_action->setEnabled (redo);
1774   }
1775 
1776   // handler for the close event
closeEvent(QCloseEvent * e)1777   void file_editor::closeEvent (QCloseEvent *e)
1778   {
1779     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1780     gui_settings *settings = rmgr.get_settings ();
1781     if (settings->value (ed_hiding_closes_files).toBool ())
1782       {
1783         if (check_closing ())
1784           {
1785             // All tabs are closed without cancelling,
1786             // store closing state for restoring session when shown again.
1787             // Editor is closing when session data is stored in preferences
1788             m_closed = true;
1789             e->ignore ();
1790           }
1791         else
1792           {
1793             e->ignore ();
1794             return;
1795           }
1796       }
1797     else
1798       e->accept ();
1799 
1800     octave_dock_widget::closeEvent (e);
1801   }
1802 
dragEnterEvent(QDragEnterEvent * e)1803   void file_editor::dragEnterEvent (QDragEnterEvent *e)
1804   {
1805     if (e->mimeData ()->hasUrls ())
1806       {
1807         e->acceptProposedAction ();
1808       }
1809   }
1810 
dropEvent(QDropEvent * e)1811   void file_editor::dropEvent (QDropEvent *e)
1812   {
1813     if (e->mimeData ()->hasUrls ())
1814       {
1815         for (const auto& url : e->mimeData ()->urls ())
1816           request_open_file (url.toLocalFile ());
1817       }
1818   }
1819 
is_editor_console_tabbed(void)1820   bool file_editor::is_editor_console_tabbed (void)
1821   {
1822     main_window *w = static_cast<main_window *>(main_win ());
1823     QList<QDockWidget *> w_list = w->tabifiedDockWidgets (this);
1824     QDockWidget *console =
1825       static_cast<QDockWidget *> (w->get_dock_widget_list ().at (0));
1826 
1827     for (int i = 0; i < w_list.count (); i++)
1828       {
1829         if (w_list.at (i) == console)
1830           return true;
1831       }
1832 
1833     return false;
1834   }
1835 
construct(void)1836   void file_editor::construct (void)
1837   {
1838     QWidget *editor_widget = new QWidget (this);
1839 
1840     // FIXME: what was the intended purpose of this unused variable?
1841     // QStyle *editor_style = QApplication::style ();
1842 
1843     // Menu bar: do not set it native, required in macOS and Ubuntu Unity (Qt5)
1844     // for a visible menu bar in the editor widget.  This property is ignored
1845     // on other platforms.
1846     m_menu_bar = new QMenuBar (editor_widget);
1847     m_menu_bar->setNativeMenuBar (false);
1848 
1849     m_tool_bar = new QToolBar (editor_widget);
1850     m_tool_bar->setMovable (true);
1851 
1852     m_tab_widget = new file_editor_tab_widget (editor_widget);
1853 
1854     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
1855 
1856     // the mru-list and an empty array of actions
1857     gui_settings *settings = rmgr.get_settings ();
1858     m_mru_files = settings->value (ed_mru_file_list).toStringList ();
1859     m_mru_files_encodings = settings->value (ed_mru_file_encodings)
1860                             .toStringList ();
1861 
1862     if (m_mru_files_encodings.count () != m_mru_files.count ())
1863       {
1864         // encodings don't have the same count -> do not use them!
1865         m_mru_files_encodings = QStringList ();
1866         for (int i = 0; i < m_mru_files.count (); i++)
1867           m_mru_files_encodings << QString ();
1868       }
1869 
1870     for (int i = 0; i < MaxMRUFiles; ++i)
1871       {
1872         m_mru_file_actions[i] = new QAction (this);
1873         m_mru_file_actions[i]->setVisible (false);
1874       }
1875 
1876     // menu bar
1877 
1878     // file menu
1879 
1880     m_fileMenu = add_menu (m_menu_bar, tr ("&File"));
1881 
1882     // new and open menus are inserted later by the main window
1883     m_mru_file_menu = new QMenu (tr ("&Recent Editor Files"), m_fileMenu);
1884     for (int i = 0; i < MaxMRUFiles; ++i)
1885       m_mru_file_menu->addAction (m_mru_file_actions[i]);
1886     m_fileMenu->addMenu (m_mru_file_menu);
1887 
1888     m_fileMenu->addSeparator ();
1889 
1890     m_edit_function_action
1891       = add_action (m_fileMenu,
1892                     tr ("&Edit Function"),
1893                     SLOT (request_context_edit (bool)));
1894 
1895     m_fileMenu->addSeparator ();
1896 
1897     m_save_action
1898       = add_action (m_fileMenu, rmgr.icon ("document-save"),
1899                     tr ("&Save File"), SLOT (request_save_file (bool)));
1900 
1901     m_save_as_action
1902       = add_action (m_fileMenu, rmgr.icon ("document-save-as"),
1903                     tr ("Save File &As..."),
1904                     SLOT (request_save_file_as (bool)));
1905 
1906     m_fileMenu->addSeparator ();
1907 
1908     m_close_action
1909       = add_action (m_fileMenu, rmgr.icon ("window-close",false),
1910                     tr ("&Close"), SLOT (request_close_file (bool)));
1911 
1912     m_close_all_action
1913       = add_action (m_fileMenu, rmgr.icon ("window-close",false),
1914                     tr ("Close All"), SLOT (request_close_all_files (bool)));
1915 
1916     m_close_others_action
1917       = add_action (m_fileMenu, rmgr.icon ("window-close",false),
1918                     tr ("Close Other Files"),
1919                     SLOT (request_close_other_files (bool)));
1920 
1921     m_fileMenu->addSeparator ();
1922 
1923     m_print_action
1924       = add_action (m_fileMenu, rmgr.icon ("document-print"),
1925                     tr ("Print..."), SLOT (request_print_file (bool)));
1926 
1927     // edit menu (undo, copy, paste and select all later via main window)
1928 
1929     m_edit_menu = add_menu (m_menu_bar, tr ("&Edit"));
1930 
1931     m_redo_action
1932       = add_action (m_edit_menu, rmgr.icon ("edit-redo"),
1933                     tr ("&Redo"), SLOT (request_redo (bool)));
1934     m_redo_action->setEnabled (false);
1935 
1936     m_edit_menu->addSeparator ();
1937 
1938     m_cut_action
1939       = add_action (m_edit_menu, rmgr.icon ("edit-cut"),
1940                     tr ("Cu&t"), SLOT (request_cut (bool)));
1941     m_cut_action->setEnabled (false);
1942 
1943     m_find_action
1944       = add_action (m_edit_menu, rmgr.icon ("edit-find-replace"),
1945                     tr ("&Find and Replace..."), SLOT (request_find (bool)));
1946 
1947     m_find_next_action
1948       = add_action (m_edit_menu, tr ("Find &Next..."),
1949                     SLOT (request_find_next (bool)));
1950 
1951     m_find_previous_action
1952       = add_action (m_edit_menu, tr ("Find &Previous..."),
1953                     SLOT (request_find_previous (bool)));
1954 
1955     m_edit_menu->addSeparator ();
1956 
1957     m_edit_cmd_menu = m_edit_menu->addMenu (tr ("&Commands"));
1958 
1959     m_delete_line_action
1960       = add_action (m_edit_cmd_menu, tr ("Delete Line"),
1961                     SLOT (request_delete_line (bool)));
1962 
1963     m_copy_line_action
1964       = add_action (m_edit_cmd_menu, tr ("Copy Line"),
1965                     SLOT (request_copy_line (bool)));
1966 
1967     m_cut_line_action
1968       = add_action (m_edit_cmd_menu, tr ("Cut Line"),
1969                     SLOT (request_cut_line (bool)));
1970 
1971     m_edit_cmd_menu->addSeparator ();
1972 
1973     m_delete_start_word_action
1974       = add_action (m_edit_cmd_menu, tr ("Delete to Start of Word"),
1975                     SLOT (request_delete_start_word (bool)));
1976 
1977     m_delete_end_word_action
1978       = add_action (m_edit_cmd_menu, tr ("Delete to End of Word"),
1979                     SLOT (request_delete_end_word (bool)));
1980 
1981     m_delete_start_line_action
1982       = add_action (m_edit_cmd_menu, tr ("Delete to Start of Line"),
1983                     SLOT (request_delete_start_line (bool)));
1984 
1985     m_delete_end_line_action
1986       = add_action (m_edit_cmd_menu, tr ("Delete to End of Line"),
1987                     SLOT (request_delete_end_line (bool)));
1988 
1989     m_edit_cmd_menu->addSeparator ();
1990 
1991     m_duplicate_selection_action
1992       = add_action (m_edit_cmd_menu, tr ("Duplicate Selection/Line"),
1993                     SLOT (request_duplicate_selection (bool)));
1994 
1995     m_transpose_line_action
1996       = add_action (m_edit_cmd_menu, tr ("Transpose Line"),
1997                     SLOT (request_transpose_line (bool)));
1998 
1999     m_edit_cmd_menu->addSeparator ();
2000 
2001     m_completion_action
2002       = add_action (m_edit_cmd_menu, tr ("&Show Completion List"),
2003                     SLOT (request_completion (bool)));
2004 
2005     m_edit_fmt_menu = m_edit_menu->addMenu (tr ("&Format"));
2006 
2007     m_upper_case_action
2008       = add_action (m_edit_fmt_menu, tr ("&Uppercase Selection"),
2009                     SLOT (request_upper_case (bool)));
2010 
2011     m_lower_case_action
2012       = add_action (m_edit_fmt_menu, tr ("&Lowercase Selection"),
2013                     SLOT (request_lower_case (bool)));
2014 
2015     m_edit_fmt_menu->addSeparator ();
2016 
2017     m_comment_selection_action
2018       = add_action (m_edit_fmt_menu, tr ("&Comment"),
2019                     SLOT (request_comment_selected_text (bool)));
2020 
2021     m_uncomment_selection_action
2022       = add_action (m_edit_fmt_menu, tr ("&Uncomment"),
2023                     SLOT (request_uncomment_selected_text (bool)));
2024 
2025     m_comment_var_selection_action
2026       = add_action (m_edit_fmt_menu, tr ("Comment (Choosing String)"),
2027                     SLOT (request_comment_var_selected_text (bool)));
2028 
2029     m_edit_fmt_menu->addSeparator ();
2030 
2031     m_indent_selection_action
2032       = add_action (m_edit_fmt_menu, tr ("&Indent Selection Rigidly"),
2033                     SLOT (request_indent_selected_text (bool)));
2034 
2035     m_unindent_selection_action
2036       = add_action (m_edit_fmt_menu, tr ("&Unindent Selection Rigidly"),
2037                     SLOT (request_unindent_selected_text (bool)));
2038 
2039     m_smart_indent_line_or_selection_action
2040       = add_action (m_edit_fmt_menu, tr ("Indent Code"),
2041                     SLOT (request_smart_indent_line_or_selected_text (void)));
2042 
2043     m_edit_fmt_menu->addSeparator ();
2044 
2045     m_conv_eol_windows_action
2046       = add_action (m_edit_fmt_menu,
2047                     tr ("Convert Line Endings to &Windows (CRLF)"),
2048                     SLOT (request_conv_eol_windows (bool)));
2049 
2050     m_conv_eol_unix_action
2051       = add_action (m_edit_fmt_menu, tr ("Convert Line Endings to &Unix (LF)"),
2052                     SLOT (request_conv_eol_unix (bool)));
2053 
2054     m_conv_eol_mac_action
2055       = add_action (m_edit_fmt_menu,
2056                     tr ("Convert Line Endings to Legacy &Mac (CR)"),
2057                     SLOT (request_conv_eol_mac (bool)));
2058 
2059     m_edit_nav_menu = m_edit_menu->addMenu (tr ("Navi&gation"));
2060 
2061     m_goto_line_action
2062       = add_action (m_edit_nav_menu, tr ("Go &to Line..."),
2063                     SLOT (request_goto_line (bool)));
2064 
2065     m_edit_cmd_menu->addSeparator ();
2066 
2067     m_move_to_matching_brace
2068       = add_action (m_edit_nav_menu, tr ("Move to Matching Brace"),
2069                     SLOT (request_move_match_brace (bool)));
2070 
2071     m_sel_to_matching_brace
2072       = add_action (m_edit_nav_menu, tr ("Select to Matching Brace"),
2073                     SLOT (request_sel_match_brace (bool)));
2074 
2075     m_edit_nav_menu->addSeparator ();
2076 
2077     m_next_bookmark_action
2078       = add_action (m_edit_nav_menu, tr ("&Next Bookmark"),
2079                     SLOT (request_next_bookmark (bool)));
2080 
2081     m_previous_bookmark_action
2082       = add_action (m_edit_nav_menu, tr ("Pre&vious Bookmark"),
2083                     SLOT (request_previous_bookmark (bool)));
2084 
2085     m_toggle_bookmark_action
2086       = add_action (m_edit_nav_menu, tr ("Toggle &Bookmark"),
2087                     SLOT (request_toggle_bookmark (bool)));
2088 
2089     m_remove_bookmark_action
2090       = add_action (m_edit_nav_menu, tr ("&Remove All Bookmarks"),
2091                     SLOT (request_remove_bookmark (bool)));
2092 
2093     m_edit_menu->addSeparator ();
2094 
2095     m_preferences_action
2096       = add_action (m_edit_menu, rmgr.icon ("preferences-system"),
2097                     tr ("&Preferences..."),
2098                     SLOT (request_preferences (bool)));
2099 
2100     m_styles_preferences_action
2101       = add_action (m_edit_menu, rmgr.icon ("preferences-system"),
2102                     tr ("&Styles Preferences..."),
2103                     SLOT (request_styles_preferences (bool)));
2104 
2105     // view menu
2106 
2107     QMenu *view_menu = add_menu (m_menu_bar, tr ("&View"));
2108 
2109     m_view_editor_menu = view_menu->addMenu (tr ("&Editor"));
2110 
2111     m_show_linenum_action
2112       = add_action (m_view_editor_menu, tr ("Show &Line Numbers"),
2113                     SLOT (show_line_numbers (bool)));
2114     m_show_linenum_action->setCheckable (true);
2115 
2116     m_show_whitespace_action
2117       = add_action (m_view_editor_menu, tr ("Show &Whitespace Characters"),
2118                     SLOT (show_white_space (bool)));
2119     m_show_whitespace_action->setCheckable (true);
2120 
2121     m_show_eol_action
2122       = add_action (m_view_editor_menu, tr ("Show Line &Endings"),
2123                     SLOT (show_eol_chars (bool)));
2124     m_show_eol_action->setCheckable (true);
2125 
2126     m_show_indguide_action
2127       = add_action (m_view_editor_menu, tr ("Show &Indentation Guides"),
2128                     SLOT (show_indent_guides (bool)));
2129     m_show_indguide_action->setCheckable (true);
2130 
2131     m_show_longline_action
2132       = add_action (m_view_editor_menu, tr ("Show Long Line &Marker"),
2133                     SLOT (show_long_line (bool)));
2134     m_show_longline_action->setCheckable (true);
2135 
2136     m_view_editor_menu->addSeparator ();
2137 
2138     m_show_toolbar_action
2139       = add_action (m_view_editor_menu, tr ("Show &Toolbar"),
2140                     SLOT (show_toolbar (bool)));
2141     m_show_toolbar_action->setCheckable (true);
2142 
2143     m_show_statusbar_action
2144       = add_action (m_view_editor_menu, tr ("Show &Statusbar"),
2145                     SLOT (show_statusbar (bool)));
2146     m_show_statusbar_action->setCheckable (true);
2147 
2148     m_show_hscrollbar_action
2149       = add_action (m_view_editor_menu, tr ("Show &Horizontal Scrollbar"),
2150                     SLOT (show_hscrollbar (bool)));
2151     m_show_hscrollbar_action->setCheckable (true);
2152 
2153     view_menu->addSeparator ();
2154 
2155     m_zoom_in_action
2156       = add_action (view_menu, rmgr.icon ("zoom-in"), tr ("Zoom &In"),
2157                     SLOT (zoom_in (bool)));
2158 
2159     m_zoom_out_action
2160       = add_action (view_menu, rmgr.icon ("zoom-out"), tr ("Zoom &Out"),
2161                     SLOT (zoom_out (bool)));
2162 
2163     m_zoom_normal_action
2164       = add_action (view_menu, tr ("&Normal Size"), SLOT (zoom_normal (bool)));
2165 
2166     view_menu->addSeparator ();
2167 
2168     m_sort_tabs_action
2169       = add_action (view_menu, tr ("&Sort Tabs Alphabetically"),
2170                     SLOT (sort_tabs_alph (void)),
2171                     m_tab_widget->get_tab_bar ());
2172 
2173     m_menu_bar->addMenu (view_menu);
2174 
2175     // debug menu
2176 
2177     m_debug_menu = add_menu (m_menu_bar, tr ("&Debug"));
2178 
2179     m_toggle_breakpoint_action
2180       = add_action (m_debug_menu, rmgr.icon ("bp-toggle"),
2181                     tr ("Toggle &Breakpoint"),
2182                     SLOT (request_toggle_breakpoint (bool)));
2183 
2184     m_next_breakpoint_action
2185       = add_action (m_debug_menu, rmgr.icon ("bp-next"),
2186                     tr ("&Next Breakpoint"),
2187                     SLOT (request_next_breakpoint (bool)));
2188 
2189     m_previous_breakpoint_action
2190       = add_action (m_debug_menu, rmgr.icon ("bp-prev"),
2191                     tr ("Pre&vious Breakpoint"),
2192                     SLOT (request_previous_breakpoint (bool)));
2193 
2194     m_remove_all_breakpoints_action
2195       = add_action (m_debug_menu, rmgr.icon ("bp-rm-all"),
2196                     tr ("&Remove All Breakpoints"),
2197                     SLOT (request_remove_breakpoint (bool)));
2198 
2199     m_debug_menu->addSeparator ();
2200 
2201     // The other debug actions will be added by the main window.
2202 
2203     // run menu
2204 
2205     QMenu *_run_menu = add_menu (m_menu_bar, tr ("&Run"));
2206 
2207     m_run_action
2208       = add_action (_run_menu,
2209                     rmgr.icon ("system-run"),
2210                     tr ("Save File and Run / Continue"),
2211                     SLOT (request_run_file (bool)));
2212 
2213     m_run_selection_action
2214       = add_action (_run_menu,
2215                     tr ("Run &Selection"),
2216                     SLOT (request_context_run (bool)));
2217     m_run_selection_action->setEnabled (false);
2218 
2219     // help menu
2220 
2221     QMenu *_help_menu = add_menu (m_menu_bar, tr ("&Help"));
2222 
2223     m_context_help_action
2224       = add_action (_help_menu,
2225                     tr ("&Help on Keyword"),
2226                     SLOT (request_context_help (bool)));
2227 
2228     m_context_doc_action
2229       = add_action (_help_menu,
2230                     tr ("&Documentation on Keyword"),
2231                     SLOT (request_context_doc (bool)));
2232 
2233     // tab navigation (no menu, only actions; slots in tab_bar)
2234 
2235     m_switch_left_tab_action
2236       = add_action (nullptr, "", SLOT (switch_left_tab (void)),
2237                     m_tab_widget->get_tab_bar ());
2238 
2239     m_switch_right_tab_action
2240       = add_action (nullptr, "", SLOT (switch_right_tab (void)),
2241                     m_tab_widget->get_tab_bar ());
2242 
2243     m_move_tab_left_action
2244       = add_action (nullptr, "", SLOT (move_tab_left (void)),
2245                     m_tab_widget->get_tab_bar ());
2246 
2247     m_move_tab_right_action
2248       = add_action (nullptr, "", SLOT (move_tab_right (void)),
2249                     m_tab_widget->get_tab_bar ());
2250 
2251     // toolbar
2252 
2253     // popdown menu with mru files
2254     QToolButton *popdown_button = new QToolButton ();
2255     popdown_button->setToolTip (tr ("Recent Files"));
2256     popdown_button->setMenu (m_mru_file_menu);
2257     popdown_button->setPopupMode (QToolButton::InstantPopup);
2258     popdown_button->setArrowType (Qt::DownArrow);
2259     popdown_button->setToolButtonStyle (Qt::ToolButtonTextOnly);
2260 
2261     // new and open actions are inserted later from main window
2262     m_popdown_mru_action = m_tool_bar->addWidget (popdown_button);
2263     m_tool_bar->addAction (m_save_action);
2264     m_tool_bar->addAction (m_save_as_action);
2265     m_tool_bar->addAction (m_print_action);
2266     m_tool_bar->addSeparator ();
2267     // m_undo_action: later via main window
2268     m_tool_bar->addAction (m_redo_action);
2269     m_tool_bar->addSeparator ();
2270     m_tool_bar->addAction (m_cut_action);
2271     // m_copy_action: later via the main window
2272     // m_paste_action: later via the main window
2273     m_tool_bar->addAction (m_find_action);
2274     //m_tool_bar->addAction (m_find_next_action);
2275     //m_tool_bar->addAction (m_find_previous_action);
2276     m_tool_bar->addSeparator ();
2277     m_tool_bar->addAction (m_run_action);
2278     m_tool_bar->addSeparator ();
2279     m_tool_bar->addAction (m_toggle_breakpoint_action);
2280     m_tool_bar->addAction (m_previous_breakpoint_action);
2281     m_tool_bar->addAction (m_next_breakpoint_action);
2282     m_tool_bar->addAction (m_remove_all_breakpoints_action);
2283 
2284     // layout
2285     QVBoxLayout *vbox_layout = new QVBoxLayout ();
2286     vbox_layout->addWidget (m_menu_bar);
2287     vbox_layout->addWidget (m_tool_bar);
2288     vbox_layout->addWidget (m_tab_widget);
2289     vbox_layout->setMargin (0);
2290     vbox_layout->setSpacing (0);
2291     editor_widget->setLayout (vbox_layout);
2292     setWidget (editor_widget);
2293 
2294     // create the context menu of the tab bar
2295     tab_bar *bar = m_tab_widget->get_tab_bar ();
2296     QMenu *ctx_men = bar->get_context_menu ();
2297     ctx_men->addAction (m_close_action);
2298     ctx_men->addAction (m_close_all_action);
2299     ctx_men->addAction (m_close_others_action);
2300     ctx_men->addSeparator ();
2301     ctx_men->addAction (m_sort_tabs_action);
2302 
2303     // signals
2304     connect (this, SIGNAL (request_settings_dialog (const QString&)),
2305              main_win (),
2306              SLOT (process_settings_dialog_request (const QString&)));
2307 
2308     connect (this, SIGNAL (request_dbcont_signal (void)),
2309              main_win (), SLOT (debug_continue (void)));
2310 
2311     connect (m_mru_file_menu, SIGNAL (triggered (QAction *)),
2312              this, SLOT (request_mru_open_file (QAction *)));
2313 
2314     mru_menu_update ();
2315 
2316     connect (m_tab_widget, SIGNAL (tabCloseRequested (int)),
2317              this, SLOT (handle_tab_close_request (int)));
2318 
2319     connect (m_tab_widget, SIGNAL (currentChanged (int)),
2320              this, SLOT (active_tab_changed (int)));
2321 
2322     resize (500, 400);
2323     setWindowIcon (QIcon (":/actions/icons/logo.png"));
2324     set_title (tr ("Editor"));
2325 
2326     check_actions ();
2327   }
2328 
2329   // Slot when autocompletion list was cancelled
handle_autoc_cancelled(void)2330   void file_editor::handle_autoc_cancelled (void)
2331   {
2332     // List was cancelled but somehow still active and blocking the
2333     // edit area from accepting shortcuts. Only after another keypress
2334     // shortcuts and lists are working againnas expected. This is
2335     // probably caused by qt bug https://bugreports.qt.io/browse/QTBUG-83720
2336     // Hack: Accept the list, which is hidden but still active
2337     //       and undo the text insertion, if any
2338 
2339     file_editor_tab *f = reset_focus ();
2340     octave_qscintilla *qsci = f->qsci_edit_area ();
2341 
2342     int line, col;
2343     qsci->getCursorPosition (&line, &col);
2344     int l1 = qsci->lineLength (line); // Current line length
2345 
2346     // Accept autocompletion
2347     qsci->SendScintilla (QsciScintillaBase::SCI_AUTOCCOMPLETE);
2348 
2349     // Was text inserted? If yes, undo
2350     if (qsci->text (line).length () - l1)
2351       qsci->undo ();
2352   }
2353 
reset_focus(void)2354   file_editor_tab* file_editor::reset_focus (void)
2355   {
2356     // Reset the focus of the tab and the related edit area
2357     file_editor_tab *f
2358        = static_cast<file_editor_tab *> (m_tab_widget->currentWidget ());
2359      emit fetab_set_focus (f);
2360      return f;
2361   }
2362 
2363   file_editor_tab *
make_file_editor_tab(const QString & directory)2364   file_editor::make_file_editor_tab (const QString& directory)
2365   {
2366     file_editor_tab *f = new file_editor_tab (m_octave_qobj, directory);
2367 
2368     // signals from the qscintilla edit area
2369     connect (f->qsci_edit_area (), SIGNAL (status_update (bool, bool)),
2370              this, SLOT (edit_status_update (bool, bool)));
2371 
2372     connect (f->qsci_edit_area (), SIGNAL (show_doc_signal (const QString&)),
2373              main_win (), SLOT (handle_show_doc (const QString&)));
2374 
2375     connect (f->qsci_edit_area (), SIGNAL (create_context_menu_signal (QMenu *)),
2376              this, SLOT (create_context_menu (QMenu *)));
2377 
2378     connect (f->qsci_edit_area (),
2379              SIGNAL (execute_command_in_terminal_signal (const QString&)),
2380              main_win (), SLOT (execute_command_in_terminal (const QString&)));
2381 
2382     connect (f->qsci_edit_area (),
2383              SIGNAL (focus_console_after_command_signal (void)),
2384              main_win (), SLOT (focus_console_after_command (void)));
2385 
2386     connect (f->qsci_edit_area (),
2387              SIGNAL (SCN_AUTOCCOMPLETED (const char*, int, int, int)),
2388              this, SLOT (reset_focus (void)));
2389 
2390     connect (f->qsci_edit_area (), SIGNAL (SCN_AUTOCCANCELLED (void)),
2391              this, SLOT (handle_autoc_cancelled (void)));
2392 
2393     // Signals from the file editor_tab
2394     connect (f, SIGNAL (autoc_closed (void)),
2395              this, SLOT (reset_focus (void)));
2396 
2397     connect (f, SIGNAL (file_name_changed (const QString&, const QString&, bool)),
2398              this, SLOT (handle_file_name_changed (const QString&,
2399                                                    const QString&, bool)));
2400 
2401     connect (f, SIGNAL (editor_state_changed (bool, bool)),
2402              this, SLOT (handle_editor_state_changed (bool, bool)));
2403 
2404     connect (f, SIGNAL (tab_remove_request ()),
2405              this, SLOT (handle_tab_remove_request ()));
2406 
2407     connect (f, SIGNAL (editor_check_conflict_save (const QString&, bool)),
2408              this, SLOT (check_conflict_save (const QString&, bool)));
2409 
2410     connect (f, SIGNAL (mru_add_file (const QString&, const QString&)),
2411              this, SLOT (handle_mru_add_file (const QString&, const QString&)));
2412 
2413     connect (f, SIGNAL (run_file_signal (const QFileInfo&)),
2414              main_win (), SLOT (run_file_in_terminal (const QFileInfo&)));
2415 
2416     connect (f, SIGNAL (request_open_file (const QString&, const QString&)),
2417              this, SLOT (request_open_file (const QString&, const QString&)));
2418 
2419     connect (f, SIGNAL (edit_mfile_request (const QString&, const QString&,
2420                                             const QString&, int)),
2421              main_win (), SLOT (handle_edit_mfile_request (const QString&,
2422                                                            const QString&,
2423                                                            const QString&, int)));
2424 
2425     connect (f, SIGNAL (edit_area_changed (octave_qscintilla*)),
2426              this, SIGNAL (edit_area_changed (octave_qscintilla*)));
2427 
2428     connect (f, SIGNAL (set_focus_editor_signal (QWidget*)),
2429              this, SLOT (set_focus (QWidget*)));
2430 
2431     // Signals from the file_editor non-trivial operations
2432     connect (this, SIGNAL (fetab_settings_changed (const gui_settings *)),
2433              f, SLOT (notice_settings (const gui_settings *)));
2434 
2435     connect (this, SIGNAL (fetab_change_request (const QWidget*)),
2436              f, SLOT (change_editor_state (const QWidget*)));
2437 
2438     connect (this, SIGNAL (fetab_save_file (const QWidget*, const QString&,
2439                                             bool)),
2440              f, SLOT (save_file (const QWidget*, const QString&, bool)));
2441 
2442     // Signals from the file_editor trivial operations
2443     connect (this, SIGNAL (fetab_recover_from_exit (void)),
2444              f, SLOT (recover_from_exit (void)));
2445 
2446     connect (this, SIGNAL (fetab_set_directory (const QString&)),
2447              f, SLOT (set_current_directory (const QString&)));
2448 
2449     connect (this, SIGNAL (fetab_zoom_in (const QWidget*)),
2450              f, SLOT (zoom_in (const QWidget*)));
2451     connect (this, SIGNAL (fetab_zoom_out (const QWidget*)),
2452              f, SLOT (zoom_out (const QWidget*)));
2453     connect (this, SIGNAL (fetab_zoom_normal (const QWidget*)),
2454              f, SLOT (zoom_normal (const QWidget*)));
2455 
2456     connect (this, SIGNAL (fetab_context_help (const QWidget*, bool)),
2457              f, SLOT (context_help (const QWidget*, bool)));
2458 
2459     connect (this, SIGNAL (fetab_context_edit (const QWidget*)),
2460              f, SLOT (context_edit (const QWidget*)));
2461 
2462     connect (this, SIGNAL (fetab_save_file (const QWidget*)),
2463              f, SLOT (save_file (const QWidget*)));
2464 
2465     connect (this, SIGNAL (fetab_save_file_as (const QWidget*)),
2466              f, SLOT (save_file_as (const QWidget*)));
2467 
2468     connect (this, SIGNAL (fetab_print_file (const QWidget*)),
2469              f, SLOT (print_file (const QWidget*)));
2470 
2471     connect (this, SIGNAL (fetab_run_file (const QWidget*, bool)),
2472              f, SLOT (run_file (const QWidget*, bool)));
2473 
2474     connect (this, SIGNAL (fetab_context_run (const QWidget*)),
2475              f, SLOT (context_run (const QWidget*)));
2476 
2477     connect (this, SIGNAL (fetab_toggle_bookmark (const QWidget*)),
2478              f, SLOT (toggle_bookmark (const QWidget*)));
2479 
2480     connect (this, SIGNAL (fetab_next_bookmark (const QWidget*)),
2481              f, SLOT (next_bookmark (const QWidget*)));
2482 
2483     connect (this, SIGNAL (fetab_previous_bookmark (const QWidget*)),
2484              f, SLOT (previous_bookmark (const QWidget*)));
2485 
2486     connect (this, SIGNAL (fetab_remove_bookmark (const QWidget*)),
2487              f, SLOT (remove_bookmark (const QWidget*)));
2488 
2489     connect (this, SIGNAL (fetab_toggle_breakpoint (const QWidget*)),
2490              f, SLOT (toggle_breakpoint (const QWidget*)));
2491 
2492     connect (this, SIGNAL (fetab_next_breakpoint (const QWidget*)),
2493              f, SLOT (next_breakpoint (const QWidget*)));
2494 
2495     connect (this, SIGNAL (fetab_previous_breakpoint (const QWidget*)),
2496              f, SLOT (previous_breakpoint (const QWidget*)));
2497 
2498     connect (this, SIGNAL (fetab_remove_all_breakpoints (const QWidget*)),
2499              f, SLOT (remove_all_breakpoints (const QWidget*)));
2500 
2501     connect (this, SIGNAL (fetab_scintilla_command (const QWidget *,
2502                                                     unsigned int)),
2503              f, SLOT (scintilla_command (const QWidget *, unsigned int)));
2504 
2505     connect (this, SIGNAL (fetab_comment_selected_text (const QWidget*, bool)),
2506              f, SLOT (comment_selected_text (const QWidget*, bool)));
2507 
2508     connect (this, SIGNAL (fetab_uncomment_selected_text (const QWidget*)),
2509              f, SLOT (uncomment_selected_text (const QWidget*)));
2510 
2511     connect (this, SIGNAL (fetab_indent_selected_text (const QWidget*)),
2512              f, SLOT (indent_selected_text (const QWidget*)));
2513 
2514     connect (this, SIGNAL (fetab_unindent_selected_text (const QWidget*)),
2515              f, SLOT (unindent_selected_text (const QWidget*)));
2516 
2517     connect (this, SIGNAL (fetab_smart_indent_line_or_selected_text (const QWidget*)),
2518              f, SLOT (smart_indent_line_or_selected_text (const QWidget*)));
2519 
2520     connect (this,
2521              SIGNAL (fetab_convert_eol (const QWidget*, QsciScintilla::EolMode)),
2522              f, SLOT (convert_eol (const QWidget*, QsciScintilla::EolMode)));
2523 
2524     connect (this, SIGNAL (fetab_goto_line (const QWidget*, int)),
2525              f, SLOT (goto_line (const QWidget*, int)));
2526 
2527     connect (this, SIGNAL (fetab_move_match_brace (const QWidget*, bool)),
2528              f, SLOT (move_match_brace (const QWidget*, bool)));
2529 
2530     connect (this, SIGNAL (fetab_completion (const QWidget*)),
2531              f, SLOT (show_auto_completion (const QWidget*)));
2532 
2533     connect (this, SIGNAL (fetab_set_focus (const QWidget*)),
2534              f, SLOT (set_focus (const QWidget*)));
2535 
2536     connect (this, SIGNAL (fetab_insert_debugger_pointer (const QWidget*, int)),
2537              f, SLOT (insert_debugger_pointer (const QWidget*, int)));
2538 
2539     connect (this, SIGNAL (fetab_delete_debugger_pointer (const QWidget*, int)),
2540              f, SLOT (delete_debugger_pointer (const QWidget*, int)));
2541 
2542     connect (f, SIGNAL (debug_quit_signal (void)),
2543              main_win (), SLOT (debug_quit (void)));
2544 
2545     connect (this, SIGNAL (fetab_do_breakpoint_marker (bool, const QWidget*,
2546                                                        int, const QString&)),
2547              f, SLOT (do_breakpoint_marker (bool, const QWidget*, int,
2548                                             const QString&)));
2549 
2550     // Any interpreter_event signal from a file_editor_tab_widget is
2551     // handled the same as for the parent main_window object.
2552 
2553     connect (f, SIGNAL (interpreter_event (const fcn_callback&)),
2554              this, SIGNAL (interpreter_event (const fcn_callback&)));
2555 
2556     connect (f, SIGNAL (interpreter_event (const meth_callback&)),
2557              this, SIGNAL (interpreter_event (const meth_callback&)));
2558 
2559     return f;
2560   }
2561 
add_file_editor_tab(file_editor_tab * f,const QString & fn,int index)2562   void file_editor::add_file_editor_tab (file_editor_tab *f, const QString& fn,
2563                                          int index)
2564   {
2565     if (index == -1)
2566       m_tab_widget->addTab (f, fn);
2567     else
2568       m_tab_widget->insertTab (index, f, fn);
2569 
2570     m_tab_widget->setCurrentWidget (f);
2571 
2572     check_actions ();
2573   }
2574 
mru_menu_update(void)2575   void file_editor::mru_menu_update (void)
2576   {
2577     int num_files = qMin (m_mru_files.size (), int (MaxMRUFiles));
2578 
2579     // configure and show active actions of mru-menu
2580     for (int i = 0; i < num_files; ++i)
2581       {
2582         QString text = QString ("&%1 %2").
2583           arg ((i+1) % int (MaxMRUFiles)).arg (m_mru_files.at (i));
2584         m_mru_file_actions[i]->setText (text);
2585 
2586         QStringList action_data;
2587         action_data << m_mru_files.at (i) << m_mru_files_encodings.at (i);
2588         m_mru_file_actions[i]->setData (action_data);
2589 
2590         m_mru_file_actions[i]->setVisible (true);
2591       }
2592 
2593     // hide unused mru-menu entries
2594     for (int j = num_files; j < MaxMRUFiles; ++j)
2595       m_mru_file_actions[j]->setVisible (false);
2596 
2597     // delete entries in string-list beyond MaxMRUFiles
2598     while (m_mru_files.size () > MaxMRUFiles)
2599       {
2600         m_mru_files.removeLast ();
2601         m_mru_files_encodings.removeLast ();
2602       }
2603 
2604     // save actual mru-list in settings
2605     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2606     gui_settings *settings = rmgr.get_settings ();
2607 
2608     settings->setValue (ed_mru_file_list.key,  m_mru_files);
2609     settings->setValue (ed_mru_file_encodings.key,  m_mru_files_encodings);
2610     settings->sync ();
2611   }
2612 
call_custom_editor(const QString & file_name,int line)2613   bool file_editor::call_custom_editor (const QString& file_name, int line)
2614   {
2615     // Check if the user wants to use a custom file editor.
2616     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2617     gui_settings *settings = rmgr.get_settings ();
2618 
2619     if (settings->value (global_use_custom_editor.key,
2620                          global_use_custom_editor.def).toBool ())
2621       {
2622         // use the external editor interface for handling the call
2623         emit request_open_file_external (file_name, line);
2624 
2625         if (line < 0 && ! file_name.isEmpty ())
2626           handle_mru_add_file (QFileInfo (file_name).canonicalFilePath (),
2627                                QString ());
2628 
2629         return true;
2630       }
2631 
2632     return false;
2633   }
2634 
toggle_preference(const gui_pref & preference)2635   void file_editor::toggle_preference (const gui_pref& preference)
2636   {
2637     resource_manager& rmgr = m_octave_qobj.get_resource_manager ();
2638     gui_settings *settings = rmgr.get_settings ();
2639 
2640     bool old = settings->value (preference).toBool ();
2641     settings->setValue (preference.key, ! old);
2642     notice_settings (settings);
2643   }
2644 
2645   // Function for closing the files in a removed directory
handle_dir_remove(const QString & old_name,const QString & new_name)2646   void file_editor::handle_dir_remove (const QString& old_name,
2647                                        const QString& new_name)
2648   {
2649     QDir old_dir (old_name);
2650     session_data f_data;
2651 
2652     std::list<file_editor_tab *> editor_tab_lst = m_tab_widget->tab_list ();
2653 
2654     for (auto editor_tab : editor_tab_lst)
2655       {
2656         QString file_name = editor_tab->file_name ();
2657 
2658         if (file_name.isEmpty ())
2659           continue;   // Nothing to do, no valid file name
2660 
2661         // Get abs. file path and its path relative to the removed directory
2662         QString rel_path_to_file = old_dir.relativeFilePath (file_name);
2663         QString abs_path_to_file = old_dir.absoluteFilePath (file_name);
2664 
2665         // Test whether the file is located within the directory that will
2666         // be removed.  For this, two conditions must be met:
2667         // 1. The path of the file rel. to the dir is not equal to the
2668         //    its absolute one.
2669         //    If both are equal, then there is no relative path and removed
2670         //    directory and file are on different drives (e.g. on windows)
2671         // 2. The (real) relative path does not start with "../", i.e.,
2672         //    the file can be reached from the directory by descending only
2673         if ((rel_path_to_file != abs_path_to_file)
2674             && (rel_path_to_file.left (3) != QString ("../")))
2675           {
2676             // The currently considered file is included in the
2677             // removed/renamed diectory: Delete it.
2678             m_no_focus = true;  // Remember for not focussing editor
2679 
2680             if (editor_tab)
2681               {
2682                 // Get index and line
2683                 int l, c;
2684                 editor_tab->qsci_edit_area ()->getCursorPosition (&l, &c);
2685                 f_data.line = l + 1;
2686                 f_data.index = m_tab_widget->indexOf (editor_tab);
2687                 // Close
2688                 editor_tab->file_has_changed (QString (), true);
2689               }
2690             m_no_focus = false;  // Back to normal
2691 
2692             // Store file for possible later reload
2693             f_data.file_name = file_name;
2694 
2695             // Add the new file path and the encoding for later reloading
2696             // if new_name is given
2697             if (! new_name.isEmpty ())
2698               {
2699                 QDir new_dir (new_name);
2700                 QString append_to_new_dir;
2701                 if (new_dir.exists ())
2702                   {
2703                     // The new directory already exists (movefile was used).
2704                     // This means, we have to add the name (not the path)
2705                     // of the old dir and the relative path to the file
2706                     // to new dir.
2707                     append_to_new_dir
2708                       = old_dir.dirName () + "/" + rel_path_to_file;
2709                   }
2710                 else
2711                   append_to_new_dir = rel_path_to_file;
2712 
2713                 f_data.new_file_name
2714                   = new_dir.absoluteFilePath (append_to_new_dir);
2715               }
2716             else
2717               f_data.new_file_name = ""; // no new name, just removing this file
2718 
2719             f_data.encoding = editor_tab->encoding (); // store the encoding
2720 
2721             // Store data in list for later reloading
2722             m_tmp_closed_files << f_data;
2723           }
2724       }
2725   }
2726 
editor_tab_has_focus(void)2727   bool file_editor::editor_tab_has_focus (void)
2728   {
2729     QWidget *foc_w = focusWidget ();
2730     if (foc_w && foc_w->inherits ("octave::octave_qscintilla"))
2731       return true;
2732     return false;
2733   }
2734 
2735   // Check whether this file is already open in the editor.
find_tab_widget(const QString & file)2736   file_editor_tab * file_editor::find_tab_widget (const QString& file)
2737   {
2738     std::string std_file = file.toStdString ();
2739 
2740     std::list<file_editor_tab *> fe_tab_lst = m_tab_widget->tab_list ();
2741 
2742     for (auto fe_tab : fe_tab_lst)
2743       {
2744         QString tab_file = fe_tab->file_name ();
2745 
2746         // We check file == tab_file because
2747         //
2748         //   same_file ("", "")
2749         //
2750         // is false
2751 
2752         if (same_file (std_file, tab_file.toStdString ()) || file == tab_file)
2753           return fe_tab;
2754       }
2755 
2756     return nullptr;
2757   }
2758 
add_action(QMenu * menu,const QString & text,const char * member,QWidget * receiver)2759   QAction * file_editor::add_action (QMenu *menu, const QString& text,
2760                                      const char *member,
2761                                      QWidget *receiver)
2762   {
2763     return add_action (menu, QIcon (), text, member, receiver);
2764   }
2765 
add_action(QMenu * menu,const QIcon & icon,const QString & text,const char * member,QWidget * receiver)2766   QAction * file_editor::add_action (QMenu *menu, const QIcon& icon,
2767                                      const QString& text, const char *member,
2768                                      QWidget *receiver)
2769   {
2770     QAction *a;
2771     QWidget *r = this;
2772 
2773     if (receiver != nullptr)
2774       r = receiver;
2775 
2776     if (menu)
2777       a = menu->addAction (icon, text, r, member);
2778     else
2779       {
2780         a = new QAction (this);
2781         connect (a, SIGNAL (triggered ()), r, member);
2782       }
2783 
2784     addAction (a);  // important for shortcut context
2785     a->setShortcutContext (Qt::WidgetWithChildrenShortcut);
2786 
2787     return a;
2788   }
2789 
add_menu(QMenuBar * p,QString name)2790   QMenu* file_editor::add_menu (QMenuBar *p, QString name)
2791   {
2792     QMenu *menu = p->addMenu (name);
2793 
2794     QString base_name = name;  // get a copy
2795     // replace intended '&' ("&&") by a temp. string
2796     base_name.replace ("&&", "___octave_amp_replacement___");
2797     // remove single '&' (shortcut)
2798     base_name.remove ("&");
2799     // restore intended '&'
2800     base_name.replace ("___octave_amp_replacement___", "&&");
2801 
2802     // remember names with and without shortcut
2803     m_hash_menu_text[menu] = QStringList () << name << base_name;
2804 
2805     return menu;
2806   }
2807 }
2808 
2809 #endif
2810