Patch file for multiple SQL tabs is attached.
Thank you
On Mon, Feb 1, 2016 at 3:01 AM, Dave Page <[email protected]> wrote:
> On Sun, Jan 31, 2016 at 7:03 PM, Sergey Busel <[email protected]> wrote:
> > Hello,
> > It's my first time here and I hope I am sending this to the right mailing
> > list.
> >
> > I am starting to use pgAdmin 3 more and more nowadays and thought that
> > having a multi tabbed interface would be beneficial especially that most
> (if
> > not all) similar tools go that route. Somewhere in the mail archives I've
> > read that tabs are not being implemented because too much code would
> need to
> > be changed. But I thought I'd give it a try anyway.
> >
> > I have implemented SQL tabs in the 1.22.0 version and have been using it
> for
> > a week. Works just fine for me, so I wonder if there is any interest in
> > adding this feature to pgAdmin 3.
> >
> > Brief overview:
> > The changes to existing code were kept to a minimum.
> > All SQL tabs still operate on the same connection and share the same
> result
> > pane.
> > Each tab has it's own dirty flag, EOL style, and file from/to which it
> was
> > loaded/saved.
> >
> > Let me know if anybody is interested.
>
> Feel free to send the patch, and we'll take a look.
>
> Thanks!
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff -aur --exclude-from expats /home/sergey/c/pgadmin3-1.22.0/pgadmin/ctl/ctlSQLBox.cpp /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/ctl/ctlSQLBox.cpp
--- /home/sergey/c/pgadmin3-1.22.0/pgadmin/ctl/ctlSQLBox.cpp 2016-01-04 07:03:33.000000000 -0600
+++ /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/ctl/ctlSQLBox.cpp 2016-01-31 12:09:37.689549967 -0600
@@ -184,6 +184,36 @@
m_database = db;
}
+void ctlSQLBox::SetChanged(bool b)
+{
+ m_changed = b;
+}
+
+bool ctlSQLBox::IsChanged()
+{
+ return m_changed;
+}
+
+void ctlSQLBox::SetOrigin(int origin)
+{
+ m_origin = origin;
+}
+
+int ctlSQLBox::GetOrigin()
+{
+ return m_origin;
+}
+
+void ctlSQLBox::SetFilename(wxString &filename)
+{
+ m_filename = filename;
+}
+
+wxString ctlSQLBox::GetFilename()
+{
+ return m_filename;
+}
+
void ctlSQLBox::OnSearchReplace(wxCommandEvent &ev)
{
if (!m_dlgFindReplace)
diff -aur --exclude-from expats /home/sergey/c/pgadmin3-1.22.0/pgadmin/frm/frmQuery.cpp /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/frm/frmQuery.cpp
--- /home/sergey/c/pgadmin3-1.22.0/pgadmin/frm/frmQuery.cpp 2016-01-04 07:03:33.000000000 -0600
+++ /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/frm/frmQuery.cpp 2016-02-01 20:37:52.061428472 -0600
@@ -84,6 +84,7 @@
#define CTRLID_CONNECTION 4200
#define CTRLID_DATABASELABEL 4201
+#define CTL_SQLQUERYBOOK 4202
#define XML_FROM_WXSTRING(s) ((const xmlChar *)(const char *)s.mb_str(wxConvUTF8))
#define WXSTRING_FROM_XML(s) wxString((char *)s, wxConvUTF8)
@@ -106,6 +107,9 @@
EVT_MENU(MNU_OPEN, frmQuery::OnOpen)
EVT_MENU(MNU_SAVE, frmQuery::OnSave)
EVT_MENU(MNU_SAVEAS, frmQuery::OnSaveAs)
+ // Handlers to add and remove SQL tabs
+ EVT_MENU(MNU_NEWSQLTAB, frmQuery::OnSqlBookAddPage)
+ EVT_MENU(MNU_CLOSESQLTAB, frmQuery::OnSqlBookRemovePage)
EVT_MENU(MNU_EXPORT, frmQuery::OnExport)
EVT_MENU(MNU_SAVEAS_IMAGE_GQB, frmQuery::SaveExplainAsImage)
EVT_MENU(MNU_SAVEAS_IMAGE_EXPLAIN, frmQuery::SaveExplainAsImage)
@@ -170,6 +174,9 @@
EVT_PGQUERYRESULT(QUERY_COMPLETE, frmQuery::OnQueryComplete)
EVT_MENU(PGSCRIPT_COMPLETE, frmQuery::OnScriptComplete)
EVT_AUINOTEBOOK_PAGE_CHANGED(CTL_NTBKCENTER, frmQuery::OnChangeNotebook)
+ // sqlQueryBook event handlers
+ EVT_AUINOTEBOOK_PAGE_CHANGED(CTL_SQLQUERYBOOK, frmQuery::OnSqlBookPageChanged)
+ EVT_AUINOTEBOOK_PAGE_CHANGING(CTL_SQLQUERYBOOK, frmQuery::OnSqlBookPageChanging)
EVT_SPLITTER_SASH_POS_CHANGED(GQB_HORZ_SASH, frmQuery::OnResizeHorizontally)
EVT_BUTTON(CTL_DELETECURRENTBTN, frmQuery::OnDeleteCurrent)
EVT_BUTTON(CTL_DELETEALLBTN, frmQuery::OnDeleteAll)
@@ -235,7 +242,7 @@
loading = true;
closing = false;
- origin = ORIGIN_MANUAL;
+ //origin = ORIGIN_MANUAL;
dlgName = wxT("frmQuery");
recentKey = wxT("RecentFiles");
@@ -263,6 +270,12 @@
saveasImageMenu->Append(MNU_SAVEAS_IMAGE_GQB, _("Graphical Query (image)"), _("Save Graphical Query as an image"));
saveasImageMenu->Append(MNU_SAVEAS_IMAGE_EXPLAIN, _("Explain (image)"), _("Save output of Explain as an image"));
fileMenu->Append(wxID_ANY, _("Save as"), saveasImageMenu);
+
+ // SQL tabs related menu items
+ fileMenu->AppendSeparator();
+ fileMenu->Append(MNU_NEWSQLTAB, _("New SQL &tab"), _("Open a new query tab"));
+ fileMenu->Append(MNU_CLOSESQLTAB, _("&Close SQL tab"), _("Close the current query tab"));
+
fileMenu->AppendSeparator();
fileMenu->Append(MNU_EXPORT, _("&Export..."), _("Export data to file"));
fileMenu->Append(MNU_QUICKREPORT, _("&Quick report..."), _("Run a quick report..."));
@@ -495,12 +508,20 @@
wxBoxSizer *boxSQL = new wxBoxSizer(wxHORIZONTAL);
// Query box
- sqlQuery = new ctlSQLBox(pnlQuery, CTL_SQLQUERY, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSIMPLE_BORDER | wxTE_RICH2);
- sqlQuery->SetDatabase(conn);
- sqlQuery->SetMarginWidth(1, 16);
- sqlQuery->SetDropTarget(new DnDFile(this));
- SetEOLModeDisplay(sqlQuery->GetEOLMode());
- boxSQL->Add(sqlQuery, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1);
+ //sqlQuery = new ctlSQLBox(pnlQuery, CTL_SQLQUERY, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSIMPLE_BORDER | wxTE_RICH2);
+ //sqlQuery->SetDatabase(conn);
+ //sqlQuery->SetMarginWidth(1, 16);
+ //sqlQuery->SetDropTarget(new DnDFile(this));
+ //SetEOLModeDisplay(sqlQuery->GetEOLMode());
+
+ // Use sqlQueryBook instead of sqlQuery and put the book inside boxSQL sizer.
+ // We don't add any SQL tabs until all menu items are initialized from settings.
+ sqlQueryBook = new ctlAuiNotebook(pnlQuery, CTL_SQLQUERYBOOK, wxDefaultPosition, wxDefaultSize, wxAUI_NB_TOP | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_WINDOWLIST_BUTTON);
+ sqlQueryCounter = 0;
+ sqlQueryExec = NULL;
+
+ //boxSQL->Add(sqlQuery, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1);
+ boxSQL->Add(sqlQueryBook, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1);
boxQuery->Add(boxSQL, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1);
@@ -535,8 +556,10 @@
outputPane->AddPage(msgResult, _("Messages"));
outputPane->AddPage(msgHistory, _("History"));
- sqlQuery->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
- sqlQuery->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+ // Moved to SqlBookAddPage()
+ //sqlQuery->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+ //sqlQuery->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+
sqlResult->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
msgResult->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
msgHistory->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
@@ -586,47 +609,50 @@
// Auto indent
settings->Read(wxT("frmQuery/AutoIndent"), &bVal, true);
editMenu->Check(MNU_AUTOINDENT, bVal);
- if (bVal)
- sqlQuery->SetAutoIndent(true);
- else
- sqlQuery->SetAutoIndent(false);
+ //if (bVal)
+ // sqlQuery->SetAutoIndent(true);
+ //else
+ // sqlQuery->SetAutoIndent(false);
// Word wrap
settings->Read(wxT("frmQuery/WordWrap"), &bVal, false);
viewMenu->Check(MNU_WORDWRAP, bVal);
- if (bVal)
- sqlQuery->SetWrapMode(wxSTC_WRAP_WORD);
- else
- sqlQuery->SetWrapMode(wxSTC_WRAP_NONE);
+ //if (bVal)
+ // sqlQuery->SetWrapMode(wxSTC_WRAP_WORD);
+ //else
+ // sqlQuery->SetWrapMode(wxSTC_WRAP_NONE);
// Indent Guides
settings->Read(wxT("frmQuery/ShowIndentGuides"), &bVal, false);
viewMenu->Check(MNU_SHOWINDENTGUIDES, bVal);
- if (bVal)
- sqlQuery->SetIndentationGuides(true);
- else
- sqlQuery->SetIndentationGuides(false);
+ //if (bVal)
+ // sqlQuery->SetIndentationGuides(true);
+ //else
+ // sqlQuery->SetIndentationGuides(false);
// Whitespace
settings->Read(wxT("frmQuery/ShowWhitespace"), &bVal, false);
viewMenu->Check(MNU_SHOWWHITESPACE, bVal);
- if (bVal)
- sqlQuery->SetViewWhiteSpace(wxSTC_WS_VISIBLEALWAYS);
- else
- sqlQuery->SetViewWhiteSpace(wxSTC_WS_INVISIBLE);
+ //if (bVal)
+ // sqlQuery->SetViewWhiteSpace(wxSTC_WS_VISIBLEALWAYS);
+ //else
+ // sqlQuery->SetViewWhiteSpace(wxSTC_WS_INVISIBLE);
// Line ends
settings->Read(wxT("frmQuery/ShowLineEnds"), &bVal, false);
viewMenu->Check(MNU_SHOWLINEENDS, bVal);
- if (bVal)
- sqlQuery->SetViewEOL(1);
- else
- sqlQuery->SetViewEOL(0);
+ //if (bVal)
+ // sqlQuery->SetViewEOL(1);
+ //else
+ // sqlQuery->SetViewEOL(0);
// Line number
settings->Read(wxT("frmQuery/ShowLineNumber"), &bVal, false);
viewMenu->Check(MNU_SHOWLINENUMBER, bVal);
+ // Create the SQL box. After this, sqlQuery variable can be used.
+ SqlBookAddPage();
+
if (!file.IsEmpty() && wxFileName::FileExists(file))
{
wxFileName fn = file;
@@ -641,8 +667,10 @@
sqlQuery->SetText(query);
sqlQuery->Colourise(0, query.Length());
wxSafeYield(); // needed to process sqlQuery modify event
- changed = false;
- origin = ORIGIN_INITIAL;
+ //changed = false;
+ //origin = ORIGIN_INITIAL;
+ sqlQuery->SetChanged(false);
+ sqlQuery->SetOrigin(ORIGIN_INITIAL);
/* _title if not empty should contain displayName of base object for the query.
It's pretty good for a proposed filename if the user chooses to Save As. */
lastFilename = _title;
@@ -860,10 +888,11 @@
settings->WriteBool(wxT("frmQuery/AutoIndent"), editMenu->IsChecked(MNU_AUTOINDENT));
- if (editMenu->IsChecked(MNU_AUTOINDENT))
- sqlQuery->SetAutoIndent(true);
- else
- sqlQuery->SetAutoIndent(false);
+ SqlBookSetAutoIndent(event.IsChecked());
+ //if (editMenu->IsChecked(MNU_AUTOINDENT))
+ // sqlQuery->SetAutoIndent(true);
+ //else
+ // sqlQuery->SetAutoIndent(false);
}
@@ -873,10 +902,11 @@
settings->WriteBool(wxT("frmQuery/WordWrap"), viewMenu->IsChecked(MNU_WORDWRAP));
- if (viewMenu->IsChecked(MNU_WORDWRAP))
- sqlQuery->SetWrapMode(wxSTC_WRAP_WORD);
- else
- sqlQuery->SetWrapMode(wxSTC_WRAP_NONE);
+ SqlBookSetWrapMode(event.IsChecked());
+ //if (viewMenu->IsChecked(MNU_WORDWRAP))
+ // sqlQuery->SetWrapMode(wxSTC_WRAP_WORD);
+ //else
+ // sqlQuery->SetWrapMode(wxSTC_WRAP_NONE);
}
@@ -886,10 +916,11 @@
settings->WriteBool(wxT("frmQuery/ShowIndentGuides"), viewMenu->IsChecked(MNU_SHOWINDENTGUIDES));
- if (viewMenu->IsChecked(MNU_SHOWINDENTGUIDES))
- sqlQuery->SetIndentationGuides(true);
- else
- sqlQuery->SetIndentationGuides(false);
+ SqlBookSetIndentGuides(event.IsChecked());
+ //if (viewMenu->IsChecked(MNU_SHOWINDENTGUIDES))
+ // sqlQuery->SetIndentationGuides(true);
+ //else
+ // sqlQuery->SetIndentationGuides(false);
}
@@ -899,10 +930,11 @@
settings->WriteBool(wxT("frmQuery/ShowWhitespace"), viewMenu->IsChecked(MNU_SHOWWHITESPACE));
- if (viewMenu->IsChecked(MNU_SHOWWHITESPACE))
- sqlQuery->SetViewWhiteSpace(wxSTC_WS_VISIBLEALWAYS);
- else
- sqlQuery->SetViewWhiteSpace(wxSTC_WS_INVISIBLE);
+ SqlBookSetViewWhiteSpace(event.IsChecked());
+ //if (viewMenu->IsChecked(MNU_SHOWWHITESPACE))
+ // sqlQuery->SetViewWhiteSpace(wxSTC_WS_VISIBLEALWAYS);
+ //else
+ // sqlQuery->SetViewWhiteSpace(wxSTC_WS_INVISIBLE);
}
@@ -912,10 +944,11 @@
settings->WriteBool(wxT("frmQuery/ShowLineEnds"), viewMenu->IsChecked(MNU_SHOWLINEENDS));
- if (viewMenu->IsChecked(MNU_SHOWLINEENDS))
- sqlQuery->SetViewEOL(1);
- else
- sqlQuery->SetViewEOL(0);
+ SqlBookSetViewEOL(event.IsChecked());
+ //if (viewMenu->IsChecked(MNU_SHOWLINEENDS))
+ // sqlQuery->SetViewEOL(1);
+ //else
+ // sqlQuery->SetViewEOL(0);
}
@@ -925,7 +958,8 @@
settings->WriteBool(wxT("frmQuery/ShowLineNumber"), viewMenu->IsChecked(MNU_SHOWLINENUMBER));
- sqlQuery->UpdateLineNumber();
+ SqlBookSetViewLineNumbers(event.IsChecked());
+ //sqlQuery->UpdateLineNumber();
}
void frmQuery::OnActivate(wxActivateEvent &event)
@@ -1080,6 +1114,7 @@
else
{
conn = (pgConn *)cbConnection->GetClientData(sel);
+ SqlBookSetDatabase(conn);
sqlResult->SetConnection(conn);
pgScript->SetConnection(conn);
title = wxT("Query - ") + cbConnection->GetValue();
@@ -1421,17 +1456,22 @@
void frmQuery::setExtendedTitle()
{
wxString chgStr;
- if (changed)
+ //if (changed)
+ if (sqlQuery->IsChanged())
chgStr = wxT(" *");
- if (lastPath.IsNull())
+ wxString filename = sqlQuery->GetFilename();
+ //if (lastPath.IsNull())
+ if (filename.IsNull())
SetTitle(title + chgStr);
else
{
- SetTitle(title + wxT(" - [") + lastPath + wxT("]") + chgStr);
+ //SetTitle(title + wxT(" - [") + lastPath + wxT("]") + chgStr);
+ SetTitle(title + wxT(" - [") + filename + wxT("]") + chgStr);
}
// Allow to save initial queries though they are not changed
- bool enableSave = changed || (origin == ORIGIN_INITIAL);
+ //bool enableSave = changed || (origin == ORIGIN_INITIAL);
+ bool enableSave = sqlQuery->IsChanged() || (sqlQuery->GetOrigin() == ORIGIN_INITIAL);
toolBar->EnableTool(MNU_SAVE, enableSave);
fileMenu->Enable(MNU_SAVE, enableSave);
}
@@ -1668,11 +1708,14 @@
bool frmQuery::CheckChanged(bool canVeto)
{
- if (changed && settings->GetAskSaveConfirmation())
+ //if (changed && settings->GetAskSaveConfirmation())
+ if (sqlQuery->IsChanged() && settings->GetAskSaveConfirmation())
{
- wxString fn;
- if (!lastPath.IsNull())
- fn = wxString::Format(_("The text in file %s has changed.\nDo you want to save changes?"), lastPath.c_str());
+ wxString fn, filename;
+ filename = sqlQuery->GetFilename();
+ //if (!lastPath.IsNull())
+ if (!filename.IsNull())
+ fn = wxString::Format(_("The text in file %s has changed.\nDo you want to save changes?"), filename.c_str() /*lastPath.c_str()*/);
else
fn = _("The text has changed.\nDo you want to save changes?");
wxMessageDialog msg(this, fn, _("Query"),
@@ -1683,12 +1726,14 @@
switch (msg.ShowModal())
{
case wxID_YES:
- if (lastPath.IsNull())
+ //if (lastPath.IsNull())
+ if (filename.IsNull())
OnSaveAs(noEvent);
else
OnSave(noEvent);
- return changed;
+ //return changed;
+ return sqlQuery->IsChanged();
case wxID_CANCEL:
return true;
@@ -1732,7 +1777,8 @@
return;
}
- if (CheckChanged(event.CanVeto()) && event.CanVeto())
+ //if (CheckChanged(event.CanVeto()) && event.CanVeto())
+ if (SqlBookClose(event.CanVeto()) && event.CanVeto())
{
event.Veto();
return;
@@ -1749,7 +1795,7 @@
Hide();
- sqlQuery->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+ //sqlQuery->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
sqlResult->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
msgResult->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
msgHistory->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
@@ -1776,14 +1822,18 @@
// Sometimes there come events 20 and 520 AFTER the initial query was set by constructor.
// Their occurrence is related to query's size and possibly international characters in it (??)
// Filter them out to keep "initial" origin of query text.
- (origin != ORIGIN_INITIAL || (event.m_modificationType != 20 && event.m_modificationType != 520)))
+ //(origin != ORIGIN_INITIAL || (event.m_modificationType != 20 && event.m_modificationType != 520)))
+ (sqlQuery->GetOrigin() != ORIGIN_INITIAL || (event.m_modificationType != 20 && event.m_modificationType != 520)))
{
// This is the default change origin.
// In other cases the changer function will reset it after this event.
- origin = ORIGIN_MANUAL;
- if (!changed)
+ //origin = ORIGIN_MANUAL;
+ sqlQuery->SetOrigin(ORIGIN_MANUAL);
+ //if (!changed)
+ if (!sqlQuery->IsChanged())
{
- changed = true;
+ //changed = true;
+ sqlQuery->SetChanged(true);
setExtendedTitle();
}
}
@@ -1826,8 +1876,11 @@
sqlQuery->Colourise(0, str.Length());
sqlQuery->EmptyUndoBuffer();
wxSafeYield(); // needed to process sqlQuery modify event
- changed = false;
- origin = ORIGIN_FILE;
+ //changed = false;
+ //origin = ORIGIN_FILE;
+ sqlQuery->SetFilename(lastPath);
+ sqlQuery->SetChanged(false);
+ sqlQuery->SetOrigin(ORIGIN_FILE);
setExtendedTitle();
SetLineEndingStyle();
UpdateRecentFiles(true);
@@ -1881,26 +1934,31 @@
void frmQuery::OnSave(wxCommandEvent &event)
{
bool modeUnicode = settings->GetUnicodeFile();
+ wxString filename = sqlQuery->GetFilename();
- if (lastPath.IsNull())
+ //if (lastPath.IsNull())
+ if (filename.IsNull())
{
OnSaveAs(event);
return;
}
- wxUtfFile file(lastPath, wxFile::write, modeUnicode ? wxFONTENCODING_UTF8 : wxFONTENCODING_DEFAULT);
+ //wxUtfFile file(lastPath, wxFile::write, modeUnicode ? wxFONTENCODING_UTF8 : wxFONTENCODING_DEFAULT);
+ wxUtfFile file(filename, wxFile::write, modeUnicode ? wxFONTENCODING_UTF8 : wxFONTENCODING_DEFAULT);
if (file.IsOpened())
{
if ((file.Write(sqlQuery->GetText()) == 0) && (!modeUnicode))
wxMessageBox(_("Query text incomplete.\nQuery contained characters that could not be converted to the local charset.\nPlease correct the data or try using UTF8 instead."));
file.Close();
- changed = false;
+ //changed = false;
+ sqlQuery->SetChanged(false);
setExtendedTitle();
UpdateRecentFiles();
}
else
{
- wxLogError(__("Could not write the file %s: Errcode=%d."), lastPath.c_str(), wxSysErrorCode());
+ //wxLogError(__("Could not write the file %s: Errcode=%d."), lastPath.c_str(), wxSysErrorCode());
+ wxLogError(__("Could not write the file %s: Errcode=%d."), filename.c_str(), wxSysErrorCode());
}
}
@@ -1924,7 +1982,8 @@
{
wxMessageBox(_("This file contains mixed line endings. They will be converted to the current setting."), _("Warning"), wxICON_INFORMATION | wxOK);
sqlQuery->ConvertEOLs(mode);
- changed = true;
+ //changed = true;
+ sqlQuery->SetChanged(true);
setExtendedTitle();
updateMenu();
}
@@ -1940,27 +1999,28 @@
// Now set the status text, menu options, and the mode
sqlQuery->SetEOLMode(mode);
- switch(mode)
- {
-
- case wxSTC_EOL_LF:
- lineEndMenu->Check(MNU_LF, true);
- SetStatusText(_("Unix"), STATUSPOS_FORMAT);
- break;
-
- case wxSTC_EOL_CRLF:
- lineEndMenu->Check(MNU_CRLF, true);
- SetStatusText(_("DOS"), STATUSPOS_FORMAT);
- break;
-
- case wxSTC_EOL_CR:
- lineEndMenu->Check(MNU_CR, true);
- SetStatusText(_("Mac"), STATUSPOS_FORMAT);
- break;
-
- default:
- wxLogError(wxT("Someone created a new line ending style! Run, run for your lives!!"));
- }
+// switch(mode)
+// {
+//
+// case wxSTC_EOL_LF:
+// lineEndMenu->Check(MNU_LF, true);
+// SetStatusText(_("Unix"), STATUSPOS_FORMAT);
+// break;
+//
+// case wxSTC_EOL_CRLF:
+// lineEndMenu->Check(MNU_CRLF, true);
+// SetStatusText(_("DOS"), STATUSPOS_FORMAT);
+// break;
+//
+// case wxSTC_EOL_CR:
+// lineEndMenu->Check(MNU_CR, true);
+// SetStatusText(_("Mac"), STATUSPOS_FORMAT);
+// break;
+//
+// default:
+// wxLogError(wxT("Someone created a new line ending style! Run, run for your lives!!"));
+// }
+ SetEOLModeDisplay(mode);
delete reCRLF;
delete reCR;
@@ -1992,12 +2052,16 @@
SetEOLModeDisplay(mode);
- if (!changed)
+ //if (!changed)
+ if (!sqlQuery->IsChanged())
{
- changed = true;
+ //changed = true;
+ sqlQuery->SetChanged(true);
setExtendedTitle();
}
+ setExtendedTitle();
+ // TODO: ??
pgScript->SetConnection(conn);
}
@@ -2031,11 +2095,16 @@
void frmQuery::OnSaveAs(wxCommandEvent &event)
{
+ wxString filename;
+ filename = sqlQuery->GetFilename();
+ if (filename.IsNull())
+ filename = lastFilename;
+
#ifdef __WXMSW__
- wxFileDialog *dlg = new wxFileDialog(this, _("Save query file as"), lastDir, lastFilename,
+ wxFileDialog *dlg = new wxFileDialog(this, _("Save query file as"), lastDir, filename, /*lastFilename,*/
_("Query files (*.sql)|*.sql|All files (*.*)|*.*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#else
- wxFileDialog *dlg = new wxFileDialog(this, _("Save query file as"), lastDir, lastFilename,
+ wxFileDialog *dlg = new wxFileDialog(this, _("Save query file as"), lastDir, filename, /*lastFilename,*/
_("Query files (*.sql)|*.sql|All files (*)|*"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
#endif
if (dlg->ShowModal() == wxID_OK)
@@ -2069,12 +2138,16 @@
if ((file.Write(sqlQuery->GetText()) == 0) && (!lastFileFormat))
wxMessageBox(_("Query text incomplete.\nQuery contained characters that could not be converted to the local charset.\nPlease correct the data or try using UTF8 instead."));
file.Close();
- changed = false;
+ //changed = false;
+ sqlQuery->SetChanged(false);
+ sqlQuery->SetFilename(lastPath);
// Forget about Initial origin thus making "Save" button behave as usual
// (be enabled/disabled according to dirty flag only).
- if (origin == ORIGIN_INITIAL)
- origin = ORIGIN_FILE;
+ //if (origin == ORIGIN_INITIAL)
+ // origin = ORIGIN_FILE;
+ if (sqlQuery->GetOrigin() == ORIGIN_INITIAL)
+ sqlQuery->SetOrigin(ORIGIN_FILE);
setExtendedTitle();
UpdateRecentFiles();
@@ -2257,7 +2330,8 @@
// Only prompt the user if the dirty flag is set, and last modification wasn't from GQB,
// and the textbox is not empty, and the new query is different.
- if(changed && origin != ORIGIN_GQB &&
+ //if(changed && origin != ORIGIN_GQB &&
+ if (sqlQuery->IsChanged() && sqlQuery->GetOrigin() != ORIGIN_GQB &&
!sqlQuery->GetText().Trim().IsEmpty() && sqlQuery->GetText() != newQuery + wxT("\n"))
{
wxString fn;
@@ -2267,7 +2341,8 @@
fn = _("The generated SQL query has changed.\nDo you want to update it?");
wxMessageDialog msg(this, fn, _("Query"), wxYES_NO | wxICON_EXCLAMATION);
- if(msg.ShowModal() == wxID_YES && changed)
+ //if(msg.ShowModal() == wxID_YES && changed)
+ if(msg.ShowModal() == wxID_YES && sqlQuery->IsChanged())
{
canGenerate = true;
}
@@ -2287,8 +2362,10 @@
sqlQuery->Colourise(0, sqlQuery->GetText().Length());
wxSafeYield(); // needed to process sqlQuery modify event
sqlNotebook->SetSelection(0);
- changed = true;
- origin = ORIGIN_GQB;
+ //changed = true;
+ //origin = ORIGIN_GQB;
+ sqlQuery->SetChanged(true);
+ sqlQuery->SetOrigin(ORIGIN_GQB);
setExtendedTitle();
gqbUpdateRunning = false;
@@ -2437,6 +2514,7 @@
}
query.Replace(wxT("$SELECTION$"), selection);
}
+
execQuery(query);
sqlQuery->SetFocus();
}
@@ -2495,7 +2573,8 @@
sqlQuery->StartStyling(0, wxSTC_INDICS_MASK);
sqlQuery->SetStyling(sqlQuery->GetText().Length(), 0);
- if (!changed)
+ //if (!changed)
+ if (!sqlQuery->IsChanged())
setExtendedTitle();
aborted = false;
@@ -2519,6 +2598,9 @@
}
}
+ // Remember the tab from which execute was called. By the time query completes, SQL tab selection may change.
+ sqlQueryExec = sqlQuery;
+
// We must do this lot before the query starts, otherwise
// it might not happen once the main thread gets busy with
// other stuff.
@@ -2852,9 +2934,9 @@
if (!errMsg2.IsEmpty())
showMessage(errMsg2);
- if (errPos > 0)
+ if (errPos > 0 && sqlQueryExec != NULL)
{
- int selStart = sqlQuery->GetSelectionStart(), selEnd = sqlQuery->GetSelectionEnd();
+ int selStart = sqlQueryExec->GetSelectionStart(), selEnd = sqlQueryExec->GetSelectionEnd();
if (selStart == selEnd)
selStart = 0;
@@ -2862,29 +2944,30 @@
// Set an indicator on the error word (break on any kind of bracket, a space or full stop)
int sPos = errPos + selStart - 1, wEnd = 1;
- sqlQuery->StartStyling(sPos, wxSTC_INDICS_MASK);
- int c = sqlQuery->GetCharAt(sPos + wEnd);
- size_t len = sqlQuery->GetText().Length();
+ sqlQueryExec->StartStyling(sPos, wxSTC_INDICS_MASK);
+ int c = sqlQueryExec->GetCharAt(sPos + wEnd);
+ size_t len = sqlQueryExec->GetText().Length();
while(c != ' ' && c != '(' && c != '{' && c != '[' && c != '.' &&
(unsigned int)(sPos + wEnd) < len)
{
wEnd++;
- c = sqlQuery->GetCharAt(sPos + wEnd);
+ c = sqlQueryExec->GetCharAt(sPos + wEnd);
}
- sqlQuery->SetStyling(wEnd, wxSTC_INDIC0_MASK);
+ sqlQueryExec->SetStyling(wEnd, wxSTC_INDIC0_MASK);
- int line = 0, maxLine = sqlQuery->GetLineCount();
- while (line < maxLine && sqlQuery->GetLineEndPosition(line) < errPos + selStart + 1)
+ int line = 0, maxLine = sqlQueryExec->GetLineCount();
+ while (line < maxLine && sqlQueryExec->GetLineEndPosition(line) < errPos + selStart + 1)
line++;
if (line < maxLine)
{
- sqlQuery->GotoPos(sPos);
- sqlQuery->MarkerAdd(line, 0);
+ sqlQueryExec->GotoPos(sPos);
+ sqlQueryExec->MarkerAdd(line, 0);
- if (!changed)
+ //if (!changed)
+ if (!sqlQueryExec->IsChanged())
setExtendedTitle();
- sqlQuery->EnsureVisible(line);
+ sqlQueryExec->EnsureVisible(line);
}
}
}
@@ -2981,9 +3064,9 @@
if (sqlResult->RunStatus() == PGRES_TUPLES_OK || sqlResult->RunStatus() == PGRES_COMMAND_OK)
{
// Get the executed query
- wxString executedQuery = sqlQuery->GetSelectedText();
+ wxString executedQuery = sqlQueryExec->GetSelectedText();
if (executedQuery.IsNull())
- executedQuery = sqlQuery->GetText();
+ executedQuery = sqlQueryExec->GetText();
// Same query, but without return feeds and carriage returns
wxString executedQueryWithoutReturns = executedQuery;
@@ -3176,6 +3259,7 @@
updateMenu();
}
+ sqlQueryExec = NULL;
sqlQuery->SetFocus();
}
@@ -3478,8 +3562,10 @@
sqlQuery->SetText(query);
sqlQuery->Colourise(0, query.Length());
wxSafeYield(); // needed to process sqlQuery modify event
- changed = true;
- origin = ORIGIN_HISTORY;
+ //changed = true;
+ //origin = ORIGIN_HISTORY;
+ sqlQuery->SetChanged(true);
+ sqlQuery->SetOrigin(ORIGIN_HISTORY);
setExtendedTitle();
SetLineEndingStyle();
btnDeleteCurrent->Enable(true);
@@ -3523,6 +3609,233 @@
}
}
+// Methods related to SQL tabs //
+
+void frmQuery::OnSqlBookPageChanged(wxAuiNotebookEvent &event)
+{
+ // Try to always keep sqlQuery variable pointing to the currently selected SQLBox.
+ // When closing and removing all tabs, page count may be zero.
+ if (sqlQueryBook->GetPageCount() > 0)
+ {
+ size_t curpage = sqlQueryBook->GetSelection();
+ sqlQuery = (ctlSQLBox *) sqlQueryBook->GetPage(curpage);
+
+ // Update UI with chosen query's info
+ SetEOLModeDisplay(sqlQuery->GetEOLMode());
+ setExtendedTitle();
+ }
+ else
+ {
+ // This should help us find bugs (such as using sqlQuery after closing all tabs) much faster.
+ sqlQuery = NULL;
+ }
+}
+
+void frmQuery::OnSqlBookPageChanging(wxAuiNotebookEvent &event)
+{
+ // Veto event while page change is prohibited.
+ if (!SqlBookCanChangePage())
+ {
+ event.Veto();
+ // Maybe status bar?
+ wxMessageBox(_("Cannot change to selected SQL tab now. Try again a bit later."));
+ }
+}
+
+void frmQuery::OnSqlBookAddPage(wxCommandEvent &event)
+{
+ if (SqlBookCanChangePage())
+ SqlBookAddPage();
+ else
+ wxMessageBox(_("Cannot add a new SQL tab now. Try again a bit later."));
+}
+
+void frmQuery::OnSqlBookRemovePage(wxCommandEvent &event)
+{
+ // Don't allow removal of the last SQL box via user generated event
+ size_t pagecnt = sqlQueryBook->GetPageCount();
+ if (pagecnt == 1)
+ {
+ wxMessageBox(_("Cannot remove the last SQL tab"));
+ return;
+ }
+
+ // Prevent removing the page that is currently being executed
+ if (sqlQueryExec != NULL && sqlQueryExec == sqlQuery)
+ {
+ wxMessageBox(_("The query on this SQL tab is still running.\nWait for it to finish or cancel it before closing the tab."));
+ return;
+ }
+
+ if (CheckChanged(true))
+ return;
+
+ if (!SqlBookRemovePage())
+ wxMessageBox(_("Could not close selected tab"));
+}
+
+bool frmQuery::SqlBookCanChangePage()
+{
+ return !(m_loadingfile || ms_pgScriptRunning);
+}
+
+void frmQuery::SqlBookAddPage()
+{
+ ctlSQLBox *box;
+ wxString caption;
+ bool bVal;
+
+ // All SQL boxes use the same wxID, CTL_SQLQUERY.
+ // This should probably be changed, but it works for now and has minimal impact on existing code.
+ box = new ctlSQLBox(sqlQueryBook, CTL_SQLQUERY, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSIMPLE_BORDER | wxTE_RICH2);
+ box->SetDatabase(conn);
+ box->SetMarginWidth(1, 16);
+ box->SetDropTarget(new DnDFile(this));
+ box->SetChanged(false);
+ box->SetOrigin(ORIGIN_MANUAL);
+
+ bVal = editMenu->IsChecked(MNU_AUTOINDENT);
+ box->SetAutoIndent(bVal);
+
+ bVal = viewMenu->IsChecked(MNU_WORDWRAP);
+ box->SetWrapMode(bVal ? wxSTC_WRAP_WORD : wxSTC_WRAP_NONE);
+
+ bVal = viewMenu->IsChecked(MNU_SHOWINDENTGUIDES);
+ box->SetIndentationGuides(bVal);
+
+ bVal = viewMenu->IsChecked(MNU_SHOWWHITESPACE);
+ box->SetViewWhiteSpace(bVal ? wxSTC_WS_VISIBLEALWAYS : wxSTC_WS_INVISIBLE);
+
+ bVal = viewMenu->IsChecked(MNU_SHOWLINEENDS);
+ box->SetViewEOL(bVal ? 1 : 0);
+
+ box->Connect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+ box->Connect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+
+ sqlQueryCounter ++;
+ caption = _("Query") + wxString::Format(wxT(" %i"), sqlQueryCounter);
+ sqlQueryBook->AddPage(box, caption, true);
+
+ // Probably not needed, as the line above should trigger the PageChange event
+ sqlQuery = box;
+}
+
+bool frmQuery::SqlBookRemovePage()
+{
+ size_t pageidx;
+ if (sqlQueryBook->GetPageCount() > 0)
+ {
+ sqlQuery->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+ sqlQuery->Disconnect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus));
+
+ pageidx = sqlQueryBook->GetSelection();
+ return sqlQueryBook->DeletePage(pageidx);
+ }
+ return false;
+}
+
+void frmQuery::SqlBookSetAutoIndent(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetAutoIndent(b);
+ }
+}
+
+void frmQuery::SqlBookSetWrapMode(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetWrapMode(b ? wxSTC_WRAP_WORD : wxSTC_WRAP_NONE);
+ }
+}
+
+void frmQuery::SqlBookSetIndentGuides(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetIndentationGuides(b);
+ }
+}
+
+void frmQuery::SqlBookSetViewWhiteSpace(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetViewWhiteSpace(b ? wxSTC_WS_VISIBLEALWAYS : wxSTC_WS_INVISIBLE);
+ }
+}
+
+void frmQuery::SqlBookSetViewEOL(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetViewEOL(b ? 1 : 0);
+ }
+}
+
+void frmQuery::SqlBookSetViewLineNumbers(bool b)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->UpdateLineNumber();
+ }
+}
+
+void frmQuery::SqlBookSetDatabase(pgConn *con)
+{
+ size_t i, cnt;
+ ctlSQLBox *box;
+
+ for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i)
+ {
+ box = (ctlSQLBox *) sqlQueryBook->GetPage(i);
+ box->SetDatabase(con);
+ }
+}
+
+// Returns true if any SQL tab attempts to veto the closing, false otherwise.
+bool frmQuery::SqlBookClose(bool canVeto)
+{
+ // Close all tabs one at a time. The deleted tab is always the current one.
+ // This loop relies on the fact that deleting a tab will invoke PageChange event,
+ // which in turn will assign another current tab. Hence, the order in which tabs
+ // are closed is not defined here.
+ while (sqlQueryBook->GetPageCount() > 0)
+ {
+ if (CheckChanged(canVeto))
+ return true;
+
+ // Removing the page should select another page and trigger PageChange event.
+ SqlBookRemovePage();
+ }
+ return false;
+}
+
///////////////////////////////////////////////////////
diff -aur --exclude-from expats /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/ctl/ctlSQLBox.h /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/ctl/ctlSQLBox.h
--- /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/ctl/ctlSQLBox.h 2016-01-04 07:03:33.000000000 -0600
+++ /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/ctl/ctlSQLBox.h 2016-02-01 08:18:21.538647362 -0600
@@ -74,6 +74,14 @@
CharacterRange RegexFindText(int minPos, int maxPos, const wxString &text);
+ // Having multiple SQL tabs warrants the following properties to be tracked per tab
+ void SetChanged(bool b);
+ bool IsChanged();
+ void SetOrigin(int origin);
+ int GetOrigin();
+ void SetFilename(wxString &filename);
+ wxString GetFilename();
+
DECLARE_DYNAMIC_CLASS(ctlSQLBox)
DECLARE_EVENT_TABLE()
@@ -93,6 +101,11 @@
pgConn *m_database;
bool m_autoIndent, m_autocompDisabled;
+ // Variables to track info per SQL box
+ wxString m_filename;
+ bool m_changed;
+ int m_origin;
+
friend class QueryPrintout;
};
diff -aur --exclude-from expats /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/frm/frmQuery.h /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/frm/frmQuery.h
--- /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/frm/frmQuery.h 2016-01-04 07:03:33.000000000 -0600
+++ /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/frm/frmQuery.h 2016-02-01 19:13:00.098158513 -0600
@@ -101,11 +101,13 @@
}
void SetChanged(bool p_changed)
{
- changed = p_changed;
+ //changed = p_changed;
+ sqlQuery->SetChanged(p_changed);
}
void SetOrigin(int p_origin)
{
- origin = p_origin;
+ //origin = p_origin;
+ sqlQuery->SetOrigin(p_origin);
}
void SetLastPath(wxString p_lastpath)
{
@@ -135,6 +137,10 @@
wxButton *btnDeleteAll;
wxArrayString histoQueries;
+ ctlAuiNotebook *sqlQueryBook;
+ size_t sqlQueryCounter;
+ ctlSQLBox *sqlQueryExec; //currently executing SQL tab
+
// Query timing/status update
wxTimer timer;
wxLongLong elapsedQuery, startTimeQuery;
@@ -261,6 +267,24 @@
bool relatesToWindow(wxWindow *which, wxWindow *related);
+ // Methods related to SQL tabs
+ void SqlBookAddPage();
+ bool SqlBookRemovePage();
+ bool SqlBookCanChangePage();
+ void SqlBookSetAutoIndent(bool b);
+ void SqlBookSetWrapMode(bool b);
+ void SqlBookSetIndentGuides(bool b);
+ void SqlBookSetViewWhiteSpace(bool b);
+ void SqlBookSetViewEOL(bool b);
+ void SqlBookSetViewLineNumbers(bool b);
+ void SqlBookSetDatabase(pgConn *con);
+ bool SqlBookClose(bool canVeto);
+ // SQL tabs event handlers
+ void OnSqlBookAddPage(wxCommandEvent &event);
+ void OnSqlBookRemovePage(wxCommandEvent &event);
+ void OnSqlBookPageChanged(wxAuiNotebookEvent &event);
+ void OnSqlBookPageChanging(wxAuiNotebookEvent &event);
+
wxWindow *currentControl();
wxMenu *queryMenu;
wxMenu *favouritesMenu;
@@ -280,7 +304,7 @@
bool m_loadingfile;
// Complements dirty flag, showing last origin of query's modification (see enum ORIGIN_..)
- int origin;
+ //int origin;
// A simple mutex-like flag to prevent concurrent script execution.
// Required because the pgScript parser isn't currently thread-safe :-(
diff -aur --exclude-from expats /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/frm/menu.h /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/frm/menu.h
--- /home/sergey/c/pgadmin3-1.22.0/pgadmin/include/frm/menu.h 2016-01-04 07:03:33.000000000 -0600
+++ /home/sergey/c/pgadmin3-1.22.0-dev/pgadmin/include/frm/menu.h 2016-02-01 08:20:54.148645460 -0600
@@ -44,6 +44,8 @@
MNU_OPEN,
MNU_SAVE,
MNU_SAVEAS,
+ MNU_NEWSQLTAB,
+ MNU_CLOSESQLTAB,
MNU_EXPORT,
MNU_OPTIONS,
MNU_CUT,
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers