# HG changeset patch # User Daniel J Sebald # Date 1356294828 21600 # Node ID 424edeca3c66899136546a9b96ae091c1fac2087 # Parent 13d1e9bfa362fd931fe85addd2795bd5852f8085 Redo portions of file editor to use more signals/slots rather than casting. * file-editor-tab.cc, file-editor-tab.h (file_editor_tab::~file_editor_tab): Add. Delete lexer to prevent memory leak. Delete _edit_area to prevent memory leak. (file_editor_tab::conditional_close): Add. Simple slot that uses QWidget pointer as unique ID, not for function call. (file_editor_tab::file_name_query): Add. Simple slot that signals file name to whomever is connected. (file_editor_tab::find, file_editor_tab : public QWidget): Removed use of exec() and keep a pointer to the find_dialog as a member. Toggle hide()/show() via a connected slot to toggle visibility as desired. * file-editor.cc, file-editor.h, file-editor-tab.cc file-editor-tab.h (file_editor : public file_editor_interface, file_editor_tab : public QWidget, file_editor_tab::file_editor_tab, file_editor_tab::closeEvent, file_editor_tab::load_file, file_editor_tab::new_file, file_editor_tab::run_file): Remove _file_editor pointer member from file_editor_tab and rid file_editor::get_main_window from file_editor. There should be no need for such information about higher-level hierarchy inside lower-level objects. (file_editor::request_open_file, file_editor_tab::open_file): Move QFileDialog to file_editor::request_open_file and delete file_editor_tab::open_file since most of the remaining functionality is in file_editor_tab::load_file. (file_editor::active_editor_tab): Deleted. (file_editor::fetab_change_request, file_editor_tab::change_editor_state): Added to initiate a request for the editor tab to change focus. (file_editor_tab::editor_state_changed): Added arguments to pass the copy status and the directory path so that editor doesn't have to call functions for such information. (file_editor::handle_editor_state_changed): Add copying directory of the file_editor_tab to the current editing directory. (file_editor::check_conflict_save, file_editor_tab::editor_check_conflict_save, file_editor_tab::save_file_as, file_editor_tab::handle_save_file_as_answer): Moved a portion of the save-file-as dialog mechanism to the file_editor where all file names can be obtained to check for conflict with already open files. The new signal indicates to the editor that a name check be done, and in turn the editor signals the tab to save the file. * main-window.cc, file-editor.cc, file-editor.h, file-editor-interface.h (file_editor::terminal, file_editor : public file_editor_interface, file_editor_interface : public QDockWidget): Since file_editor_tab no longer looks up to main_window, remove _main_window and _terminal from file_editor and file_editor_interface, as well as file_editor::terminal. * file-editor-tab.cc (file_editor_tab::file_has_changed): Make the dialog boxes non-modal and use slots to handle user answers. (file_editor_tab::closeEvent): Remove portion that accesses upper hierarchy member functions, can find better approaches. (file_editor_tab::file_editor_tab): Make there no parent for QsciScintilla so that window modality can be set to individual editor. * file-editor-tab.cc, file-editor.cc (file_editor_tab::load_file): Use show() member rather than exec() and set modality to window so that rest of application may function. Return a QString with message rather than a boolean. * file-editor-tab.cc, (file_editor_tab::file_has_changed): Remove static variable alreadyAsking. Multiple file_editor_tabs are using this code so do not want to block recognition of multiple file having changed on disk (bug #37406). Instead, simply stop tracking via the file watcher. (file_editor_tab::save_file, file_editor_tab::save_file_as, file_editor_tab::handle_save_file_as_answer, file_editor_tab::handle_save_file_as_answer_close): Added a remove_on_success variable. Changed the QFileDialog to WindowModal and created slots to handle file selected signal and finished signal. Signal/slot connects vary based upon remove_on_success setting. (file_editor_tab::check_file_modified): Changed the QFileDialog to NonModal and attach some slots. Editor tab can't be parent in case deleted, so use read-only state of the editor area. * file-editor-tab.h (file_editor_tab : public QWidget): New signals for file_editor for tab and file name management. (file_editor_tab::get_file_name): Delete. * file-editor.h (file_editor : public file_editor_interface): Make QStringList sessionFileNames a member of file_editor so that it can retain data between file_editor_tab signals. Also can be used for checking precense of filenames and prevent opening multiple copies (bug #36869) Added signals for file editor tabs--settings_changed, fetab_close_request, and query_file_name. Three new slots for tab and file name management. * file-editor-interface.h, file-editor.h (file_editor_interface::add_file_editor_tab, file_editor::add_file_editor_tab): Made the text name for the tab an input variable. * file-editor.cc (file_editor::~file_editor): Replace dynamic_cast with simple signal querying all file editor tabs for file names which end up in savedSessionTabs. (file_editor::handle_file_name_changed): Dynamic cast not necessary since QObject and QWidget are compatible. (file_editor::handle_tab_close_request): Replace dynamic_cast with signal to request file_editor_tab with associated tabID tag should close. (file_editor::handle_tab_remove_request): Rename of handle_tab_close_request. Instead of dynamic cast, loop through pointers comparing QWidget* to QObject*, if same tag remove tab via index and also delete which fixes a memory leak. (file_editor::handle_add_filename_to_list): Simple slot that uses append() of the list member functions. (file_editor::notice_settings): Rather than dynamic cast, emit signal for the file_editor_tabs. (file_editor::add_file_editor_tab): New variety of connections for improved flow. (file_editor::request_open_file): Given error messages are made WindowModal, the tab shouldn't be delete if file open is not successful. The file_editor_tab takes care of that. (file_editor::request_open_file): Added check and message box for the requested file already open in editor. For the non-interactive overloaded version, open a message dialog box to tell the user file not found, e.g., could not find file in the settings when launched. (file_editor::request_open_file): Inquire file names and update list before checking for existence of files. Supply empty title to editor tab then have file_editor_tab update name. * file-editor-tab.h, file-editor-tab.cc, file-editor.cc (file_editor_tab::run_file): New signal process_octave_code. (file_editor::add_file_editor_tab): Connect signal process_octave_code to file_editor's parent's slot handle_command_double_clicked. diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/m-editor/file-editor-interface.h --- a/libgui/src/m-editor/file-editor-interface.h Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/m-editor/file-editor-interface.h Sun Dec 23 14:33:48 2012 -0600 @@ -27,20 +27,15 @@ #include #include -class QTerminal; -class main_window; - class file_editor_interface : public QDockWidget { Q_OBJECT public: - file_editor_interface (QTerminal *terminal, main_window *mainWindow) - : QDockWidget ((QWidget*)mainWindow) // QDockWidget constructor is explicit, hence the cast. + file_editor_interface (QWidget *p) + : QDockWidget (p) { setObjectName ("FileEditor"); - _terminal = terminal; - _main_window = mainWindow; connect (this, SIGNAL (visibilityChanged (bool)), this, SLOT (handle_visibility_changed (bool))); @@ -57,15 +52,12 @@ public slots: virtual void request_new_file () = 0; virtual void request_open_file () = 0; - virtual void request_open_file (const QString& fileName, bool silent = false) = 0; + virtual void request_open_file (const QString& fileName) = 0; signals: void active_changed (bool active); protected: - QTerminal* _terminal; - main_window* _main_window; - void closeEvent (QCloseEvent *e) { emit active_changed (false); diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/m-editor/file-editor-tab.cc --- a/libgui/src/m-editor/file-editor-tab.cc Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/m-editor/file-editor-tab.cc Sun Dec 23 14:33:48 2012 -0600 @@ -42,19 +42,26 @@ #include "file-editor-tab.h" #include "file-editor.h" -#include "find-dialog.h" #include "octave-link.h" #include "debug.h" #include "oct-env.h" -file_editor_tab::file_editor_tab(file_editor *fileEditor) - : QWidget ((QWidget*)fileEditor) +// Make parent null for the file editor tab so that warning +// WindowModal messages don't affect grandparents. +file_editor_tab::file_editor_tab (QString directory) { - _file_editor = fileEditor; - _file_name = ""; + // Make sure there is a slash at the end of the directory name + // for identification when saved later. + if (directory.count () && directory.at (directory.count () - 1) != '/') + directory.append ("/"); + _file_name = directory; _edit_area = new QsciScintilla (this); + // Leave the find dialog box out of memory until requested. + _find_dialog = 0; + _find_dialog_is_visible = false; + // symbols _edit_area->setMarginType (1, QsciScintilla::SymbolMargin); _edit_area->setMarginSensitivity (1, true); @@ -106,57 +113,60 @@ connect (&_file_system_watcher, SIGNAL (fileChanged (QString)), this, SLOT (file_has_changed (QString))); - _file_name = ""; - notice_settings (); } -bool -file_editor_tab::copy_available () +file_editor_tab::~file_editor_tab () { - return _copy_available; + // Destroy items attached to _edit_area. + QsciLexer *lexer = _edit_area->lexer (); + if (lexer) + { + delete lexer; + _edit_area->setLexer(0); + } + if (_find_dialog) + { + delete _find_dialog; + _find_dialog = 0; + } + + // Destroy _edit_area. + delete _edit_area; } void file_editor_tab::closeEvent (QCloseEvent *e) { - if (_file_editor->get_main_window ()->is_closing ()) + // ignore close event if file is not saved and user cancels + // closing this window + if (check_file_modified ("Close File", + QMessageBox::Cancel) == QMessageBox::Cancel) { - // close whole application: save file or not if modified - check_file_modified ("Closing Octave", 0); // no cancel possible - e->accept (); + e->ignore (); } else { - // ignore close event if file is not saved and user cancels - // closing this window - if (check_file_modified ("Close File", - QMessageBox::Cancel) == QMessageBox::Cancel) - { - e->ignore (); - } - else - { - e->accept(); - } + e->accept(); } } void file_editor_tab::set_file_name (const QString& fileName) { - if (fileName != UNNAMED_FILE) - { - // update tracked file if wie really hae a file on disk - QStringList trackedFiles = _file_system_watcher.files (); - if (!trackedFiles.isEmpty ()) - _file_system_watcher.removePath (_file_name); - _file_system_watcher.addPath (fileName); - } + // update tracked file if we really have a file on disk + QStringList trackedFiles = _file_system_watcher.files (); + if (!trackedFiles.isEmpty ()) + _file_system_watcher.removePath (_file_name); + if (!fileName.isEmpty ()) + _file_system_watcher.addPath (fileName); _file_name = fileName; // update lexer after _file_name change update_lexer (); + + // update the file editor with current editing directory + emit editor_state_changed (_copy_available, QDir::cleanPath (_file_name)); } void @@ -191,7 +201,7 @@ void file_editor_tab::update_lexer () { - QsciLexer *lexer = _edit_area->lexer (); + QsciLexer *lexer = _edit_area->lexer (); delete lexer; if (_file_name.endsWith (".m") || _file_name.endsWith (".M")) @@ -246,151 +256,114 @@ } void -file_editor_tab::request_add_breakpoint (int line) +file_editor_tab::undo (const QWidget* ID) { - QFileInfo file_info (_file_name); - QString path = file_info.absolutePath (); - QString function_name = file_info.fileName (); + if (ID != this) + return; + + _edit_area->undo (); +} + +void +file_editor_tab::redo (const QWidget* ID) +{ + if (ID != this) + return; + + _edit_area->redo (); +} - // We have to cut off the suffix, because octave appends it. - function_name.chop (file_info.suffix ().length () + 1); +void +file_editor_tab::copy (const QWidget* ID) +{ + if (ID != this) + return; + + _edit_area->copy (); +} - bp_info info (path, function_name, line); +void +file_editor_tab::cut (const QWidget* ID) +{ + if (ID != this) + return; - octave_link::post_event - (this, &file_editor_tab::add_breakpoint_callback, info); + _edit_area->cut (); +} + +void +file_editor_tab::paste (const QWidget* ID) +{ + if (ID != this) + return; + + _edit_area->paste (); } void -file_editor_tab::request_remove_breakpoint (int line) +file_editor_tab::save_file (const QWidget* ID) +{ + if (ID != this) + return; + + save_file (_file_name); +} +void + +file_editor_tab::save_file (const QWidget* ID, const QString& fileName, bool remove_on_success) +{ + if (ID != this) + return; + + save_file (fileName, remove_on_success); +} + +void +file_editor_tab::save_file_as (const QWidget* ID) { + if (ID != this) + return; + + save_file_as (); +} + +void +file_editor_tab::run_file_callback (void) +{ + // Maybe someday we will do something here? +} + +void +file_editor_tab::run_file (const QWidget* ID) +{ + if (ID != this) + return; + + if (_edit_area->isModified ()) + save_file (_file_name); + QFileInfo file_info (_file_name); QString path = file_info.absolutePath (); + QString current_path + = QString::fromStdString (octave_link::last_working_directory ()); QString function_name = file_info.fileName (); // We have to cut off the suffix, because octave appends it. function_name.chop (file_info.suffix ().length () + 1); - - bp_info info (path, function_name, line); - - octave_link::post_event - (this, &file_editor_tab::remove_breakpoint_callback, info); -} - -void -file_editor_tab::comment_selected_text () -{ - do_comment_selected_text (true); -} - -void -file_editor_tab::uncomment_selected_text () -{ - do_comment_selected_text (false); -} - -void -file_editor_tab::do_comment_selected_text (bool comment) -{ - if ( _edit_area->hasSelectedText() ) - { - int lineFrom, lineTo, colFrom, colTo, i; - _edit_area->getSelection (&lineFrom,&colFrom,&lineTo,&colTo); - if ( colTo == 0 ) // the beginning of last line is not selected - lineTo--; // stop at line above - _edit_area->beginUndoAction (); - for ( i=lineFrom; i<=lineTo; i++ ) - { - if ( comment ) - _edit_area->insertAt("%",i,0); - else - { - QString line(_edit_area->text(i)); - if ( line.startsWith("%") ) - { - _edit_area->setSelection(i,0,i,1); - _edit_area->removeSelectedText(); - } - } - } - _edit_area->endUndoAction (); - } -} - -void -file_editor_tab::find () -{ - find_dialog dialog (_edit_area); - dialog.exec (); + emit process_octave_code (QString ("cd \'%1\'\n%2\n") + .arg(path).arg (function_name)); + + // TODO: Sending a run event crashes for long scripts. Find out why. + // octave_link::post_event + // (this, &file_editor_tab::run_file_callback, _file_name.toStdString ())); } void -file_editor_tab::update_window_title (bool modified) -{ - QString title(_file_name); - if ( !_long_title ) - { - QFileInfo file(_file_name); - title = file.fileName(); - } - - if ( modified ) - { - emit file_name_changed (title.prepend("* ")); - } - else - emit file_name_changed (title); -} - -void -file_editor_tab::handle_copy_available(bool enableCopy) -{ - _copy_available = enableCopy; - emit editor_state_changed (); -} - -int -file_editor_tab::check_file_modified (const QString& msg, int cancelButton) +file_editor_tab::toggle_bookmark (const QWidget* ID) { - int decision = QMessageBox::Yes; - if (_edit_area->isModified ()) - { - // file is modified but not saved, ask user what to do - decision = QMessageBox::warning (this, - msg, - tr ("The file %1\n" - "has been modified. Do you want to save the changes?"). - arg (_file_name), - QMessageBox::Save, - QMessageBox::Discard, cancelButton ); - if (decision == QMessageBox::Save) - { - save_file (); - if (_edit_area->isModified ()) - { - // If the user attempted to save the file, but it's still - // modified, then probably something went wrong, so return - // cancel for cancel this operation or try to save files - // as if cancel not possible - if ( cancelButton ) - return (QMessageBox::Cancel); - else - save_file_as (); - } - } - } - return (decision); -} + if (ID != this) + return; -void -file_editor_tab::remove_bookmark () -{ - _edit_area->markerDeleteAll (bookmark); -} - -void -file_editor_tab::toggle_bookmark () -{ int line, cur; _edit_area->getCursorPosition (&line,&cur); if ( _edit_area->markersAtLine (line) && (1 << bookmark) ) @@ -400,8 +373,11 @@ } void -file_editor_tab::next_bookmark() +file_editor_tab::next_bookmark (const QWidget* ID) { + if (ID != this) + return; + int line, cur, nextline; _edit_area->getCursorPosition (&line, &cur); if ( _edit_area->markersAtLine (line) && (1 << bookmark) ) @@ -411,8 +387,11 @@ } void -file_editor_tab::previous_bookmark () +file_editor_tab::previous_bookmark (const QWidget* ID) { + if (ID != this) + return; + int line, cur, prevline; _edit_area->getCursorPosition (&line, &cur); if ( _edit_area->markersAtLine (line) && (1 << bookmark) ) @@ -422,362 +401,12 @@ } void -file_editor_tab::remove_all_breakpoints () -{ - QFileInfo file_info (_file_name); - QString path = file_info.absolutePath (); - QString function_name = file_info.fileName (); - - // We have to cut off the suffix, because octave appends it. - function_name.chop (file_info.suffix ().length () + 1); - - bp_info info (path, function_name, 0); - - octave_link::post_event - (this, &file_editor_tab::remove_all_breakpoints_callback, info); -} - -void -file_editor_tab::toggle_breakpoint () -{ - int line, cur; - _edit_area->getCursorPosition (&line, &cur); - if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) - request_remove_breakpoint (line); - else - request_add_breakpoint (line); -} - -void -file_editor_tab::next_breakpoint () -{ - int line, cur, nextline; - _edit_area->getCursorPosition (&line, &cur); - if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) - line++; // we have a breakpoint here, so start search from next line - nextline = _edit_area->markerFindNext (line, (1 << breakpoint)); - _edit_area->setCursorPosition (nextline, 0); -} - -void -file_editor_tab::previous_breakpoint () -{ - int line, cur, prevline; - _edit_area->getCursorPosition (&line, &cur); - if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) - line--; // we have a breakpoint here, so start search from prev line - prevline = _edit_area->markerFindPrevious (line, (1 << breakpoint)); - _edit_area->setCursorPosition (prevline, 0); -} - -void -file_editor_tab::cut () -{ - _edit_area->cut (); -} - -void -file_editor_tab::copy () -{ - _edit_area->copy (); -} - -void -file_editor_tab::paste () -{ - _edit_area->paste (); -} - -void -file_editor_tab::undo () -{ - _edit_area->undo (); -} - -void -file_editor_tab::redo () -{ - _edit_area->redo (); -} - -void -file_editor_tab::set_debugger_position (int line) +file_editor_tab::remove_bookmark (const QWidget* ID) { - _edit_area->markerDeleteAll (debugger_position); - if (line > 0) - { - _edit_area->markerAdd (line, debugger_position); - } -} - -void -file_editor_tab::set_modified (bool modified) -{ - _edit_area->setModified (modified); -} - -bool -file_editor_tab::open_file (const QString& dir) -{ - QString openFileName; - QFileDialog fileDialog(this); - fileDialog.setNameFilter(SAVE_FILE_FILTER); - fileDialog.setAcceptMode(QFileDialog::AcceptOpen); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setDirectory(dir); - if (fileDialog.exec () == QDialog::Accepted) - { - openFileName = fileDialog.selectedFiles().at(0); - if (openFileName.isEmpty ()) - return false; - - return load_file(openFileName); - } - else - { - return false; - } -} - -bool -file_editor_tab::load_file(const QString& fileName, bool silent) -{ - if (!_file_editor->isVisible ()) - { - _file_editor->show (); - } - - QFile file (fileName); - if (!file.open (QFile::ReadOnly)) - { - if (silent==false) - QMessageBox::warning (this, tr ("Octave Editor"), - tr ("Could not open file %1 for read:\n%2.").arg (fileName). - arg (file.errorString ())); - return false; - } - - QTextStream in (&file); - QApplication::setOverrideCursor (Qt::WaitCursor); - _edit_area->setText (in.readAll ()); - QApplication::restoreOverrideCursor (); - - set_file_name (fileName); - update_window_title (false); // window title (no modification) - _edit_area->setModified (false); // loaded file is not modified yet - - return true; -} - -void -file_editor_tab::new_file () -{ - if (!_file_editor->isVisible ()) - { - _file_editor->show (); - } - - set_file_name (UNNAMED_FILE); - update_window_title (false); // window title (no modification) - _edit_area->setText (""); - _edit_area->setModified (false); // new file is not modified yet -} - -bool file_editor_tab::save_file() -{ - return save_file (_file_name); -} - -bool -file_editor_tab::save_file (const QString& saveFileName) -{ - // it is a new file with the name "" -> call saveFielAs - if (saveFileName == UNNAMED_FILE || saveFileName.isEmpty ()) - { - return save_file_as(); - } + if (ID != this) + return; - // remove the file to save from the tracker since we will change it on disk now - QStringList watched_files = _file_system_watcher.files(); - if (!watched_files.isEmpty ()) - _file_system_watcher.removePath(saveFileName); - - // open the file for writing - QFile file (saveFileName); - if (!file.open (QFile::WriteOnly)) - { - QMessageBox::warning (this, tr ("Octave Editor"), - tr ("Could not open file %1 for write:\n%2."). - arg (saveFileName).arg (file.errorString ())); - return false; - } - - // save the contents into the file - QTextStream out (&file); - QApplication::setOverrideCursor (Qt::WaitCursor); - out << _edit_area->text (); - QApplication::restoreOverrideCursor (); - file.close(); - - // save file name after closing file otherwise tracker will notice file change - set_file_name (saveFileName); - // set the window title to actual file name (not modified) - update_window_title (false); - // files is save -> not modified - _edit_area->setModified (false); - - return true; -} - -bool -file_editor_tab::save_file_as () -{ - QString saveFileName(_file_name); - QFileDialog fileDialog(this); - if (saveFileName == UNNAMED_FILE || saveFileName.isEmpty ()) - { - QString directory = QString::fromStdString - (octave_link::last_working_directory ()); - - if (directory.isEmpty ()) - { - directory = QDir::homePath (); - } - - fileDialog.setDirectory (directory); - } - else - { - fileDialog.selectFile (saveFileName); - } - fileDialog.setNameFilter (SAVE_FILE_FILTER); - fileDialog.setDefaultSuffix ("m"); - fileDialog.setAcceptMode (QFileDialog::AcceptSave); - fileDialog.setViewMode (QFileDialog::Detail); - - if (fileDialog.exec ()) - { - saveFileName = fileDialog.selectedFiles ().at (0); - if (saveFileName.isEmpty ()) - return false; - - return save_file (saveFileName); - } - - return false; -} - -void -file_editor_tab::run_file () -{ - if (_edit_area->isModified ()) - save_file(_file_name); - - QFileInfo file_info (_file_name); - QString path = file_info.absolutePath (); - QString current_path - = QString::fromStdString (octave_link::last_working_directory ()); - QString function_name = file_info.fileName (); - - // We have to cut off the suffix, because octave appends it. - function_name.chop (file_info.suffix ().length () + 1); - _file_editor->terminal ()->sendText (QString ("cd \'%1\'\n%2\n") - .arg(path).arg (function_name)); - // TODO: Sending a run event crashes for long scripts. Find out why. - // octave_link::post_event - // (this, &file_editor_tab::run_file_callback, _file_name.toStdString ())); -} - -void -file_editor_tab::file_has_changed (const QString&) -{ - if (QFile::exists (_file_name)) - { - // Prevent popping up multiple message boxes when the file has - // been changed multiple times. - static bool alreadyAsking = false; - if (!alreadyAsking) - { - alreadyAsking = true; - - int decision = - QMessageBox::warning (this, tr ("Octave Editor"), - tr ("It seems that \'%1\' has been modified by another application. Do you want to reload it?"). - arg (_file_name), QMessageBox::Yes, - QMessageBox::No); - - if (decision == QMessageBox::Yes) - { - load_file (_file_name); - } - - alreadyAsking = false; - } - } - else - { - int decision = - QMessageBox::warning (this, tr ("Octave Editor"), - tr ("It seems that \'%1\' has been deleted or renamed. Do you want to save it now?"). - arg (_file_name), QMessageBox::Save, - QMessageBox::Close); - if (decision == QMessageBox::Save) - { - if (!save_file_as ()) - { - set_file_name (UNNAMED_FILE); - update_window_title (true); // window title (no modification) - set_modified (true); - } - } - else - { - emit close_request (); - } - } -} - -void -file_editor_tab::notice_settings () -{ - QSettings *settings = resource_manager::get_settings (); - - if (settings==NULL) - return; // this shouldn't happen! - - _edit_area->setCaretLineVisible(settings->value ("editor/highlightCurrentLine",true).toBool ()); - - if (settings->value ("editor/codeCompletion",true).toBool ()) - _edit_area->setAutoCompletionThreshold (1); - else - _edit_area->setAutoCompletionThreshold (-1); - - QFont font( settings->value ("editor/fontName","Courier").toString () , - settings->value ("editor/fontSize",10).toInt () ); - if (settings->value ("editor/showLineNumbers",true).toBool ()) - { - _edit_area->setMarginLineNumbers (2, true); - _edit_area->setMarginsFont( font ); - QFontMetrics metrics( font ); - _edit_area->setMarginWidth(2, metrics.width("9999")); - } - else - { - _edit_area->setMarginLineNumbers (2, false); - _edit_area->setMarginWidth(2, 0); - } - - update_lexer (); - - _long_title = settings->value ("editor/longWindowTitle",false).toBool (); - - update_window_title (false); -} - -void -file_editor_tab::run_file_callback (void) -{ - // Maybe someday we will do something here? + _edit_area->markerDeleteAll (bookmark); } void @@ -831,3 +460,636 @@ if (intmap.size() > 0) _edit_area->markerDeleteAll (breakpoint); } + +void +file_editor_tab::request_add_breakpoint (int line) +{ + QFileInfo file_info (_file_name); + QString path = file_info.absolutePath (); + QString function_name = file_info.fileName (); + + // We have to cut off the suffix, because octave appends it. + function_name.chop (file_info.suffix ().length () + 1); + + bp_info info (path, function_name, line); + + octave_link::post_event + (this, &file_editor_tab::add_breakpoint_callback, info); +} + +void +file_editor_tab::request_remove_breakpoint (int line) +{ + QFileInfo file_info (_file_name); + QString path = file_info.absolutePath (); + QString function_name = file_info.fileName (); + + // We have to cut off the suffix, because octave appends it. + function_name.chop (file_info.suffix ().length () + 1); + + bp_info info (path, function_name, line); + + octave_link::post_event + (this, &file_editor_tab::remove_breakpoint_callback, info); +} + +void +file_editor_tab::toggle_breakpoint (const QWidget* ID) +{ + if (ID != this) + return; + + int line, cur; + _edit_area->getCursorPosition (&line, &cur); + if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) + request_remove_breakpoint (line); + else + request_add_breakpoint (line); +} + +void +file_editor_tab::next_breakpoint (const QWidget* ID) +{ + if (ID != this) + return; + + int line, cur, nextline; + _edit_area->getCursorPosition (&line, &cur); + if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) + line++; // we have a breakpoint here, so start search from next line + nextline = _edit_area->markerFindNext (line, (1 << breakpoint)); + _edit_area->setCursorPosition (nextline, 0); +} + +void +file_editor_tab::previous_breakpoint (const QWidget* ID) +{ + if (ID != this) + return; + + int line, cur, prevline; + _edit_area->getCursorPosition (&line, &cur); + if ( _edit_area->markersAtLine (line) && (1 << breakpoint) ) + line--; // we have a breakpoint here, so start search from prev line + prevline = _edit_area->markerFindPrevious (line, (1 << breakpoint)); + _edit_area->setCursorPosition (prevline, 0); +} + +void +file_editor_tab::remove_all_breakpoints (const QWidget* ID) +{ + if (ID != this) + return; + + QFileInfo file_info (_file_name); + QString path = file_info.absolutePath (); + QString function_name = file_info.fileName (); + + // We have to cut off the suffix, because octave appends it. + function_name.chop (file_info.suffix ().length () + 1); + + bp_info info (path, function_name, 0); + + octave_link::post_event + (this, &file_editor_tab::remove_all_breakpoints_callback, info); +} + +void +file_editor_tab::comment_selected_text (const QWidget* ID) +{ + if (ID != this) + return; + + do_comment_selected_text (true); +} + +void +file_editor_tab::uncomment_selected_text (const QWidget* ID) +{ + if (ID != this) + return; + + do_comment_selected_text (false); +} + +void +file_editor_tab::handle_find_dialog_finished (int) +{ + // Find dialog is going to hide. Save location of window for + // when it is reshown. + _find_dialog_geometry = _find_dialog->geometry (); + _find_dialog_is_visible = false; +} + +void +file_editor_tab::find (const QWidget* ID) +{ + if (ID != this) + return; + + // The find_dialog feature doesn't need a slot for return info. + // Rather than Qt::DeleteOnClose, let the find feature hang about + // in case it contains useful information like previous searches + // and so on. Perhaps one find dialog for the whole editor is + // better, but individual find dialogs has the nice feature of + // retaining position per file editor tabs, which can be undocked. + + if (!_find_dialog) + { + _find_dialog = new find_dialog (_edit_area); + connect (_find_dialog, SIGNAL (finished (int)), + this, SLOT (handle_find_dialog_finished (int))); + _find_dialog->setWindowModality (Qt::NonModal); + _find_dialog_geometry = _find_dialog->geometry (); + } + + if (!_find_dialog->isVisible ()) + { + _find_dialog->setGeometry (_find_dialog_geometry); + _find_dialog->show (); + _find_dialog_is_visible = true; + } + + _find_dialog->activateWindow (); +} + +void +file_editor_tab::do_comment_selected_text (bool comment) +{ + if ( _edit_area->hasSelectedText() ) + { + int lineFrom, lineTo, colFrom, colTo, i; + _edit_area->getSelection (&lineFrom,&colFrom,&lineTo,&colTo); + if ( colTo == 0 ) // the beginning of last line is not selected + lineTo--; // stop at line above + _edit_area->beginUndoAction (); + for ( i=lineFrom; i<=lineTo; i++ ) + { + if ( comment ) + _edit_area->insertAt("%",i,0); + else + { + QString line(_edit_area->text(i)); + if ( line.startsWith("%") ) + { + _edit_area->setSelection(i,0,i,1); + _edit_area->removeSelectedText(); + } + } + } + _edit_area->endUndoAction (); + } +} + +void +file_editor_tab::update_window_title (bool modified) +{ + QString title (""); + if (_file_name.isEmpty () || _file_name.at (_file_name.count () - 1) == '/') + title = UNNAMED_FILE; + else + title = _file_name; + if ( !_long_title ) + { + QFileInfo file(_file_name); + title = file.fileName(); + } + + if ( modified ) + { + emit file_name_changed (title.prepend("* ")); + } + else + emit file_name_changed (title); +} + +void +file_editor_tab::handle_copy_available(bool enableCopy) +{ + _copy_available = enableCopy; + emit editor_state_changed (_copy_available, QDir::cleanPath (_file_name)); +} + +int +file_editor_tab::check_file_modified (const QString& msg, int cancelButton) +{ + int decision = QMessageBox::Yes; + if (_edit_area->isModified ()) + { + // File is modified but not saved, ask user what to do. The file + // editor tab can't be made parent because it may be deleted depending + // upon the response. Instead, change the _edit_area to read only. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Warning, tr ("Octave Editor"), + tr ("The file \'%1\' has been modified. Do you want to save the changes?"). + arg (_file_name), QMessageBox::Yes | QMessageBox::No, 0); + _edit_area->setReadOnly (true); + connect (msgBox, SIGNAL (finished (int)), + this, SLOT (handle_file_modified_answer (int))); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + return (QMessageBox::Cancel); + } + else + { + // Nothing was modified, just remove from editor. + emit tab_remove_request (); + } + + return (decision); +} + +void +file_editor_tab::handle_file_modified_answer (int decision) +{ + if (decision == QMessageBox::Yes) + { + // Save file, then remove from editor. + save_file (_file_name, true); + } + else if (decision == QMessageBox::No) + { + // User doesn't want to save, just remove from editor. + emit tab_remove_request (); + } + else + { + // User canceled, allow editing again. + _edit_area->setReadOnly (false); + } +} + +void +file_editor_tab::set_modified (bool modified) +{ + _edit_area->setModified (modified); +} + +QString +file_editor_tab::load_file(const QString& fileName) +{ + QFile file (fileName); + if (!file.open (QFile::ReadOnly)) + { + return file.errorString (); + } + + QTextStream in (&file); + QApplication::setOverrideCursor (Qt::WaitCursor); + _edit_area->setText (in.readAll ()); + QApplication::restoreOverrideCursor (); + + set_file_name (fileName); + update_window_title (false); // window title (no modification) + _edit_area->setModified (false); // loaded file is not modified yet + + return QString (); +} + +void +file_editor_tab::new_file () +{ + update_window_title (false); // window title (no modification) + _edit_area->setText (""); + _edit_area->setModified (false); // new file is not modified yet +} + +void +file_editor_tab::save_file (const QString& saveFileName, bool remove_on_success) +{ + // If it is a new file with no name, signal that saveFileAs + // should be performed. + if (saveFileName.isEmpty () || saveFileName.at (saveFileName.count () - 1) == '/') + { + save_file_as (remove_on_success); + return; + } + + // stop watching file + QStringList trackedFiles = _file_system_watcher.files (); + if (!trackedFiles.isEmpty ()) + _file_system_watcher.removePath (saveFileName); + + // open the file for writing + QFile file (saveFileName); + if (!file.open (QIODevice::WriteOnly)) + { + // Unsuccessful, begin watching file again if it was being + // watched previously. + if (trackedFiles.contains (saveFileName)) + _file_system_watcher.addPath (saveFileName); + + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("Could not open file %1 for write:\n%2."). + arg (saveFileName).arg (file.errorString ()), + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + return; + } + + // save the contents into the file + QTextStream out (&file); + QApplication::setOverrideCursor (Qt::WaitCursor); + out << _edit_area->text (); + QApplication::restoreOverrideCursor (); + file.close(); + + // save file name after closing file as set_file_name starts watching again + set_file_name (saveFileName); + // set the window title to actual file name (not modified) + update_window_title (false); + // files is save -> not modified + _edit_area->setModified (false); + + if (remove_on_success) + { + emit tab_remove_request (); + return; // Don't touch member variables after removal + } +} + +void +file_editor_tab::save_file_as (bool remove_on_success) +{ + // Simply put up the file chooser dialog box with a slot connection + // then return control to the system waiting for a file selection. + + // If the tab is removed in response to a QFileDialog signal, the tab + // can't be a parent. + QFileDialog* fileDialog; + if (remove_on_success) + { + // If tab is closed, "this" cannot be parent in which case modality + // has no effect. Disable editing instead. + _edit_area->setReadOnly (true); + fileDialog = new QFileDialog (); + } + else + fileDialog = new QFileDialog (this); + + if (!_file_name.isEmpty () && _file_name.at (_file_name.count () - 1) != '/') + { + fileDialog->selectFile (_file_name); + } + else + { + fileDialog->selectFile (""); + if (_file_name.isEmpty ()) + { + fileDialog->setDirectory (QDir::currentPath ()); + } + else + { + // The file name is actually the directory name from the + // constructor argument. + fileDialog->setDirectory (_file_name); + } + } + fileDialog->setNameFilter (SAVE_FILE_FILTER); + fileDialog->setDefaultSuffix ("m"); + fileDialog->setAcceptMode (QFileDialog::AcceptSave); + fileDialog->setViewMode (QFileDialog::Detail); + if (remove_on_success) + { + connect (fileDialog, SIGNAL (fileSelected (const QString&)), + this, SLOT (handle_save_file_as_answer_close (const QString&))); + connect (fileDialog, SIGNAL (rejected ()), + this, SLOT (handle_save_file_as_answer_cancel ())); + } + else + { + connect (fileDialog, SIGNAL (fileSelected (const QString&)), + this, SLOT (handle_save_file_as_answer (const QString&))); + } + fileDialog->setWindowModality (Qt::WindowModal); + fileDialog->setAttribute (Qt::WA_DeleteOnClose); + fileDialog->show (); +} + +void +file_editor_tab::message_duplicate_file_name (const QString& saveFileName) +{ + // Could overwrite the file here (and tell user the file was + // overwritten), but the user could have unintentionally + // selected the same name not intending to overwrite. + + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("File not saved! You've selected a file name\n\n %1\n\nwhich is the same as the current file name. Use ""Save"" to overwrite. (Could allow overwriting, with message, if that is what folks want.)"). + arg (saveFileName), + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); +} + +void +file_editor_tab::handle_save_file_as_answer (const QString& saveFileName) +{ + if (saveFileName == _file_name) + { + message_duplicate_file_name (saveFileName); + // Nothing done, allow editing again. + _edit_area->setReadOnly (false); + } + else + { + // Have editor check for conflict, do not delete tab after save. + emit editor_check_conflict_save (saveFileName, false); + } +} + +void +file_editor_tab::handle_save_file_as_answer_close (const QString& saveFileName) +{ + if (saveFileName == _file_name) + { + message_duplicate_file_name (saveFileName); + // Nothing done, allow editing again. + _edit_area->setReadOnly (false); + } + else + { + // Have editor check for conflict, delete tab after save. + emit editor_check_conflict_save (saveFileName, true); + } +} + +void +file_editor_tab::handle_save_file_as_answer_cancel () +{ + // User canceled, allow editing again. + _edit_area->setReadOnly (false); +} + +void +file_editor_tab::file_has_changed (const QString&) +{ + // Prevent popping up multiple message boxes when the file has + // been changed multiple times by temporarily removing from the + // file watcher. + QStringList trackedFiles = _file_system_watcher.files (); + if (!trackedFiles.isEmpty ()) + _file_system_watcher.removePath (_file_name); + + if (QFile::exists (_file_name)) + { + // Create a WindowModal message that blocks the edit area + // by making _edit_area parent. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Warning, tr ("Octave Editor"), + tr ("It seems that \'%1\' has been modified by another application. Do you want to reload it?"). + arg (_file_name), QMessageBox::Yes | QMessageBox::No, this); + connect (msgBox, SIGNAL (finished (int)), + this, SLOT (handle_file_reload_answer (int))); + msgBox->setWindowModality (Qt::WindowModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + } + else + { + // Create a WindowModal message that blocks the edit area + // by making _edit_area parent. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Warning, tr ("Octave Editor"), + tr ("It seems that \'%1\' has been deleted or renamed. Do you want to save it now?"). + arg (_file_name), QMessageBox::Save | QMessageBox::Close, this); + connect (msgBox, SIGNAL (finished (int)), + this, SLOT (handle_file_resave_answer (int))); + msgBox->setWindowModality (Qt::WindowModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + } +} + +void +file_editor_tab::notice_settings () +{ + QSettings *settings = resource_manager::get_settings (); + + if (settings==NULL) + return; // this shouldn't happen! + + _edit_area->setCaretLineVisible(settings->value ("editor/highlightCurrentLine",true).toBool ()); + + if (settings->value ("editor/codeCompletion",true).toBool ()) + _edit_area->setAutoCompletionThreshold (1); + else + _edit_area->setAutoCompletionThreshold (-1); + + QFont font( settings->value ("editor/fontName","Courier").toString () , + settings->value ("editor/fontSize",10).toInt () ); + if (settings->value ("editor/showLineNumbers",true).toBool ()) + { + _edit_area->setMarginLineNumbers (2, true); + _edit_area->setMarginsFont( font ); + QFontMetrics metrics( font ); + _edit_area->setMarginWidth(2, metrics.width("9999")); + } + else + { + _edit_area->setMarginLineNumbers (2, false); + _edit_area->setMarginWidth(2, 0); + } + + update_lexer (); + + _long_title = settings->value ("editor/longWindowTitle",false).toBool (); + + update_window_title (false); +} + +void +file_editor_tab::conditional_close (const QWidget* ID) +{ + if (ID != this) + return; + + close (); +} + +void +file_editor_tab::change_editor_state (const QWidget* ID) +{ + if (ID != this) + { + // Widget may be going out of focus. If so, record location. + if (_find_dialog) + { + if (_find_dialog->isVisible ()) + { + _find_dialog_geometry = _find_dialog->geometry (); + _find_dialog->hide (); + } + } + return; + } + + if (_find_dialog && _find_dialog_is_visible) + { + _find_dialog->setGeometry (_find_dialog_geometry); + _find_dialog->show (); + } + emit editor_state_changed (_copy_available, QDir::cleanPath (_file_name)); +} + +void +file_editor_tab::file_name_query (const QWidget* ID) +{ + // A zero (null pointer) means that all file editor tabs + // should respond, otherwise just the desired file editor tab. + if (ID != this && ID != 0) + return; + + // Unnamed files shouldn't be transmitted. + if (!_file_name.isEmpty ()) + emit add_filename_to_list (_file_name); +} + +void +file_editor_tab::handle_file_reload_answer (int decision) +{ + if (decision == QMessageBox::Yes) + { + load_file (_file_name); + } + + // Start watching file once again. + _file_system_watcher.addPath (_file_name); +} + +void +file_editor_tab::handle_file_resave_answer (int decision) +{ + if (decision == QMessageBox::Save) + { + save_file (_file_name); + } + else + { + if (close ()) + { + emit tab_remove_request (); + return; // Don't touch member variables after removal + } + } + + // Start watching file once again. + _file_system_watcher.addPath (_file_name); +} + +void +file_editor_tab::set_debugger_position (int line) +{ + _edit_area->markerDeleteAll (debugger_position); + if (line > 0) + { + _edit_area->markerAdd (line, debugger_position); + } +} diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/m-editor/file-editor-tab.h --- a/libgui/src/m-editor/file-editor-tab.h Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/m-editor/file-editor-tab.h Sun Dec 23 14:33:48 2012 -0600 @@ -28,61 +28,88 @@ #include #include +#include "find-dialog.h" + class file_editor; class file_editor_tab : public QWidget { Q_OBJECT public: - file_editor_tab (file_editor *fileEditor); - bool copy_available (); + file_editor_tab (QString directory = ""); + ~file_editor_tab (); public slots: void update_window_title(bool modified); void handle_copy_available(bool enableCopy); void handle_margin_clicked (int line, int margin, Qt::KeyboardModifiers state); - void comment_selected_text (); - void uncomment_selected_text (); - void find (); - void remove_bookmark (); - void toggle_bookmark (); - void next_bookmark (); - void previous_bookmark (); - void remove_all_breakpoints (); - void toggle_breakpoint (); - void next_breakpoint (); - void previous_breakpoint (); - void cut (); - void copy (); - void paste (); - void undo (); - void redo (); + + /** Tells the editor tab to react on changed settings. */ + void notice_settings (); + /** Will initiate close if associated with the identifier tag. */ + void conditional_close (const QWidget* ID); + /** Change to a different editor tab by identifier tag. */ + void change_editor_state (const QWidget* ID); + /** Simply transmit file name. */ + void file_name_query (const QWidget* ID); + + void undo (const QWidget* ID); + void redo (const QWidget* ID); + void copy (const QWidget* ID); + void cut (const QWidget* ID); + void paste (const QWidget* ID); + void save_file (const QWidget* ID); + void save_file (const QWidget* ID, const QString& fileName, bool remove_on_success); + void save_file_as (const QWidget* ID); + void run_file (const QWidget* ID); + void toggle_bookmark (const QWidget* ID); + void next_bookmark (const QWidget* ID); + void previous_bookmark (const QWidget* ID); + void remove_bookmark (const QWidget* ID); + + void toggle_breakpoint (const QWidget* ID); + void next_breakpoint (const QWidget* ID); + void previous_breakpoint (const QWidget* ID); + void remove_all_breakpoints (const QWidget* ID); + + void comment_selected_text (const QWidget* ID); + void uncomment_selected_text (const QWidget* ID); + void find (const QWidget* ID); + void set_debugger_position (int line); void set_modified (bool modified = true); - bool open_file (const QString& dir = QString ()); - bool load_file (const QString& fileName, bool silent = false); + QString load_file (const QString& fileName); void new_file (); - bool save_file (); - bool save_file (const QString& saveFileName); - bool save_file_as(); - void run_file (); void file_has_changed (const QString& fileName); - QString get_file_name () const {return _file_name;} - - /** Tells the editor tab to react on changed settings. */ - void notice_settings (); signals: void file_name_changed (const QString& fileName); - void editor_state_changed (); - void close_request (); + void editor_state_changed (bool copy_available, const QString& fileName); + void tab_remove_request (); + void add_filename_to_list (const QString& fileName); + void editor_check_conflict_save (const QString& saveFileName, bool remove_on_success); + void process_octave_code (const QString& command); protected: void closeEvent (QCloseEvent *event); void set_file_name (const QString& fileName); +private slots: + /** When user closes message box for reload question. */ + void handle_file_reload_answer (int decision); + /** When user closes message box for resave question. */ + void handle_file_resave_answer (int decision); + /** When user closes message box for modified question. */ + void handle_file_modified_answer (int decision); + /** When user closes find_dialog box. */ + void handle_find_dialog_finished (int decision); + /** When user closes QFileDialog box. */ + void handle_save_file_as_answer (const QString& fileName); + void handle_save_file_as_answer_close (const QString& fileName); + void handle_save_file_as_answer_cancel (); + private: struct bp_info @@ -96,6 +123,10 @@ int line; }; + void save_file (const QString& saveFileName, bool remove_on_success = false); + void save_file_as (bool remove_on_success = false); + void message_duplicate_file_name (const QString& fileName); + void update_lexer (); void request_add_breakpoint (int line); void request_remove_breakpoint (int line); @@ -108,7 +139,6 @@ void remove_breakpoint_callback (const bp_info& info); void remove_all_breakpoints_callback (const bp_info& info); - file_editor * _file_editor; QsciScintilla * _edit_area; QString _file_name; @@ -118,6 +148,10 @@ bool _copy_available; QFileSystemWatcher _file_system_watcher; + + find_dialog * _find_dialog; + bool _find_dialog_is_visible; + QRect _find_dialog_geometry; }; #endif // FILEEDITORTAB_H diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/m-editor/file-editor.cc --- a/libgui/src/m-editor/file-editor.cc Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/m-editor/file-editor.cc Sun Dec 23 14:33:48 2012 -0600 @@ -35,46 +35,33 @@ #include #include -file_editor::file_editor (QTerminal *t, main_window *m) - : file_editor_interface (t, m) +#include "octave-link.h" + +file_editor::file_editor (QWidget *p) + : file_editor_interface (p) { + // Set current editing directory before construct because loaded + // files will change ced accordingly. + ced = QDir::currentPath (); + construct (); - _terminal = t; - _main_window = m; setVisible (false); } file_editor::~file_editor () { QSettings *settings = resource_manager::get_settings (); - QStringList sessionFileNames; + fetFileNames.clear (); if (settings->value ("editor/restoreSession",true).toBool ()) { - for (int n=0;n<_tab_widget->count();++n) - { - file_editor_tab* tab = dynamic_cast (_tab_widget->widget (n)); - if (!tab) - continue; - sessionFileNames.append (tab->get_file_name ()); - } + // Have all file editor tabs signal what their file names are. + emit fetab_file_name_query (0); } - settings->setValue ("editor/savedSessionTabs", sessionFileNames); + settings->setValue ("editor/savedSessionTabs", fetFileNames); settings->sync (); } -QTerminal * -file_editor::terminal () -{ - return _terminal; -} - -main_window * -file_editor::get_main_window () -{ - return _main_window; -} - QMenu * file_editor::debug_menu () { @@ -102,10 +89,14 @@ void file_editor::request_new_file () { - file_editor_tab *fileEditorTab = new file_editor_tab (this); + // New file isn't a file_editor_tab function since the file + // editor tab has yet to be created and there is no object to + // pass a signal to. Hence, functionality is here. + + file_editor_tab *fileEditorTab = new file_editor_tab (ced); if (fileEditorTab) { - add_file_editor_tab (fileEditorTab); + add_file_editor_tab (fileEditorTab, UNNAMED_FILE); fileEditorTab->new_file (); } } @@ -113,208 +104,263 @@ void file_editor::request_open_file () { - file_editor_tab *current_tab = active_editor_tab (); - int curr_tab_index = _tab_widget->currentIndex (); - file_editor_tab *fileEditorTab = new file_editor_tab (this); - if (fileEditorTab) - { - add_file_editor_tab (fileEditorTab); - QString dir = QDir::currentPath (); - // get the filename of the last active tab to open a new file from there - if (current_tab) - dir = QDir::cleanPath (current_tab->get_file_name ()); - if (!fileEditorTab->open_file (dir)) - { - // If no file was loaded, remove the tab again. - _tab_widget->removeTab (_tab_widget->indexOf (fileEditorTab)); - // restore focus to previous tab - if (curr_tab_index>=0) - _tab_widget->setCurrentIndex (curr_tab_index); - } - } + // Open file isn't a file_editor_tab function since the file + // editor tab has yet to be created and there is no object to + // pass a signal to. Hence, functionality is here. + + // Create a NonModal message. + QFileDialog* fileDialog = new QFileDialog (this); + fileDialog->setNameFilter (SAVE_FILE_FILTER); + fileDialog->setAcceptMode (QFileDialog::AcceptOpen); + fileDialog->setViewMode (QFileDialog::Detail); + fileDialog->setDirectory (ced); + connect (fileDialog, SIGNAL (fileSelected (const QString&)), + this, SLOT (request_open_file (const QString&))); + fileDialog->setWindowModality (Qt::NonModal); + fileDialog->setAttribute (Qt::WA_DeleteOnClose); + fileDialog->show (); } void -file_editor::request_open_file (const QString& fileName, bool silent) +file_editor::request_open_file (const QString& openFileName) { - if (!isVisible ()) + if (openFileName.isEmpty ()) { - show (); + // ?? Not sure this will happen. This routine isn't even called + // if the user hasn't selected a file. } + else + { + // Have all file editor tabs signal what their file names are. + fetFileNames.clear (); + emit fetab_file_name_query (0); - file_editor_tab *fileEditorTab = new file_editor_tab (this); - int curr_tab_index = _tab_widget->currentIndex (); - if (fileEditorTab) - { - add_file_editor_tab (fileEditorTab); - if (!fileEditorTab->load_file (fileName, silent)) + // Check whether this file is already open in the editor. + if (fetFileNames.contains (openFileName, Qt::CaseSensitive)) { - // If no file was loaded, remove the tab again. - _tab_widget->removeTab (_tab_widget->indexOf (fileEditorTab)); - // restore focus to previous tab - _tab_widget->setCurrentIndex (curr_tab_index); + // Create a NonModal message so nothing is blocked and + // bring the existing file forward. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("File %1 is already open in the editor."). + arg (openFileName), QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + for(int i = 0; i < _tab_widget->count (); i++) + { + if (_tab_widget->tabText (i) == openFileName) + { + _tab_widget->setCurrentIndex (i); + break; + } + } + return; + } + + file_editor_tab *fileEditorTab = new file_editor_tab (); + if (fileEditorTab) + { + QString result = fileEditorTab->load_file(openFileName); + if (result == "") + { + // Supply empty title then have the file_editor_tab update + // with full or short name. + add_file_editor_tab (fileEditorTab, ""); + fileEditorTab->update_window_title (false); + } + else + { + delete fileEditorTab; + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("Could not open file %1 for read:\n%2."). + arg (openFileName).arg (result), + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + } } } } void +file_editor::check_conflict_save (const QString& saveFileName, bool remove_on_success) +{ + // Have all file editor tabs signal what their file names are. + fetFileNames.clear (); + emit fetab_file_name_query (0); + + // If one of those names matches the desired name, that's a conflict. + if (fetFileNames.contains (saveFileName, Qt::CaseSensitive)) + { + // Note: to overwrite the contents of some other file editor tab + // with the same name requires identifying which file editor tab + // that is (not too difficult) then close that tab. Of course, + // that could trigger another dialog box if the file editor tab + // with the same name has modifications in it. This could become + // somewhat confusing to the user. For now, opt to do nothing. + + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("File not saved! You've selected a file name\n\n %1\n\nwhich is the same as an already open file in the editor. (Could allow overwriting, with message, if that is what folks want.)"). + arg (saveFileName), + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + + return; + } + + QObject* saveFileObject = sender (); + QWidget* saveFileWidget = 0; + for(int i = 0; i < _tab_widget->count (); i++) + { + if (_tab_widget->widget (i) == saveFileObject) + { + saveFileWidget = _tab_widget->widget (i); + break; + } + } + if (!saveFileWidget) + { + // Create a NonModal message about error. + QMessageBox* msgBox = new QMessageBox ( + QMessageBox::Critical, tr ("Octave Editor"), + tr ("The associated file editor tab has disappeared. It was likely closed by some means."), + QMessageBox::Ok, 0); + msgBox->setWindowModality (Qt::NonModal); + msgBox->setAttribute (Qt::WA_DeleteOnClose); + msgBox->show (); + return; + } + + // Can save without conflict, have the file editor tab do so. + emit fetab_save_file (saveFileWidget, saveFileName, remove_on_success); +} + +void file_editor::request_undo () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->undo (); + emit fetab_undo (_tab_widget->currentWidget ()); } void file_editor::request_redo () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->redo (); + emit fetab_redo (_tab_widget->currentWidget ()); } void file_editor::request_copy () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->copy (); + emit fetab_copy (_tab_widget->currentWidget ()); } void file_editor::request_cut () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->cut (); + emit fetab_cut (_tab_widget->currentWidget ()); } void file_editor::request_paste () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->paste (); + emit fetab_paste (_tab_widget->currentWidget ()); } void file_editor::request_save_file () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->save_file (); + emit fetab_save_file (_tab_widget->currentWidget ()); } void file_editor::request_save_file_as () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->save_file_as (); + emit fetab_save_file_as (_tab_widget->currentWidget ()); } void file_editor::request_run_file () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->run_file (); + emit fetab_run_file (_tab_widget->currentWidget ()); } void file_editor::request_toggle_bookmark () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->toggle_bookmark (); + emit fetab_toggle_bookmark (_tab_widget->currentWidget ()); } void file_editor::request_next_bookmark () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->next_bookmark (); + emit fetab_next_bookmark (_tab_widget->currentWidget ()); } void file_editor::request_previous_bookmark () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->previous_bookmark (); + emit fetab_previous_bookmark (_tab_widget->currentWidget ()); } void file_editor::request_remove_bookmark () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->remove_bookmark (); + emit fetab_remove_bookmark (_tab_widget->currentWidget ()); } void file_editor::request_toggle_breakpoint () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->toggle_breakpoint (); + emit fetab_toggle_breakpoint (_tab_widget->currentWidget ()); } void file_editor::request_next_breakpoint () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->next_breakpoint (); + emit fetab_next_breakpoint (_tab_widget->currentWidget ()); } void file_editor::request_previous_breakpoint () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->previous_breakpoint (); + emit fetab_previous_breakpoint (_tab_widget->currentWidget ()); } void file_editor::request_remove_breakpoint () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->remove_all_breakpoints (); + emit fetab_remove_all_breakpoints (_tab_widget->currentWidget ()); } void file_editor::request_comment_selected_text () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->comment_selected_text (); + emit fetab_comment_selected_text (_tab_widget->currentWidget ()); } void file_editor::request_uncomment_selected_text () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->uncomment_selected_text (); + emit fetab_uncomment_selected_text (_tab_widget->currentWidget ()); } void file_editor::request_find () { - file_editor_tab *_active_file_editor_tab = active_editor_tab (); - if (_active_file_editor_tab) - _active_file_editor_tab->find (); + emit fetab_find (_tab_widget->currentWidget ()); } void file_editor::handle_file_name_changed (const QString& fileName) { - QObject *senderObject = sender (); - file_editor_tab *fileEditorTab - = dynamic_cast (senderObject); + QObject *fileEditorTab = sender(); if (fileEditorTab) { for(int i = 0; i < _tab_widget->count (); i++) @@ -330,26 +376,72 @@ void file_editor::handle_tab_close_request (int index) { - file_editor_tab *fileEditorTab - = dynamic_cast (_tab_widget->widget (index)); + // Signal to the tabs a request to close whomever matches the identifying + // tag (i.e., unique widget pointer). The reason for this indirection is + // that it will enable a file editor widget to toss up a non-static + // dialog box and later signal that it wants to be removed. + QWidget *tabID = _tab_widget->widget (index); + emit fetab_close_request (tabID); +} + +void +file_editor::handle_tab_remove_request () +{ + QObject *fileEditorTab = sender(); if (fileEditorTab) - if (fileEditorTab->close ()) - { - _tab_widget->removeTab (index); - delete fileEditorTab; - } + { + for(int i = 0; i < _tab_widget->count (); i++) + { + if (_tab_widget->widget (i) == fileEditorTab) + { + _tab_widget->removeTab (i); + delete fileEditorTab; + } + } + } +} + +void +file_editor::handle_add_filename_to_list (const QString& fileName) +{ + fetFileNames.append (fileName); } void -file_editor::handle_tab_close_request () +file_editor::active_tab_changed (int index) +{ + emit fetab_change_request (_tab_widget->widget (index)); +} + +void +file_editor::handle_editor_state_changed (bool copy_available, const QString& file_name) { - file_editor_tab *fileEditorTab = dynamic_cast (sender ()); - if (fileEditorTab) - if (fileEditorTab->close ()) - { - _tab_widget->removeTab (_tab_widget->indexOf (fileEditorTab)); - delete fileEditorTab; - } + // In case there is some scenario where traffic could be coming from + // all the file editor tabs, just process info from the current active tab. + if (sender() == _tab_widget->currentWidget ()) + { + _copy_action->setEnabled (copy_available); + _cut_action->setEnabled (copy_available); + if (!file_name.isEmpty ()) + { + ced = QDir::cleanPath (file_name); + int lastslash = ced.lastIndexOf ('/'); + // Test against > 0 because if somehow the directory is "/" the + // slash should be retained. Otherwise, last slash is removed. + if (lastslash > 0 && lastslash != ced.count ()) + { + ced = ced.left (lastslash); + } + } + setFocusProxy (_tab_widget->currentWidget ()); + } +} + +void +file_editor::notice_settings () +{ + // Relay signal to file editor tabs. + emit fetab_settings_changed (); } // slot for signal that is emitted when floating property changes @@ -364,37 +456,6 @@ } void -file_editor::active_tab_changed (int) -{ - handle_editor_state_changed (); -} - -void -file_editor::handle_editor_state_changed () -{ - file_editor_tab *f = active_editor_tab (); - if (f) - { - bool copy_available = f->copy_available (); - _copy_action->setEnabled (copy_available); - _cut_action->setEnabled (copy_available); - setFocusProxy (f); - } -} - -void -file_editor::notice_settings () -{ - for(int i = 0; i < _tab_widget->count (); i++) - { - file_editor_tab *fileEditorTab - = dynamic_cast (_tab_widget->widget (i)); - if (fileEditorTab) - fileEditorTab->notice_settings (); - } -} - -void file_editor::construct () { QWidget *editor_widget = new QWidget (this); @@ -607,25 +668,79 @@ QStringList sessionFileNames = settings->value("editor/savedSessionTabs", QStringList()).toStringList (); for (int n=0; n < sessionFileNames.count (); ++n) - request_open_file (sessionFileNames.at (n), true); + request_open_file (sessionFileNames.at (n)); } } void -file_editor::add_file_editor_tab (file_editor_tab *f) +file_editor::add_file_editor_tab (file_editor_tab *f, const QString &fn) { - _tab_widget->addTab (f, ""); - connect (f, SIGNAL (file_name_changed(QString)), - this, SLOT(handle_file_name_changed(QString))); - connect (f, SIGNAL (editor_state_changed ()), - this, SLOT (handle_editor_state_changed ())); - connect (f, SIGNAL (close_request ()), - this, SLOT (handle_tab_close_request ())); + _tab_widget->addTab (f, fn); + + // Signals from the file editor_tab + connect (f, SIGNAL (file_name_changed (const QString&)), + this, SLOT (handle_file_name_changed (const QString&))); + connect (f, SIGNAL (editor_state_changed (bool, const QString&)), + this, SLOT (handle_editor_state_changed (bool, const QString&))); + connect (f, SIGNAL (tab_remove_request ()), + this, SLOT (handle_tab_remove_request ())); + connect (f, SIGNAL (add_filename_to_list (const QString&)), + this, SLOT (handle_add_filename_to_list (const QString&))); + connect (f, SIGNAL (editor_check_conflict_save (const QString&, bool)), + this, SLOT (check_conflict_save (const QString&, bool))); + connect (f, SIGNAL (process_octave_code (const QString&)), + parent (), SLOT (handle_command_double_clicked (const QString&))); + + // Signals from the file_editor non-trivial operations + connect (this, SIGNAL (fetab_settings_changed ()), + f, SLOT (notice_settings ())); + connect (this, SIGNAL (fetab_close_request (const QWidget*)), + f, SLOT (conditional_close (const QWidget*))); + connect (this, SIGNAL (fetab_change_request (const QWidget*)), + f, SLOT (change_editor_state (const QWidget*))); + connect (this, SIGNAL (fetab_file_name_query (const QWidget*)), + f, SLOT (file_name_query (const QWidget*))); + connect (this, SIGNAL (fetab_save_file (const QWidget*, const QString&, bool)), + f, SLOT (save_file (const QWidget*, const QString&, bool))); + // Signals from the file_editor trivial operations + connect (this, SIGNAL (fetab_undo (const QWidget*)), + f, SLOT (undo (const QWidget*))); + connect (this, SIGNAL (fetab_redo (const QWidget*)), + f, SLOT (redo (const QWidget*))); + connect (this, SIGNAL (fetab_copy (const QWidget*)), + f, SLOT (copy (const QWidget*))); + connect (this, SIGNAL (fetab_cut (const QWidget*)), + f, SLOT (cut (const QWidget*))); + connect (this, SIGNAL (fetab_paste (const QWidget*)), + f, SLOT (paste (const QWidget*))); + connect (this, SIGNAL (fetab_save_file (const QWidget*)), + f, SLOT (save_file (const QWidget*))); + connect (this, SIGNAL (fetab_save_file_as (const QWidget*)), + f, SLOT (save_file_as (const QWidget*))); + connect (this, SIGNAL (fetab_run_file (const QWidget*)), + f, SLOT (run_file (const QWidget*))); + connect (this, SIGNAL (fetab_toggle_bookmark (const QWidget*)), + f, SLOT (toggle_bookmark (const QWidget*))); + connect (this, SIGNAL (fetab_next_bookmark (const QWidget*)), + f, SLOT (next_bookmark (const QWidget*))); + connect (this, SIGNAL (fetab_previous_bookmark (const QWidget*)), + f, SLOT (previous_bookmark (const QWidget*))); + connect (this, SIGNAL (fetab_remove_bookmark (const QWidget*)), + f, SLOT (remove_bookmark (const QWidget*))); + connect (this, SIGNAL (fetab_toggle_breakpoint (const QWidget*)), + f, SLOT (toggle_breakpoint (const QWidget*))); + connect (this, SIGNAL (fetab_next_breakpoint (const QWidget*)), + f, SLOT (next_breakpoint (const QWidget*))); + connect (this, SIGNAL (fetab_previous_breakpoint (const QWidget*)), + f, SLOT (previous_breakpoint (const QWidget*))); + connect (this, SIGNAL (fetab_remove_all_breakpoints (const QWidget*)), + f, SLOT (remove_all_breakpoints (const QWidget*))); + connect (this, SIGNAL (fetab_comment_selected_text (const QWidget*)), + f, SLOT (comment_selected_text (const QWidget*))); + connect (this, SIGNAL (fetab_uncomment_selected_text (const QWidget*)), + f, SLOT (uncomment_selected_text (const QWidget*))); + connect (this, SIGNAL (fetab_find (const QWidget*)), + f, SLOT (find (const QWidget*))); + _tab_widget->setCurrentWidget (f); } - -file_editor_tab * -file_editor::active_editor_tab () -{ - return dynamic_cast (_tab_widget->currentWidget ()); -} diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/m-editor/file-editor.h --- a/libgui/src/m-editor/file-editor.h Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/m-editor/file-editor.h Sun Dec 23 14:33:48 2012 -0600 @@ -30,7 +30,6 @@ #include #include -#include "main-window.h" #include "file-editor-interface.h" #include "file-editor-tab.h" @@ -48,23 +47,48 @@ Q_OBJECT public: - file_editor (QTerminal *terminal, main_window *m); + file_editor (QWidget *p); ~file_editor (); void loadFile (const QString& fileName); - QTerminal * terminal (); - main_window * get_main_window (); - QMenu * debug_menu (); QToolBar * toolbar (); void handle_entered_debug_mode (); void handle_quit_debug_mode (); +signals: + void fetab_settings_changed (); + void fetab_close_request (const QWidget* ID); + void fetab_change_request (const QWidget* ID); + void fetab_file_name_query (const QWidget* ID); + // Save is a ping-pong type of communication + void fetab_save_file (const QWidget* ID, const QString& fileName, bool remove_on_success); + // No fetab_open, functionality in editor + // No fetab_new, functionality in editor + void fetab_undo (const QWidget* ID); + void fetab_redo (const QWidget* ID); + void fetab_copy (const QWidget* ID); + void fetab_cut (const QWidget* ID); + void fetab_paste (const QWidget* ID); + void fetab_save_file (const QWidget* ID); + void fetab_save_file_as (const QWidget* ID); + void fetab_run_file (const QWidget* ID); + void fetab_toggle_bookmark (const QWidget* ID); + void fetab_next_bookmark (const QWidget* ID); + void fetab_previous_bookmark (const QWidget* ID); + void fetab_remove_bookmark (const QWidget* ID); + void fetab_toggle_breakpoint (const QWidget* ID); + void fetab_next_breakpoint (const QWidget* ID); + void fetab_previous_breakpoint (const QWidget* ID); + void fetab_remove_all_breakpoints (const QWidget* ID); + void fetab_comment_selected_text (const QWidget* ID); + void fetab_uncomment_selected_text (const QWidget* ID); + void fetab_find (const QWidget* ID); + public slots: void request_new_file (); void request_open_file (); - void request_open_file (const QString& fileName, bool silent = false); void request_undo (); void request_redo (); @@ -90,19 +114,28 @@ void handle_file_name_changed (const QString& fileName); void handle_tab_close_request (int index); - void handle_tab_close_request (); + void handle_tab_remove_request (); + void handle_add_filename_to_list (const QString& fileName); void active_tab_changed (int index); - void handle_editor_state_changed (); + void handle_editor_state_changed (bool enableCopy, const QString& fileName); + void check_conflict_save (const QString& fileName, bool remove_on_success); + /** Slot when floating property changes */ void top_level_changed (bool floating); /** Tells the editor to react on changed settings. */ void notice_settings (); +private slots: + void request_open_file (const QString& fileName); + private: void construct (); - void add_file_editor_tab(file_editor_tab *f); - file_editor_tab *active_editor_tab(); + void add_file_editor_tab(file_editor_tab *f, const QString &fn); + void save_file_as (QWidget *fetabID = 0); + + QStringList fetFileNames; + QString ced; QMenuBar * _menu_bar; QToolBar * _tool_bar; diff -r 13d1e9bfa362 -r 424edeca3c66 libgui/src/main-window.cc --- a/libgui/src/main-window.cc Tue Dec 25 19:41:12 2012 +0000 +++ b/libgui/src/main-window.cc Sun Dec 23 14:33:48 2012 -0600 @@ -576,7 +576,7 @@ dummyWidget->hide (); setCentralWidget (dummyWidget); - _file_editor = new file_editor (_terminal, this); + _file_editor = new file_editor (this); QMenu *file_menu = menuBar ()->addMenu (tr ("&File"));