Mon, 22 Apr 2013 16:06:41 +0300
Keep track of when the file was last saved and warn if there are unsaved changes when the application is closing.
file.cpp | file | annotate | diff | comparison | revisions | |
file.h | file | annotate | diff | comparison | revisions | |
gui.cpp | file | annotate | diff | comparison | revisions | |
gui.h | file | annotate | diff | comparison | revisions | |
history.cpp | file | annotate | diff | comparison | revisions | |
ldtypes.cpp | file | annotate | diff | comparison | revisions |
--- a/file.cpp Mon Apr 22 14:14:36 2013 +0300 +++ b/file.cpp Mon Apr 22 16:06:41 2013 +0300 @@ -18,17 +18,36 @@ #include <vector> #include <stdio.h> +#include <qmessagebox.h> #include "common.h" #include "config.h" #include "file.h" #include "misc.h" #include "bbox.h" #include "gui.h" +#include "history.h" cfg (str, io_ldpath, ""); cfg (str, io_recentfiles, ""); // ============================================================================= +OpenFile::OpenFile () { + implicit = true; + savePos = -1; +} + +// ============================================================================= +OpenFile::~OpenFile () { + // Clear everything from the model + for (LDObject* obj : objects) + delete obj; + + // Clear the cache as well + for (LDObject* obj : objCache) + delete obj; +} + +// ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= OpenFile* findLoadedFile (str zName) { @@ -150,16 +169,34 @@ // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= -// Clear everything from the model -void OpenFile::close () { - for (LDObject* obj : objects) - delete obj; +bool OpenFile::safeToClose () { + setlocale (LC_ALL, "C"); - // Clear the cache as well - for (LDObject* obj : objCache) - delete obj; + // If we have unsaved changes, warn and give the option of saving. + if (!implicit && History::pos () != savePos) { + switch (QMessageBox::question (g_ForgeWindow, "Unsaved Changes", + format ("There are unsaved changes to %s. Should it be saved?", zFileName.chars ()), + (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel), QMessageBox::Cancel)) + { + case QMessageBox::Yes: + if (!save ()) { + str errormsg = format ("Failed to save %s: %s\nDo you still want to close?", + zFileName.chars (), strerror (lastError)); + if (!confirm ("Save Failure", errormsg)) + return false; + } + + break; + + case QMessageBox::Cancel: + return false; + + default: + break; + } + } - delete this; + return true; } // ============================================================================= @@ -171,7 +208,7 @@ // Remove all loaded files and the objects they contain for (OpenFile* file : g_LoadedFiles) - file->close (); + delete file; // Clear the array g_LoadedFiles.clear(); @@ -238,6 +275,7 @@ if (!pFile) return; + pFile->implicit = false; g_CurrentFile = pFile; // Recalculate the bounding box @@ -254,13 +292,16 @@ // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= -bool OpenFile::save (str zPath) { - if (!~zPath) - zPath = zFileName; +bool OpenFile::save (str path) { + if (!~path) + path = zFileName; - FILE* fp = fopen (zPath, "w"); - if (!fp) + FILE* fp = fopen (path, "w"); + + if (!fp) { + lastError = errno; return false; + } // Write all entries now for (LDObject* obj : objects) { @@ -271,6 +312,13 @@ } fclose (fp); + + // We have successfully saved, update the save position now. + savePos = History::pos (); + + g_ForgeWindow->setTitle (); + logf ("Saved successfully to %s\n", path.chars ()); + return true; } @@ -506,7 +554,7 @@ // First, close all but the current open file. for (OpenFile* file : g_LoadedFiles) if (file != g_CurrentFile) - file->close (); + delete file; g_LoadedFiles.clear (); g_LoadedFiles.push_back (g_CurrentFile);
--- a/file.h Mon Apr 22 14:14:36 2013 +0300 +++ b/file.h Mon Apr 22 16:06:41 2013 +0300 @@ -35,18 +35,30 @@ vector<LDObject*> objects; vector<LDObject*> objCache; // Cache of this file's contents, if desired - // Closes this OpenFile. The object is deleted in the process. - void close (); + int lastError; + + // Is this file implicit? Implicit files are opened automatically for + // caching purposes and are hidden from the user. + bool implicit; + + OpenFile (); + ~OpenFile (); // Saves this file to disk. bool save (str zPath = ""); + // Perform safety checks. Do this before closing any files! + bool safeToClose (); + // Adds an object to this file at the appropriate location. ulong addObject (LDObject* obj); // Deletes the given object from the object chain. void forgetObject (LDObject* obj); + // At what point was this file last saved? + long savePos; + LDObject* object (size_t uPos) const { return objects[uPos]; }
--- a/gui.cpp Mon Apr 22 14:14:36 2013 +0300 +++ b/gui.cpp Mon Apr 22 16:06:41 2013 +0300 @@ -453,22 +453,25 @@ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= void ForgeWindow::setTitle () { - str zTitle = APPNAME_DISPLAY " v" VERSION_STRING; + str title = APPNAME_DISPLAY " v" VERSION_STRING; // Append our current file if we have one if (g_CurrentFile) { - zTitle.appendformat (": %s", basename (g_CurrentFile->zFileName.chars())); + title += format (": %s", basename (g_CurrentFile->zFileName.chars())); if (g_CurrentFile->objects.size() > 0 && g_CurrentFile->objects[0]->getType() == OBJ_Comment) { // Append title LDComment* comm = static_cast<LDComment*> (g_CurrentFile->objects[0]); - zTitle.appendformat (": %s", comm->zText.chars()); + title += format (": %s", comm->zText.chars()); } + + if (History::pos () != g_CurrentFile->savePos) + title += '*'; } - setWindowTitle (zTitle.chars()); + setWindowTitle (title); } // ============================================================================= @@ -861,6 +864,21 @@ // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ============================================================================= +void ForgeWindow::closeEvent (QCloseEvent* ev) { + // Check whether it's safe to close all files. + for (OpenFile* f : g_LoadedFiles) { + if (!f->safeToClose ()) { + ev->ignore (); + return; + } + } + + ev->accept (); +} + +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= std::vector<LDObject*>& ForgeWindow::selection () { return paSelection; } @@ -873,7 +891,11 @@ } // ============================================================================= -bool confirm (str zMessage) { - return QMessageBox::question (g_ForgeWindow, "Confirm", zMessage, +bool confirm (str msg) { + return confirm ("Confirm", msg); +} + +bool confirm (str title, str msg) { + return QMessageBox::question (g_ForgeWindow, title, msg, (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes; } \ No newline at end of file
--- a/gui.h Mon Apr 22 14:14:36 2013 +0300 +++ b/gui.h Mon Apr 22 16:06:41 2013 +0300 @@ -143,7 +143,10 @@ bool isSelected (LDObject* obj); short getSelectedColor(); LDObjectType_e getSelectedType (); - + +protected: + void closeEvent (QCloseEvent* ev); + private: void createMenuActions (); void createMenus (); @@ -161,7 +164,8 @@ // Other GUI-related stuff not directly part of ForgeWindow: QIcon getIcon (const char* sIconName); std::vector<quickColorMetaEntry> parseQuickColorMeta (); -bool confirm (str zMessage); +bool confirm (str title, str msg); +bool confirm (str msg); // ----------------------------------------------------------------------------- // Pointer to the instance of ForgeWindow.
--- a/history.cpp Mon Apr 22 14:14:36 2013 +0300 +++ b/history.cpp Mon Apr 22 16:06:41 2013 +0300 @@ -31,37 +31,37 @@ namespace History { std::vector<HistoryEntry*> entries; - static long lPos = -1; + static long m_pos = -1; // ========================================================================= void addEntry (HistoryEntry* entry) { // If there's any entries after our current position, we need to remove them now - for (ulong i = lPos + 1; i < entries.size(); ++i) { + for (ulong i = m_pos + 1; i < entries.size(); ++i) { delete entries[i]; entries.erase (entries.begin() + i); } entries.push_back (entry); - lPos++; + m_pos++; updateActions (); } // ========================================================================= void undo () { - if (lPos == -1) + if (m_pos == -1) return; // nothing to undo - entries[lPos--]->undo (); + entries[m_pos--]->undo (); updateActions (); } // ========================================================================= void redo () { - if (lPos == (long) entries.size () - 1) + if (m_pos == (long) entries.size () - 1) return; // nothing to redo; - entries[++lPos]->redo (); + entries[++m_pos]->redo (); updateActions (); } @@ -71,19 +71,22 @@ delete entry; entries.clear (); - lPos = -1; + m_pos = -1; updateActions (); } // ========================================================================= void updateActions () { - ACTION_NAME (undo)->setEnabled (lPos > -1); - ACTION_NAME (redo)->setEnabled (lPos < (long) entries.size () - 1); + ACTION_NAME (undo)->setEnabled (m_pos > -1); + ACTION_NAME (redo)->setEnabled (m_pos < (long) entries.size () - 1); + + // Update the window title as well + g_ForgeWindow->setTitle (); } // ========================================================================= long pos () { - return lPos; + return m_pos; } }
--- a/ldtypes.cpp Mon Apr 22 14:14:36 2013 +0300 +++ b/ldtypes.cpp Mon Apr 22 16:06:41 2013 +0300 @@ -116,6 +116,7 @@ str LDSubfile::getContents () { str val = format ("1 %d %s ", dColor, vPosition.getStringRep (false).chars ()); val += mMatrix.getStringRep (); + val += ' '; val += zFileName; return val; }