Patch file for multiple SQL tabs is attached. Thank you
On Mon, Feb 1, 2016 at 3:01 AM, Dave Page <dp...@pgadmin.org> wrote: > On Sun, Jan 31, 2016 at 7:03 PM, Sergey Busel <sbu...@gmail.com> 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 (pgadmin-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgadmin-hackers