Keep track of when the file was last saved and warn if there are unsaved changes when the application is closing.

Mon, 22 Apr 2013 16:06:41 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Mon, 22 Apr 2013 16:06:41 +0300
changeset 127
a6e2067bb2f1
parent 126
d4f794a48b3e
child 128
73a7edf82ca9

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;
 }

mercurial