diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 0071dd1fc7..488e3c65f5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,4 +1,3 @@ - set (OPENCS_SRC main.cpp) opencs_units (. editor) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5966d089e3..8dc5366a98 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -113,5 +113,7 @@ int CS::Editor::run() { mStartup.show(); + QApplication::setQuitOnLastWindowClosed (true); + return QApplication::exec(); } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 11d877d0b1..af34aeedf4 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,7 +2,7 @@ #include "document.hpp" #include - +#include void CSMDoc::Document::load (const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool lastAsModified) { @@ -237,6 +237,11 @@ CSMDoc::Document::Document (const std::vector& files, b connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } +CSMDoc::Document::~Document() +{ + qDebug() << "document destroyed"; +} + QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; @@ -290,11 +295,13 @@ void CSMDoc::Document::abortOperation (int type) } } + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } + void CSMDoc::Document::operationDone (int type) { emit stateChanged (getState(), this); @@ -308,9 +315,12 @@ void CSMDoc::Document::saving() if (mSaveCount>15) { + //clear the stack before resetting the save state + //to avoid emitting incorrect states + mUndoStack.setClean(); + mSaveCount = 0; mSaveTimer.stop(); - mUndoStack.setClean(); emit stateChanged (getState(), this); } } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index a7b198689e..94d5fe85cc 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -63,6 +63,7 @@ namespace CSMDoc public: Document (const std::vector& files, bool new_); + ~Document(); QUndoStack& getUndoStack(); @@ -105,4 +106,4 @@ namespace CSMDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index ce001afaa9..7ee4b87260 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -56,7 +56,7 @@ void CSVDoc::Operations::quitOperation (int type) mLayout->removeItem ((*iter)->getLayout()); - delete *iter; + (*iter)->deleteLater(); mOperations.erase (iter); if (oldCount > 1) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 286d7a6ed6..995d3ca2e2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "../../model/doc/document.hpp" @@ -39,6 +40,16 @@ void CSVDoc::View::setupFileMenu() mSave = new QAction (tr ("&Save"), this); connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); + + QAction *close = new QAction (tr ("&Close"), this); + connect (close, SIGNAL (triggered()), this, SLOT (close())); + file->addAction(close); + + QAction *exit = new QAction (tr ("&Exit"), this); + connect (exit, SIGNAL (triggered()), this, SLOT (exit())); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); + + file->addAction(exit); } void CSVDoc::View::setupEditMenu() @@ -117,15 +128,15 @@ void CSVDoc::View::updateActions() mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); } -CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent) - : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), QMainWindow (viewParent) +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) + : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), + mViewTotal (totalViews) { - setDockOptions (QMainWindow::AllowNestedDocks); - resize (300, 300); /// \todo get default size from settings and set reasonable minimal size - mSubViewWindow = new QMainWindow(); - setCentralWidget (mSubViewWindow); + mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); + + setCentralWidget (&mSubViewWindow); mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -200,7 +211,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); - mSubViewWindow->addDockWidget (Qt::TopDockWidgetArea, view); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, SLOT (addSubView (const CSMWorld::UniversalId&))); @@ -239,7 +250,12 @@ void CSVDoc::View::abortOperation (int type) updateActions(); } -QDockWidget *CSVDoc::View::getOperations() const +CSVDoc::Operations *CSVDoc::View::getOperations() const { return mOperations; } + +void CSVDoc::View::exit() +{ + emit exitApplicationRequest (this); +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 28ab24b744..e91a4d4a80 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -41,7 +41,8 @@ namespace CSVDoc std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; - QMainWindow* mSubViewWindow; + QMainWindow mSubViewWindow; + // not implemented View (const View&); @@ -65,9 +66,12 @@ namespace CSVDoc void updateActions(); + void exitApplication(); + public: - View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent); + View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); + ///< The ownership of \a document is not transferred to *this. virtual ~View(); @@ -82,7 +86,7 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); - QDockWidget *getOperations() const; + Operations *getOperations() const; signals: @@ -90,23 +94,28 @@ namespace CSVDoc void loadDocumentRequest(); + void exitApplicationRequest (CSVDoc::View *view); + public slots: void addSubView (const CSMWorld::UniversalId& id); + void abortOperation (int type); + private slots: void newView(); void save(); + void exit(); + void verify(); void addGlobalsSubView(); void addGmstsSubView(); - void abortOperation (int type); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 718b807281..527e3b2044 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -12,6 +12,11 @@ #include "view.hpp" +#include +#include +#include +#include + void CSVDoc::ViewManager::updateIndices() { std::map > documents; @@ -31,7 +36,7 @@ void CSVDoc::ViewManager::updateIndices() } CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) -: mDocumentManager (documentManager) + : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; @@ -40,7 +45,6 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); - } CSVDoc::ViewManager::~ViewManager() @@ -63,9 +67,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } - QMainWindow *mainWindow = new QMainWindow; + View *view = new View (*this, document, countViews (document)+1); - View *view = new View (*this, document, countViews (document)+1, mainWindow); mViews.push_back (view); @@ -94,6 +97,8 @@ bool CSVDoc::ViewManager::closeRequest (View *view) { std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); + bool continueWithClose = true; + if (iter!=mViews.end()) { bool last = countViews (view->getDocument())<=1; @@ -101,16 +106,139 @@ bool CSVDoc::ViewManager::closeRequest (View *view) /// \todo check if save is in progress -> warn user about possible data loss /// \todo check if document has not been saved -> return false and start close dialogue - mViews.erase (iter); - view->deleteLater(); + CSMDoc::Document *document = view->getDocument(); if (last) - mDocumentManager.removeDocument (view->getDocument()); + continueWithClose = notifySaveOnClose (view); else + { + (*iter)->deleteLater(); + mViews.erase (iter); + updateIndices(); + } } - return true; + return continueWithClose; +} + +bool CSVDoc::ViewManager::notifySaveOnClose (CSVDoc::View *view) +{ + bool result = true; + CSMDoc::Document *document = view->getDocument(); + + //notify user of saving in progress + if ( (document->getState() & CSMDoc::State_Saving) ) + result = showSaveInProgressMessageBox (view); + + //notify user of unsaved changes and process response + else if ( document->getState() & CSMDoc::State_Modified) + result = showModifiedDocumentMessageBox (view); + + return result; +} + +bool CSVDoc::ViewManager::showModifiedDocumentMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document has been modified."); + messageBox.setInformativeText ("Do you want to save your changes?"); + messageBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + messageBox.setDefaultButton (QMessageBox::Save); + + bool retVal = true; + + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + mUserWarned = true; + int response = messageBox.exec(); + mUserWarned = false; + + switch (response) + { + case QMessageBox::Save: + + document->save(); + mExitOnSaveStateChange = true; + retVal = false; + break; + + case QMessageBox::Discard: + + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + break; + + case QMessageBox::Cancel: + + //disconnect to prevent unintended view closures + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + break; + + default: + break; + + } + + return retVal; +} + +bool CSVDoc::ViewManager::showSaveInProgressMessageBox (CSVDoc::View *view) +{ + QMessageBox messageBox; + CSMDoc::Document *document = view->getDocument(); + + messageBox.setText ("The document is currently being saved."); + messageBox.setInformativeText("Do you want to close now and abort saving, or wait until saving has completed?"); + + QPushButton* waitButton = messageBox.addButton (tr("Wait"), QMessageBox::YesRole); + QPushButton* closeButton = messageBox.addButton (tr("Close Now"), QMessageBox::RejectRole); + QPushButton* cancelButton = messageBox.addButton (tr("Cancel"), QMessageBox::NoRole); + + messageBox.setDefaultButton (waitButton); + + bool retVal = true; + + //Connections shut down message box if operation ends before user makes a decision. + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + connect (this, SIGNAL (closeMessageBox()), &messageBox, SLOT (close())); + + //set / clear the user warned flag to indicate whether or not the message box is currently active. + mUserWarned = true; + messageBox.exec(); + mUserWarned = false; + + //if closed by the warning handler, defaults to the RejectRole button (closeButton) + if (messageBox.clickedButton() == waitButton) + { + //save the View iterator for shutdown after the save operation ends + mExitOnSaveStateChange = true; + retVal = false; + } + + else if (messageBox.clickedButton() == closeButton) + { + //disconnect to avoid segmentation fault + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + + view->abortOperation(CSMDoc::State_Saving); + mExitOnSaveStateChange = true; + } + + else if (messageBox.clickedButton() == cancelButton) + { + //abort shutdown, allow save to complete + //disconnection to prevent unintended view closures + mExitOnSaveStateChange = false; + disconnect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (onExitWarningHandler(int, CSMDoc::Document *))); + retVal = false; + } + + return retVal; } void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) @@ -126,3 +254,25 @@ void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, if ((*iter)->getDocument()==document) (*iter)->updateProgress (current, max, type, threads); } + +void CSVDoc::ViewManager::onExitWarningHandler (int state, CSMDoc::Document *document) +{ + if ( !(state & CSMDoc::State_Saving) ) + { + //if the user is being warned (message box is active), shut down the message box, + //as there is no save operation currently running + if ( mUserWarned ) + emit closeMessageBox(); + + //otherwise, the user has closed the message box before the save operation ended. + //exit the application + else if (mExitOnSaveStateChange) + QApplication::instance()->exit(); + } +} + +void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) +{ + if (notifySaveOnClose (view)) + QApplication::instance()->exit(); +} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 72e7a3e1a1..90f23eaa11 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -27,12 +27,17 @@ namespace CSVDoc CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories; + bool mExitOnSaveStateChange; + bool mUserWarned; // not implemented ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); void updateIndices(); + bool notifySaveOnClose (View *view = 0); + bool showModifiedDocumentMessageBox (View *view); + bool showSaveInProgressMessageBox (View *view); public: @@ -54,13 +59,21 @@ namespace CSVDoc void loadDocumentRequest(); + void closeMessageBox(); + + public slots: + + void exitApplication (CSVDoc::View *view); + private slots: void documentStateChanged (int state, CSMDoc::Document *document); void progress (int current, int max, int type, int threads, CSMDoc::Document *document); + + void onExitWarningHandler(int state, CSMDoc::Document* document); }; } -#endif \ No newline at end of file +#endif diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 71a320190c..824fbd565e 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include