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