From afc72ae949266e359a79693a67bff390761b06cc Mon Sep 17 00:00:00 2001
From: Vinicius Santos <vinicius.santos.lista@gmail.com>
Date: Tue, 14 Aug 2012 12:34:59 -0300
Subject: [PATCH 31/31] Implemeting auto save feature in the SQL Editor.

---
 pgadmin/frm/frmOptions.cpp          |    4 +
 pgadmin/frm/frmQuery.cpp            |  129 +++++++++++++++++++++++++++++++++-
 pgadmin/include/frm/frmQuery.h      |   36 +++++++++-
 pgadmin/include/utils/sysSettings.h |   10 +++
 pgadmin/pgAdmin3.cpp                |    1 +
 pgadmin/ui/frmOptions.xrc           |   12 +++
 6 files changed, 184 insertions(+), 8 deletions(-)

diff --git a/pgadmin/frm/frmOptions.cpp b/pgadmin/frm/frmOptions.cpp
index 1ad00ad..6083c69 100644
--- a/pgadmin/frm/frmOptions.cpp
+++ b/pgadmin/frm/frmOptions.cpp
@@ -81,6 +81,7 @@
 #define chkShowUsersForPrivileges   CTRL_CHECKBOX("chkShowUsersForPrivileges")
 #define txtAutoRowCount             CTRL_TEXT("txtAutoRowCount")
 #define txtIndent                   CTRL_TEXT("txtIndent")
+#define txtAutoSave					CTRL_TEXT("txtAutoSave")
 #define chkSpacesForTabs			CTRL_CHECKBOX("chkSpacesForTabs")
 #define cbCopyQuote					CTRL_COMBOBOX("cbCopyQuote")
 #define cbCopyQuoteChar				CTRL_COMBOBOX("cbCopyQuoteChar")
@@ -278,6 +279,7 @@ frmOptions::frmOptions(frmMain *parent)
 	txtMaxColSize->SetValidator(numval);
 	txtAutoRowCount->SetValidator(numval);
 	txtIndent->SetValidator(numval);
+	txtAutoSave->SetValidator(numval);
 	txtHistoryMaxQueries->SetValidator(numval);
 	txtHistoryMaxQuerySize->SetValidator(numval);
 
@@ -290,6 +292,7 @@ frmOptions::frmOptions(frmMain *parent)
 	chkShowUsersForPrivileges->SetValue(settings->GetShowUsersForPrivileges());
 	txtAutoRowCount->SetValue(NumToStr(settings->GetAutoRowCountThreshold()));
 	txtIndent->SetValue(NumToStr(settings->GetIndentSpaces()));
+	txtAutoSave->SetValue(NumToStr(settings->GetMinAutoSave()));
 	chkSpacesForTabs->SetValue(settings->GetSpacesForTabs());
 	cbCopyQuote->SetSelection(settings->GetCopyQuoting());
 	cbCopyQuoteChar->SetValue(settings->GetCopyQuoteChar());
@@ -654,6 +657,7 @@ void frmOptions::OnOK(wxCommandEvent &ev)
 	settings->SetShowUsersForPrivileges(chkShowUsersForPrivileges->GetValue());
 	settings->SetAutoRowCountThreshold(StrToLong(txtAutoRowCount->GetValue()));
 	settings->SetIndentSpaces(StrToLong(txtIndent->GetValue()));
+	settings->SetMinAutoSave(StrToLong(txtAutoSave->GetValue()));
 	settings->SetSpacesForTabs(chkSpacesForTabs->GetValue());
 	settings->SetCopyQuoting(cbCopyQuote->GetCurrentSelection());
 	settings->SetCopyQuoteChar(cbCopyQuoteChar->GetValue());
diff --git a/pgadmin/frm/frmQuery.cpp b/pgadmin/frm/frmQuery.cpp
index 2626240..06c4ff5 100644
--- a/pgadmin/frm/frmQuery.cpp
+++ b/pgadmin/frm/frmQuery.cpp
@@ -91,6 +91,7 @@
 // a simple flag will suffice.
 // Required because the pgScript parser isn't currently thread-safe :-(
 bool    frmQuery::ms_pgScriptRunning = false;
+wxString frmQuery::openLostFiles;
 
 BEGIN_EVENT_TABLE(frmQuery, pgFrame)
 	EVT_ERASE_BACKGROUND(           frmQuery::OnEraseBackground)
@@ -158,6 +159,7 @@ BEGIN_EVENT_TABLE(frmQuery, pgFrame)
 	EVT_AUI_PANE_CLOSE(             frmQuery::OnAuiUpdate)
 	EVT_TIMER(CTL_TIMERSIZES,       frmQuery::OnAdjustSizesTimer)
 	EVT_TIMER(CTL_TIMERFRM,         frmQuery::OnTimer)
+	EVT_TIMER(CTL_TIMERAUTOSAVE,    frmQuery::OnTimerAutoSave)
 // These fire when the queries complete
 	EVT_MENU(QUERY_COMPLETE,        frmQuery::OnQueryComplete)
 	EVT_MENU(PGSCRIPT_COMPLETE,     frmQuery::OnScriptComplete)
@@ -210,21 +212,23 @@ private:
 };
 
 
-frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const wxString &query, const wxString &file)
+frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const wxString &query, const wxString &file, const wxString &autoSaveQuery)
 	: pgFrame(NULL, _title),
 	  timer(this, CTL_TIMERFRM),
+	  timerAutoSave(this, CTL_TIMERAUTOSAVE),
 	  pgScript(new pgsApplication(_conn)),
 	  pgsStringOutput(&pgsOutputString),
 	  pgsOutput(pgsStringOutput, wxEOL_UNIX),
 	  pgsTimer(new pgScriptTimer(this))
 {
 	pgScript->SetCaller(this, PGSCRIPT_COMPLETE);
-
+		
 	mainForm = form;
 	conn = _conn;
 
 	loading = true;
 	closing = false;
+	userAnswer = UNDEFINED;
 
 	dlgName = wxT("frmQuery");
 	recentKey = wxT("RecentFiles");
@@ -599,7 +603,70 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w
 	settings->Read(wxT("frmQuery/ShowLineNumber"), &bVal, false);
 	viewMenu->Check(MNU_SHOWLINENUMBER, bVal);
 
-	if (!file.IsEmpty() && wxFileName::FileExists(file))
+	if (!wxFileName::DirExists(AUTOSAVE_DIR))
+		wxFileName::Mkdir(AUTOSAVE_DIR);
+
+	wxDateTime autoSaveDateTime = wxDateTime::Now();
+	if (autoSaveQuery == wxEmptyString)
+	{
+		autoSaveFileName.SetPath(AUTOSAVE_DIR);
+		autoSaveFileName.SetFullName(autoSaveDateTime.Format(wxT("%Y-%m-%d %H-%M-%S")) + AUTOSAVE_EXT);
+	}
+	else if (file != wxEmptyString)
+		autoSaveFileName = file;
+
+	wxString f = wxFindFirstFile(AUTOSAVE_DIR + wxT("*") + AUTOSAVE_EXT);
+	wxString lastOpenFile;
+
+	if (!f.IsEmpty() && autoSaveQuery == wxEmptyString)
+	{
+		wxString line, database, user, host;
+		long port;
+		bool isOK;
+
+		while (!f.IsEmpty() && f != lastOpenFile && openLostFiles.Find(f) == wxNOT_FOUND)
+		{
+			isOK = false;
+			// Analyzing file-by-file.
+			wxTextFile lostFile(f);
+
+			lostFile.Open();
+			// retrieving the metadatas of file
+			database = lostFile.GetLine(AUTOSAVE_LINE_DATABASE);
+			user = lostFile.GetLine(AUTOSAVE_LINE_USER);
+			host = lostFile.GetLine(AUTOSAVE_LINE_HOST);
+			lostFile.GetLine(AUTOSAVE_LINE_PORT).ToLong(&port);
+				
+			if ((conn->GetDbname() == database) && (conn->GetUser() == user) && (conn->GetHost() == host) && (conn->GetPort() == port))
+			{
+				isOK = true;
+
+				if (userAnswer == UNDEFINED)
+				{
+					if (wxMessageBox(wxT("The pgAdmin found one or more files that was lost by some problem. Do you want to retrieve all of them?"), wxT("Warning"), wxYES_NO | wxICON_QUESTION ) == wxYES)
+						userAnswer = ANSWER_YES;
+					else
+						userAnswer = ANSWER_NO;
+				}	
+			}
+
+			lostFile.Close();
+			
+			if (userAnswer == ANSWER_YES && isOK)
+			{
+				lastOpenFile = f;
+				openLostFile(f);
+				openLostFiles += f + wxT(" ");
+			}
+			else if (userAnswer == ANSWER_NO && isOK)
+				removeAutoSaveFile(f);
+
+			f = wxFindNextFile();
+		}
+
+		sqlQuery->SetText(query);
+	}
+	else if (autoSaveQuery == wxEmptyString && !file.IsEmpty() && wxFileName::FileExists(file))
 	{
 		wxFileName fn = file;
 		lastFilename = fn.GetFullName();
@@ -608,7 +675,7 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w
 		OpenLastFile();
 	}
 	else
-		sqlQuery->SetText(query);
+		sqlQuery->SetText(autoSaveQuery != wxEmptyString ? autoSaveQuery : query);
 
 	sqlQuery->Colourise(0, query.Length());
 
@@ -620,6 +687,10 @@ frmQuery::frmQuery(frmMain *form, const wxString &_title, pgConn *_conn, const w
 	queryMenu->Enable(MNU_CLEARHISTORY, false);
 	setTools(false);
 	lastFileFormat = settings->GetUnicodeFile();
+	if (autoSaveQuery == wxEmptyString)
+		openLostFiles += autoSaveFileName.GetFullPath() + wxT(" "); 
+	// Start the autosave.
+	timerAutoSave.Start(settings->GetMinAutoSave() * 60000);
 
 	// Note that under GTK+, SetMaxLength() function may only be used with single line text controls.
 	// (see http://docs.wxwidgets.org/2.8/wx_wxtextctrl.html#wxtextctrlsetmaxlength)
@@ -684,6 +755,9 @@ frmQuery::~frmQuery()
 		delete pgScript;
 		pgScript = NULL;
 	}
+	
+	// Delete autosave file
+	removeAutoSaveFile(autoSaveFileName.GetFullPath());
 
 	if (mainForm)
 		mainForm->RemoveFrame(this);
@@ -907,6 +981,35 @@ void frmQuery::Go()
 	loading = false;
 }
 
+void frmQuery::openLostFile(wxString fileName)
+{
+	wxString line, query;
+	int firstLine = AUTOSAVE_LINE_PORT + 1;
+	wxTextFile lostFile(fileName);
+
+	lostFile.Open();
+
+	lostFile.GoToLine(firstLine);
+	for (line = lostFile.GetLine(firstLine); !lostFile.Eof(); line = lostFile.GetNextLine())
+		query += line + wxT("\n");
+
+	frmQuery *fq = new frmQuery(mainForm, wxEmptyString, conn->Duplicate(), wxEmptyString , fileName, query);
+	if (mainForm)
+		mainForm->AddFrame(fq);
+	fq->Go();
+
+	lostFile.Close();
+}
+
+void frmQuery::removeAutoSaveFile(wxString fileName)
+{
+	if (wxFileName::FileExists(fileName))
+		wxRemoveFile(fileName);
+
+	if (openLostFiles.Find(fileName) != wxNOT_FOUND)
+		openLostFiles.erase(openLostFiles.Find(fileName), fileName.Len() + 1);
+
+}
 
 typedef struct __sqltokenhelp
 {
@@ -2797,6 +2900,24 @@ void frmQuery::OnTimer(wxTimerEvent &event)
 	}
 }
 
+
+void frmQuery::OnTimerAutoSave(wxTimerEvent &event)
+{
+	if (wxFileName::DirExists(AUTOSAVE_DIR))
+	{
+		wxTextFile fileAutoSave(autoSaveFileName.GetFullPath());
+		// Saving metadatas.
+		fileAutoSave.InsertLine(conn->GetDbname(), AUTOSAVE_LINE_DATABASE);
+		fileAutoSave.InsertLine(conn->GetUser(), AUTOSAVE_LINE_USER);
+		fileAutoSave.InsertLine(conn->GetHost(), AUTOSAVE_LINE_HOST);
+		fileAutoSave.InsertLine(wxString() << conn->GetPort(), AUTOSAVE_LINE_PORT);
+		// saving the query automatically.
+		fileAutoSave.AddLine(sqlQuery->GetText());
+		fileAutoSave.Write();
+		fileAutoSave.Close();
+	}
+
+}
 // Adjust sizes of GQB components, Located here because need to
 // avoid some issues when implementing inside controller/view Classes
 void frmQuery::adjustGQBSizes()
diff --git a/pgadmin/include/frm/frmQuery.h b/pgadmin/include/frm/frmQuery.h
index e9de7f6..93998ff 100644
--- a/pgadmin/include/frm/frmQuery.h
+++ b/pgadmin/include/frm/frmQuery.h
@@ -36,6 +36,10 @@
 //
 #define FRMQUERY_PERSPECTIVE_VER wxT("8317")
 
+// used in the autosave feature
+const wxString AUTOSAVE_DIR = wxT("autosave/");
+const wxString AUTOSAVE_EXT = wxT(".pga");
+
 #ifdef __WXMAC__
 #define FRMQUERY_DEFAULT_PERSPECTIVE wxT("layout2|name=toolBar;caption=Tool bar;state=16788208;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=415;besth=23;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=databaseBar;caption=Database bar;state=16788208;dir=1;layer=10;row=0;pos=396;prop=100000;bestw=300;besth=21;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=sqlQuery;caption=SQL query;state=17404;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=350;besth=200;minw=200;minh=100;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=outputPane;caption=Output pane;state=16779260;dir=3;layer=0;row=0;pos=0;prop=100000;bestw=550;besth=300;minw=200;minh=100;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=scratchPad;caption=Scratch pad;state=16779260;dir=2;layer=0;row=0;pos=0;prop=100000;bestw=250;besth=200;minw=100;minh=100;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,10,0)=25|dock_size(5,0,0)=200|dock_size(3,0,0)=290|dock_size(2,0,0)=255|")
 #else
@@ -74,7 +78,7 @@ public:
 class frmQuery : public pgFrame
 {
 public:
-	frmQuery(frmMain *form, const wxString &_title, pgConn *conn, const wxString &qry, const wxString &file = wxEmptyString);
+	frmQuery(frmMain *form, const wxString &_title, pgConn *conn, const wxString &qry, const wxString &file = wxEmptyString, const wxString &autoSaveQuery = wxEmptyString);
 	~frmQuery();
 	void Go();
 
@@ -106,8 +110,8 @@ public:
 	void UpdateAllRecentFiles();
 	void UpdateAllFavouritesList();
 	void UpdateAllMacrosList();
-
 private:
+	
 	frmMain *mainForm;
 	wxAuiManager manager;
 	ctlSQLBox *sqlQuery;
@@ -126,6 +130,13 @@ private:
 	wxTimer timer;
 	wxLongLong elapsedQuery, startTimeQuery;
 
+	//Auto save related
+	wxTimer timerAutoSave;
+	wxFileName autoSaveFileName;
+	void openLostFile(wxString fileName);
+	void removeAutoSaveFile(wxString fileName);
+	int userAnswer;
+
 	// pgScript interface
 	pgsApplication *pgScript;
 	wxString pgsOutputString;
@@ -218,6 +229,7 @@ private:
 	void OnDeleteAll(wxCommandEvent &event);
 
 	void OnTimer(wxTimerEvent &event);
+	void OnTimerAutoSave(wxTimerEvent &event);
 
 	void OpenLastFile();
 	void updateMenu(wxObject *obj = 0);
@@ -262,7 +274,7 @@ private:
 	// A simple mutex-like flag to prevent concurrent script execution.
 	// Required because the pgScript parser isn't currently thread-safe :-(
 	static bool    ms_pgScriptRunning;
-
+	static wxString openLostFiles;
 	DECLARE_EVENT_TABLE()
 };
 
@@ -291,7 +303,23 @@ enum
     CTL_SQLQUERYCBOX,
     CTL_DELETECURRENTBTN,
     CTL_DELETEALLBTN,
-    CTL_SCRATCHPAD
+	CTL_SCRATCHPAD,
+	CTL_TIMERAUTOSAVE
+};
+
+enum
+{
+	AUTOSAVE_LINE_DATABASE,
+	AUTOSAVE_LINE_USER,
+	AUTOSAVE_LINE_HOST,
+	AUTOSAVE_LINE_PORT
+};
+
+enum
+{
+	UNDEFINED,
+	ANSWER_YES,
+	ANSWER_NO
 };
 
 ///////////////////////////////////////////////////////
diff --git a/pgadmin/include/utils/sysSettings.h b/pgadmin/include/utils/sysSettings.h
index 69ecdd4..d8bde35 100644
--- a/pgadmin/include/utils/sysSettings.h
+++ b/pgadmin/include/utils/sysSettings.h
@@ -339,6 +339,16 @@ public:
 	{
 		WriteLong(wxT("IndentSpaces"), newval);
 	}
+	long GetMinAutoSave() const
+	{
+		long l;
+		Read(wxT("MinAutoSave"), &l, 10L);
+		return l;
+	}
+	void SetMinAutoSave(const long newval)
+	{
+		WriteLong(wxT("MinAutoSave"), newval);
+	}
 	bool GetIndicateNull() const
 	{
 		bool b;
diff --git a/pgadmin/pgAdmin3.cpp b/pgadmin/pgAdmin3.cpp
index 2e2cbc5..0cfc345 100644
--- a/pgadmin/pgAdmin3.cpp
+++ b/pgadmin/pgAdmin3.cpp
@@ -80,6 +80,7 @@
 #define PLUGINS_DIR   wxT("/plugins.d")
 #define SETTINGS_INI  wxT("/settings.ini")
 
+
 // Globals
 frmMain *winMain = 0;
 wxThread *updateThread = 0;
diff --git a/pgadmin/ui/frmOptions.xrc b/pgadmin/ui/frmOptions.xrc
index cf136c6..67050a6 100644
--- a/pgadmin/ui/frmOptions.xrc
+++ b/pgadmin/ui/frmOptions.xrc
@@ -445,6 +445,18 @@
                   <border>4</border>
                 </object>
                 <object class="sizeritem">
+                  <object class="wxStaticText" name="stAutoSave">
+                    <label>Minutes to save the document automatically</label>
+                  </object>
+                  <flag>wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="txtAutoSave"/>
+                  <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
                   <object class="wxStaticText" name="stSpacesForTabs">
                     <label>Use spaces instead of tabs</label>
                   </object>
-- 
1.7.4.msysgit.0
