- Removed unused/commented code. - Tab names now reflect the file name, if such is associated with a tab. - Removed the "Close Tab" menu item. Added X button to the active tab. - To tell the user which tab results are related to and to avoid messing with perspective, the name of the related tab is now displayed as "Data Output [Query 1]" in the output pane. If the tab is named after a file, file name will display in the square brackets. If the file name is longer then 15 chars, it will be truncated to 15 chars.
Let me know any other suggestions you may have. Thanks. On Tue, Feb 2, 2016 at 5:08 AM, Dave Page <dp...@pgadmin.org> wrote: > On Tue, Feb 2, 2016 at 3:17 AM, Sergey Busel <sbu...@gmail.com> wrote: > > Patch file for multiple SQL tabs is attached. > > Thanks - I see how this could be useful. I think it needs a little > work before it could be included: > > - Unused code should be removed, not commented out. > - I think the tab names should reflect the file name (where there is > one) - e.g. "Query 2" should become "foo.sql" (without the path) if > you save the query. > - There should be some way for the user to see which tab the results > are related to. Maybe the output pane title should read "Output Pane > (Query 2)" or "Output Pane (foo.sql)" as appropriate? I'm not sure if > that will do weird things with the perspective though - they used to > include panel titles. > - Instead of the "Close Tab" menu option, perhaps an X button should > be added to the right-hand end of the active tab? > > 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-02-02 19:54:19.875326175 -0600 @@ -184,6 +184,79 @@ m_database = db; } +void ctlSQLBox::SetChanged(bool b) +{ + if (m_changed != b) + { + m_changed = b; + UpdateTitle(); + } +} + +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; + UpdateTitle(); +} + +wxString ctlSQLBox::GetFilename() +{ + return m_filename; +} + +void ctlSQLBox::SetTitle(wxString &title) +{ + m_title = title; +} + +wxString ctlSQLBox::GetTitle() +{ + return m_title; +} + +wxString ctlSQLBox::GetChangeIndicator() +{ + if (m_changestr.IsEmpty()) + m_changestr = _("*"); + return m_changestr; +} + +void ctlSQLBox::UpdateTitle() +{ + bool hasCh = false; + wxString chStr = GetChangeIndicator(); + wxString title = GetFilename(); + + if (!title.IsEmpty()) + title = wxFileName::FileName(title).GetFullName(); + else + title = GetTitle(); + + hasCh = title.EndsWith(chStr); + + if (IsChanged() && !hasCh) + title = title + chStr; + else if (!IsChanged() && hasCh) + title = title.Mid(0, title.Len() - chStr.Len()); + + SetTitle(title); +} + 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-02 20:49:43.175324704 -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,7 @@ EVT_MENU(MNU_OPEN, frmQuery::OnOpen) EVT_MENU(MNU_SAVE, frmQuery::OnSave) EVT_MENU(MNU_SAVEAS, frmQuery::OnSaveAs) + EVT_MENU(MNU_NEWSQLTAB, frmQuery::OnSqlBookAddPage) EVT_MENU(MNU_EXPORT, frmQuery::OnExport) EVT_MENU(MNU_SAVEAS_IMAGE_GQB, frmQuery::SaveExplainAsImage) EVT_MENU(MNU_SAVEAS_IMAGE_EXPLAIN, frmQuery::SaveExplainAsImage) @@ -170,6 +172,9 @@ EVT_PGQUERYRESULT(QUERY_COMPLETE, frmQuery::OnQueryComplete) EVT_MENU(PGSCRIPT_COMPLETE, frmQuery::OnScriptComplete) EVT_AUINOTEBOOK_PAGE_CHANGED(CTL_NTBKCENTER, frmQuery::OnChangeNotebook) + EVT_AUINOTEBOOK_PAGE_CHANGED(CTL_SQLQUERYBOOK, frmQuery::OnSqlBookPageChanged) + EVT_AUINOTEBOOK_PAGE_CHANGING(CTL_SQLQUERYBOOK, frmQuery::OnSqlBookPageChanging) + EVT_AUINOTEBOOK_PAGE_CLOSE(CTL_SQLQUERYBOOK, frmQuery::OnSqlBookPageClose) EVT_SPLITTER_SASH_POS_CHANGED(GQB_HORZ_SASH, frmQuery::OnResizeHorizontally) EVT_BUTTON(CTL_DELETECURRENTBTN, frmQuery::OnDeleteCurrent) EVT_BUTTON(CTL_DELETEALLBTN, frmQuery::OnDeleteAll) @@ -235,7 +240,6 @@ loading = true; closing = false; - origin = ORIGIN_MANUAL; dlgName = wxT("frmQuery"); recentKey = wxT("RecentFiles"); @@ -263,6 +267,11 @@ 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->AppendSeparator(); fileMenu->Append(MNU_EXPORT, _("&Export..."), _("Export data to file")); fileMenu->Append(MNU_QUICKREPORT, _("&Quick report..."), _("Run a quick report...")); @@ -494,13 +503,13 @@ // This one will contain the SQL box 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); + // 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_CLOSE_ON_ACTIVE_TAB | wxAUI_NB_WINDOWLIST_BUTTON); + sqlQueryCounter = 0; + sqlQueryExec = NULL; + + boxSQL->Add(sqlQueryBook, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1); boxQuery->Add(boxSQL, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1); @@ -535,8 +544,6 @@ 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)); 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 +593,30 @@ // Auto indent settings->Read(wxT("frmQuery/AutoIndent"), &bVal, true); editMenu->Check(MNU_AUTOINDENT, bVal); - 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); // Indent Guides settings->Read(wxT("frmQuery/ShowIndentGuides"), &bVal, false); viewMenu->Check(MNU_SHOWINDENTGUIDES, bVal); - 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); // Line ends settings->Read(wxT("frmQuery/ShowLineEnds"), &bVal, false); viewMenu->Check(MNU_SHOWLINEENDS, bVal); - 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,12 +631,13 @@ sqlQuery->SetText(query); sqlQuery->Colourise(0, query.Length()); wxSafeYield(); // needed to process sqlQuery modify event - 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; setExtendedTitle(); + SqlBookUpdatePageTitle(); } updateMenu(); @@ -860,10 +851,7 @@ settings->WriteBool(wxT("frmQuery/AutoIndent"), editMenu->IsChecked(MNU_AUTOINDENT)); - if (editMenu->IsChecked(MNU_AUTOINDENT)) - sqlQuery->SetAutoIndent(true); - else - sqlQuery->SetAutoIndent(false); + SqlBookSetAutoIndent(event.IsChecked()); } @@ -873,10 +861,7 @@ 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()); } @@ -886,10 +871,7 @@ settings->WriteBool(wxT("frmQuery/ShowIndentGuides"), viewMenu->IsChecked(MNU_SHOWINDENTGUIDES)); - if (viewMenu->IsChecked(MNU_SHOWINDENTGUIDES)) - sqlQuery->SetIndentationGuides(true); - else - sqlQuery->SetIndentationGuides(false); + SqlBookSetIndentGuides(event.IsChecked()); } @@ -899,10 +881,7 @@ 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()); } @@ -912,10 +891,7 @@ settings->WriteBool(wxT("frmQuery/ShowLineEnds"), viewMenu->IsChecked(MNU_SHOWLINEENDS)); - if (viewMenu->IsChecked(MNU_SHOWLINEENDS)) - sqlQuery->SetViewEOL(1); - else - sqlQuery->SetViewEOL(0); + SqlBookSetViewEOL(event.IsChecked()); } @@ -925,7 +901,7 @@ settings->WriteBool(wxT("frmQuery/ShowLineNumber"), viewMenu->IsChecked(MNU_SHOWLINENUMBER)); - sqlQuery->UpdateLineNumber(); + SqlBookSetViewLineNumbers(event.IsChecked()); } void frmQuery::OnActivate(wxActivateEvent &event) @@ -1080,6 +1056,7 @@ else { conn = (pgConn *)cbConnection->GetClientData(sel); + SqlBookSetDatabase(conn); sqlResult->SetConnection(conn); pgScript->SetConnection(conn); title = wxT("Query - ") + cbConnection->GetValue(); @@ -1421,17 +1398,18 @@ void frmQuery::setExtendedTitle() { wxString chgStr; - if (changed) + if (sqlQuery->IsChanged()) chgStr = wxT(" *"); - if (lastPath.IsNull()) + wxString filename = sqlQuery->GetFilename(); + if (filename.IsNull()) SetTitle(title + chgStr); else { - 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 = sqlQuery->IsChanged() || (sqlQuery->GetOrigin() == ORIGIN_INITIAL); toolBar->EnableTool(MNU_SAVE, enableSave); fileMenu->Enable(MNU_SAVE, enableSave); } @@ -1668,11 +1646,12 @@ bool frmQuery::CheckChanged(bool canVeto) { - 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 (!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 +1662,12 @@ switch (msg.ShowModal()) { case wxID_YES: - if (lastPath.IsNull()) + if (filename.IsNull()) OnSaveAs(noEvent); else OnSave(noEvent); - return changed; + return sqlQuery->IsChanged(); case wxID_CANCEL: return true; @@ -1732,7 +1711,7 @@ return; } - if (CheckChanged(event.CanVeto()) && event.CanVeto()) + if (SqlBookClose(event.CanVeto()) && event.CanVeto()) { event.Veto(); return; @@ -1749,7 +1728,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,15 +1755,16 @@ // 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))) + (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) + sqlQuery->SetOrigin(ORIGIN_MANUAL); + if (!sqlQuery->IsChanged()) { - changed = true; + sqlQuery->SetChanged(true); setExtendedTitle(); + SqlBookUpdatePageTitle(); } } // do not allow update of model size of GQB on input (key press) of each @@ -1826,9 +1806,11 @@ sqlQuery->Colourise(0, str.Length()); sqlQuery->EmptyUndoBuffer(); wxSafeYield(); // needed to process sqlQuery modify event - changed = false; - origin = ORIGIN_FILE; + sqlQuery->SetFilename(lastPath); + sqlQuery->SetChanged(false); + sqlQuery->SetOrigin(ORIGIN_FILE); setExtendedTitle(); + SqlBookUpdatePageTitle(); SetLineEndingStyle(); UpdateRecentFiles(true); if(mainForm != NULL) @@ -1881,26 +1863,28 @@ void frmQuery::OnSave(wxCommandEvent &event) { bool modeUnicode = settings->GetUnicodeFile(); + wxString filename = sqlQuery->GetFilename(); - if (lastPath.IsNull()) + if (filename.IsNull()) { OnSaveAs(event); return; } - 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; + sqlQuery->SetChanged(false); setExtendedTitle(); UpdateRecentFiles(); + SqlBookUpdatePageTitle(); } else { - 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 +1908,7 @@ { wxMessageBox(_("This file contains mixed line endings. They will be converted to the current setting."), _("Warning"), wxICON_INFORMATION | wxOK); sqlQuery->ConvertEOLs(mode); - changed = true; + sqlQuery->SetChanged(true); setExtendedTitle(); updateMenu(); } @@ -1940,27 +1924,7 @@ // 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!!")); - } + SetEOLModeDisplay(mode); delete reCRLF; delete reCR; @@ -1992,12 +1956,14 @@ SetEOLModeDisplay(mode); - if (!changed) + if (!sqlQuery->IsChanged()) { - changed = true; + sqlQuery->SetChanged(true); setExtendedTitle(); } + setExtendedTitle(); + // TODO: Figure out why this is here? pgScript->SetConnection(conn); } @@ -2031,11 +1997,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,14 +2040,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; + 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 (sqlQuery->GetOrigin() == ORIGIN_INITIAL) + sqlQuery->SetOrigin(ORIGIN_FILE); setExtendedTitle(); + SqlBookUpdatePageTitle(); UpdateRecentFiles(); fileMenu->Enable(MNU_RECENT, (recentFileMenu->GetMenuItemCount() > 0)); } @@ -2257,7 +2230,7 @@ // 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 (sqlQuery->IsChanged() && sqlQuery->GetOrigin() != ORIGIN_GQB && !sqlQuery->GetText().Trim().IsEmpty() && sqlQuery->GetText() != newQuery + wxT("\n")) { wxString fn; @@ -2267,7 +2240,7 @@ 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 && sqlQuery->IsChanged()) { canGenerate = true; } @@ -2287,8 +2260,8 @@ sqlQuery->Colourise(0, sqlQuery->GetText().Length()); wxSafeYield(); // needed to process sqlQuery modify event sqlNotebook->SetSelection(0); - changed = true; - origin = ORIGIN_GQB; + sqlQuery->SetChanged(true); + sqlQuery->SetOrigin(ORIGIN_GQB); setExtendedTitle(); gqbUpdateRunning = false; @@ -2437,6 +2410,7 @@ } query.Replace(wxT("$SELECTION$"), selection); } + execQuery(query); sqlQuery->SetFocus(); } @@ -2495,7 +2469,7 @@ sqlQuery->StartStyling(0, wxSTC_INDICS_MASK); sqlQuery->SetStyling(sqlQuery->GetText().Length(), 0); - if (!changed) + if (!sqlQuery->IsChanged()) setExtendedTitle(); aborted = false; @@ -2519,6 +2493,10 @@ } } + // Remember the tab from which execute was called. By the time query completes, SQL tab selection may change. + sqlQueryExec = sqlQuery; + ClearResultSource(); + // 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 +2830,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 +2840,29 @@ // 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 (!sqlQueryExec->IsChanged()) setExtendedTitle(); - sqlQuery->EnsureVisible(line); + sqlQueryExec->EnsureVisible(line); } } } @@ -2981,9 +2959,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,9 +3154,52 @@ updateMenu(); } + // Change the tab text so the user knows which tab the result came from + wxString title = sqlQueryExec->GetTitle(); + wxString chStr = sqlQueryExec->GetChangeIndicator(); + if (title.EndsWith(chStr)) + title = title.Mid(0, title.Len() - chStr.Len()); + if (title.Len() > 18) + title = title.Mid(0, 15) + _("..."); + + wxString text = outputPane->GetPageText(0); + wxString newtext = wxString::Format(_("%s [%s]"), text.c_str(), title.c_str()); + outputPane->SetPageText(0, newtext); + + sqlQueryExec = NULL; sqlQuery->SetFocus(); } +void frmQuery::SetResultSource() +{ + // Change the tab text so the user knows which tab the result came from + if (sqlQueryExec != NULL) + { + wxString title = sqlQueryExec->GetTitle(); + wxString chStr = sqlQueryExec->GetChangeIndicator(); + if (title.EndsWith(chStr)) + title = title.Mid(0, title.Len() - chStr.Len()); + if (title.Len() > 18) + title = title.Mid(0, 15) + _("..."); + + wxString text = outputPane->GetPageText(0); + wxString newtext = wxString::Format(_("%s [%s]"), text.c_str(), title.c_str()); + outputPane->SetPageText(0, newtext); + } +} + +void frmQuery::ClearResultSource() +{ + // Clear previous indicator of which tab results came from. + wxString text = outputPane->GetPageText(0); + int idx = text.Find('['); + if (idx != wxNOT_FOUND) + { + text = text.Mid(0, idx - 1); + outputPane->SetPageText(0, text); + } +} + void frmQuery::OnTimer(wxTimerEvent &event) { @@ -3478,8 +3499,8 @@ sqlQuery->SetText(query); sqlQuery->Colourise(0, query.Length()); wxSafeYield(); // needed to process sqlQuery modify event - changed = true; - origin = ORIGIN_HISTORY; + sqlQuery->SetChanged(true); + sqlQuery->SetOrigin(ORIGIN_HISTORY); setExtendedTitle(); SetLineEndingStyle(); btnDeleteCurrent->Enable(true); @@ -3523,6 +3544,258 @@ } } +// 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(); + 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::OnSqlBookPageClose(wxAuiNotebookEvent &event) +{ + // Don't allow removal of the last SQL box via user generated event + size_t pagecnt = sqlQueryBook->GetPageCount(); + if (pagecnt == 1) + { + event.Veto(); + wxMessageBox(_("Cannot remove the last SQL tab")); + return; + } + + // Prevent removing the page that is currently being executed + if (sqlQueryExec != NULL && sqlQueryExec == sqlQuery) + { + event.Veto(); + 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)) + { + event.Veto(); + return; + } + + SqlBookDisconnectPage(); +} + +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 = wxString::Format(_("Query %i"), sqlQueryCounter); + box->SetTitle(caption); + sqlQueryBook->AddPage(box, caption, true); + + // Probably not needed, as the line above should trigger the PageChange event + sqlQuery = box; +} + +void frmQuery::SqlBookDisconnectPage() +{ + sqlQuery->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus)); + sqlQuery->Disconnect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus)); +} + +bool frmQuery::SqlBookRemovePage() +{ + size_t pageidx; + if (sqlQueryBook->GetPageCount() > 0) + { + SqlBookDisconnectPage(); + 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); + } +} + +void frmQuery::SqlBookUpdatePageTitle() +{ + size_t index; + wxString title; + + if (sqlQueryBook->GetPageCount() > 0 && sqlQuery != NULL) + { + index = sqlQueryBook->GetPageIndex(sqlQuery); + if (index == wxNOT_FOUND) + return; + + title = sqlQuery->GetTitle(); + if (sqlQueryBook->GetPageText(index) != title) + sqlQueryBook->SetPageText(index, title); + } +} + +// 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-02 19:48:49.668659652 -0600 @@ -74,11 +74,23 @@ 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(); + void SetTitle(wxString &title); + wxString GetTitle(); + wxString GetChangeIndicator(); + DECLARE_DYNAMIC_CLASS(ctlSQLBox) DECLARE_EVENT_TABLE() protected: void OnEndProcess(wxProcessEvent &ev); + void UpdateTitle(); sysProcess *process; long processID; @@ -93,6 +105,13 @@ pgConn *m_database; bool m_autoIndent, m_autocompDisabled; + // Variables to track info per SQL box + wxString m_filename; + wxString m_title; + wxString m_changestr; + 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-02 20:35:09.965325090 -0600 @@ -101,11 +101,11 @@ } void SetChanged(bool p_changed) { - changed = p_changed; + sqlQuery->SetChanged(p_changed); } void SetOrigin(int p_origin) { - origin = p_origin; + sqlQuery->SetOrigin(p_origin); } void SetLastPath(wxString p_lastpath) { @@ -135,6 +135,10 @@ wxButton *btnDeleteAll; wxArrayString histoQueries; + ctlAuiNotebook *sqlQueryBook; //container for all SQL tabs + size_t sqlQueryCounter; //for initial tab names + ctlSQLBox *sqlQueryExec; //currently executing SQL tab + // Query timing/status update wxTimer timer; wxLongLong elapsedQuery, startTimeQuery; @@ -261,6 +265,29 @@ 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); + void SqlBookUpdatePageTitle(); + void SqlBookDisconnectPage(); + bool SqlBookClose(bool canVeto); + // SQL tabs event handlers + void OnSqlBookAddPage(wxCommandEvent &event); + void OnSqlBookPageClose(wxAuiNotebookEvent &event); + void OnSqlBookPageChanged(wxAuiNotebookEvent &event); + void OnSqlBookPageChanging(wxAuiNotebookEvent &event); + + void SetResultSource(); + void ClearResultSource(); + wxWindow *currentControl(); wxMenu *queryMenu; wxMenu *favouritesMenu; @@ -280,7 +307,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-02 16:11:28.255332087 -0600 @@ -44,6 +44,7 @@ MNU_OPEN, MNU_SAVE, MNU_SAVEAS, + MNU_NEWSQLTAB, 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