comparison libgui/src/m-editor/file-editor.cc @ 31648:29d734430e5f stable

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