Dave, I still could not recreate the crash in my environment. But I have modified the way tabs are closed. Tabs are no longer being removed/deleted from the code, that task is now left for AuiNotebook when the window is closed. Again, it works for me without crashes. I am testing with up to 15, 20, 25 tabs...
Could you please test the attached patch and see if not deleting tabs from the close event makes any difference? Thank you. On Tue, Feb 16, 2016 at 4:20 AM, Dave Page <dp...@pgadmin.org> wrote: > > > On Tue, Feb 16, 2016 at 12:42 AM, Sergey Busel <sbu...@gmail.com> wrote: > >> After trying it couple times, I was able to reproduce the crash. I think >> it was due to the way tabs were being closed. A dirty tab would prompt the >> user to save the changes and close the tab right away before moving on to >> the next tab. I think it crashed because the user was closing the window >> via a menu item and wx wanted to put the focus back into the last control >> that had it, which was already closed and cleaned up by the AuiNotebook. I >> could not reproduce this issue when closing the window via the X button. >> >> Anyway, I changed the way tabs are closed. First, all tabs are checked >> for being dirty and each dirty tab prompts the user to save the changes. >> Then, all tabs are closed, one at a time. I tried the same steps 10 times >> and could not reproduce the crash. >> >> Please let me know if you are still able to reproduce the crash. >> > > Was that the correct patch? I still see what appears to be the same crash > :-( > > Thread 0 Crashed:: Dispatch queue: com.apple.main-thread > 0 libsystem_kernel.dylib 0x9ae66332 __kill + 10 > 1 libsystem_kernel.dylib 0x9ae65932 kill$UNIX2003 + 32 > 2 libsystem_c.dylib 0x955cf75e raise + 26 > 3 libwx_base_carbonud-2.8.0.dylib 0x02477a92 wxTrap() + 18 > 4 libwx_macud_core-2.8.0.dylib 0x01db2ceb > wxGUIAppTraitsBase::ShowAssertDialog(wxString const&) + 251 > 5 libwx_base_carbonud-2.8.0.dylib 0x02477d41 > _ZL16ShowAssertDialogPKwiS0_S0_S0_P11wxAppTraits + 545 > 6 libwx_base_carbonud-2.8.0.dylib 0x024780bd > wxAppConsole::OnAssertFailure(wchar_t const*, int, wchar_t const*, wchar_t > const*, wchar_t const*) + 99 > 7 libwx_base_carbonud-2.8.0.dylib 0x02477f05 wxOnAssert(wchar_t const*, > int, char const*, wchar_t const*, wchar_t const*) + 309 > 8 libwx_macud_core-2.8.0.dylib 0x01dd1c4d > wxControlContainer::SetLastFocus(wxWindow*) + 147 > 9 libwx_macud_core-2.8.0.dylib 0x01eda8c0 > wxPanel::OnChildFocus(wxChildFocusEvent&) + 50 > 10 libwx_base_carbonud-2.8.0.dylib 0x02477984 > wxAppConsole::HandleEvent(wxEvtHandler*, void (wxEvtHandler::*)(wxEvent&), > wxEvent&) const + 102 > 11 libwx_base_carbonud-2.8.0.dylib 0x02560023 > wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&, > wxEvtHandler*, wxEvent&) + 391 > 12 libwx_base_carbonud-2.8.0.dylib 0x025624ef > wxEventHashTable::HandleEvent(wxEvent&, wxEvtHandler*) + 221 > 13 libwx_base_carbonud-2.8.0.dylib 0x02561716 > wxEvtHandler::ProcessEvent(wxEvent&) + 388 > 14 libwx_base_carbonud-2.8.0.dylib 0x02560bc8 > wxEvtHandler::ProcessPendingEvents() + 302 > 15 libwx_base_carbonud-2.8.0.dylib 0x024786d8 > wxAppConsole::ProcessPendingEvents() + 232 > 16 libwx_base_carbonud-2.8.0.dylib 0x02555ea3 > wxMacProcessNotifierAndPendingEvents + 35 > 17 libwx_macud_core-2.8.0.dylib 0x01c9a2c1 > wxApp::MacHandleOneEvent(void*) + 97 > 18 libwx_macud_core-2.8.0.dylib 0x01c9a3ce wxApp::MacDoOneEvent() + 246 > 19 libwx_macud_core-2.8.0.dylib 0x01ccb7fd wxEventLoop::Dispatch() + 57 > 20 libwx_macud_core-2.8.0.dylib 0x01dff6c1 wxEventLoopManual::Run() + > 421 > 21 libwx_macud_core-2.8.0.dylib 0x01db35b4 wxAppBase::MainLoop() + 98 > 22 libwx_macud_core-2.8.0.dylib 0x01db2a6a wxAppBase::OnRun() + 52 > 23 libwx_base_carbonud-2.8.0.dylib 0x024d8782 wxEntry(int&, wchar_t**) + > 258 > 24 libwx_base_carbonud-2.8.0.dylib 0x024d896d wxEntry(int&, char**) + 77 > 25 org.postgresql.pgadmin3 0x00058264 main + 36 (pgAdmin3.cpp:126) > 26 org.postgresql.pgadmin3 0x00036615 start + 53 > > > >> >> Thanks. >> >> On Sun, Feb 14, 2016 at 11:55 PM, Neel Patel <neel.pa...@enterprisedb.com >> > wrote: >> >>> Hi , >>> >>> I just applied the patch in Linux environment and found the crash. Below >>> are the steps to reproduce. >>> We may need to perform below steps multiple time to reproduce the crash. >>> >>> - Open the "Query Window". >>> - Go to "File" and click on "New SQL tab". Open the 3-4 SQL tabs and >>> execute any query in one SQL tab. >>> - Go to "File" menu and click on "Exit". >>> - After clicking on "Exit" button only Query window should close but >>> pgAdmin3 is getting crashed. >>> >>> Below are the traces for reference. >>> >>> #0 0x00007ffff6c4fbf3 in wxControlContainer::SetLastFocus(wxWindow*) () >>> from /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #1 0x00007ffff6ccb684 in wxPanel::OnChildFocus(wxChildFocusEvent&) () >>> from /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #2 0x00007ffff633d9d6 in >>> wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&, >>> wxEvtHandler*, wxEvent&) () >>> from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #3 0x00007ffff633da7b in wxEventHashTable::HandleEvent(wxEvent&, >>> wxEvtHandler*) () from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #4 0x00007ffff633dde7 in wxEvtHandler::ProcessEvent(wxEvent&) () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #5 0x00007ffff633d938 in wxEvtHandler::ProcessPendingEvents() () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #6 0x00007ffff62bb281 in wxAppConsole::ProcessPendingEvents() () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #7 0x00007ffff6c4504e in wxAppBase::ProcessIdle() () from >>> /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #8 0x00007ffff6bbfe31 in wxapp_idle_callback () from >>> /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #9 0x00007ffff366ace5 in g_main_context_dispatch () from >>> /lib/x86_64-linux-gnu/libglib-2.0.so.0 >>> #10 0x00007ffff366b048 in ?? () from >>> /lib/x86_64-linux-gnu/libglib-2.0.so.0 >>> #11 0x00007ffff366b30a in g_main_loop_run () from >>> /lib/x86_64-linux-gnu/libglib-2.0.so.0 >>> #12 0x00007ffff4106eb2 in gtk_dialog_run () from >>> /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 >>> #13 0x00007ffff6c31aea in wxMessageDialog::ShowModal() () from >>> /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #14 0x00000000006b79bf in frmQuery::CheckChanged(bool) () >>> #15 0x00000000006a967e in frmQuery::SqlBookClose(bool) () >>> #16 0x00000000006b7d1e in frmQuery::OnClose(wxCloseEvent&) () >>> #17 0x00007ffff633d9d6 in >>> wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&, >>> wxEvtHandler*, wxEvent&) () >>> from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #18 0x00007ffff633da7b in wxEventHashTable::HandleEvent(wxEvent&, >>> wxEvtHandler*) () from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #19 0x00007ffff633dde7 in wxEvtHandler::ProcessEvent(wxEvent&) () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #20 0x00007ffff633dd70 in wxEvtHandler::ProcessEvent(wxEvent&) () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #21 0x00007ffff6cac39c in wxWindowBase::Close(bool) () from >>> /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #22 0x00007ffff633d9d6 in >>> wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&, >>> wxEvtHandler*, wxEvent&) () >>> from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #23 0x00007ffff633da7b in wxEventHashTable::HandleEvent(wxEvent&, >>> wxEvtHandler*) () from /usr/local/lib/libwx_baseu-2.8.so.0 >>> #24 0x00007ffff633dde7 in wxEvtHandler::ProcessEvent(wxEvent&) () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #25 0x00007ffff633dd70 in wxEvtHandler::ProcessEvent(wxEvent&) () from >>> /usr/local/lib/libwx_baseu-2.8.so.0 >>> #26 0x00007ffff6c2f7b5 in gtk_menu_clicked_callback () from >>> /usr/local/lib/libwx_gtk2u_core-2.8.so.0 >>> #27 0x00007ffff3b5b3b8 in g_closure_invoke () from >>> /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 >>> >>> We will check for Mac OS X environment and keep you updated. >>> >>> Thanks, >>> Neel Patel >>> >>> >>> On Fri, Feb 12, 2016 at 8:51 PM, Dave Page <dp...@pgadmin.org> wrote: >>> >>>> >>>> >>>> On Thu, Feb 11, 2016 at 2:22 PM, Sergey Busel <sbu...@gmail.com> wrote: >>>> >>>>> Added the query name to history entries. Please note that history >>>>> entries are not modified if user saves [Query 1] to file [foo.sql]. I >>>>> don't >>>>> think that would be practical. >>>>> >>>> >>>> Thanks. So I was just about to commit this, when it crashed on me. It >>>> seems to happen only if there are at least 2 non-dirty tabs open (easy to >>>> reproduce - open the query tool, add a second tab, then close the window). >>>> I've fiddled around for a couple of hours, but not been able to get to the >>>> bottom of it despite trying a few tricks that sometimes help avoid weird >>>> issues with wxWidgets. There's a stack trace below. >>>> >>>> Ashesh, Neel, Sanket, Akshay - can any of you take a quick look on an >>>> OSX system? I've attached a slightly cleaned up patch, removing some >>>> commented code and making it a git-diff against master. >>>> >>>> Thanks. >>>> >>>> Thread 0 Crashed:: Dispatch queue: com.apple.main-thread >>>> 0 org.postgresql.pgadmin3 0x0036ce4f wxWindowBase::GetParent() >>>> const + 15 (window.h:590) >>>> 1 libwx_macud_core-2.8.0.dylib 0x01ea6c0b >>>> wxControlContainer::SetLastFocus(wxWindow*) + 81 >>>> 2 libwx_macud_core-2.8.0.dylib 0x01faf8c0 >>>> wxPanel::OnChildFocus(wxChildFocusEvent&) + 50 >>>> 3 libwx_base_carbonud-2.8.0.dylib 0x0254b984 >>>> wxAppConsole::HandleEvent(wxEvtHandler*, void (wxEvtHandler::*)(wxEvent&), >>>> wxEvent&) const + 102 >>>> 4 libwx_base_carbonud-2.8.0.dylib 0x02634023 >>>> wxEvtHandler::ProcessEventIfMatches(wxEventTableEntryBase const&, >>>> wxEvtHandler*, wxEvent&) + 391 >>>> 5 libwx_base_carbonud-2.8.0.dylib 0x026364ef >>>> wxEventHashTable::HandleEvent(wxEvent&, wxEvtHandler*) + 221 >>>> 6 libwx_base_carbonud-2.8.0.dylib 0x02635716 >>>> wxEvtHandler::ProcessEvent(wxEvent&) + 388 >>>> 7 libwx_base_carbonud-2.8.0.dylib 0x02634bc8 >>>> wxEvtHandler::ProcessPendingEvents() + 302 >>>> 8 libwx_base_carbonud-2.8.0.dylib 0x0254c6d8 >>>> wxAppConsole::ProcessPendingEvents() + 232 >>>> 9 libwx_base_carbonud-2.8.0.dylib 0x02629ea3 >>>> wxMacProcessNotifierAndPendingEvents + 35 >>>> 10 libwx_macud_core-2.8.0.dylib 0x01d6f2c1 >>>> wxApp::MacHandleOneEvent(void*) + 97 >>>> 11 libwx_macud_core-2.8.0.dylib 0x01d6f3ce wxApp::MacDoOneEvent() + >>>> 246 >>>> 12 libwx_macud_core-2.8.0.dylib 0x01da07fd wxEventLoop::Dispatch() >>>> + 57 >>>> 13 libwx_macud_core-2.8.0.dylib 0x01ed46c1 wxEventLoopManual::Run() >>>> + 421 >>>> 14 libwx_macud_core-2.8.0.dylib 0x01e885b4 wxAppBase::MainLoop() + >>>> 98 >>>> 15 libwx_macud_core-2.8.0.dylib 0x01e87a6a wxAppBase::OnRun() + 52 >>>> 16 libwx_base_carbonud-2.8.0.dylib 0x025ac782 wxEntry(int&, >>>> wchar_t**) + 258 >>>> 17 libwx_base_carbonud-2.8.0.dylib 0x025ac96d wxEntry(int&, char**) + >>>> 77 >>>> 18 org.postgresql.pgadmin3 0x00128354 main + 36 >>>> (pgAdmin3.cpp:126) >>>> 19 org.postgresql.pgadmin3 0x00106705 start + 53 >>>> >>>> >>>>> >>>>> >>>>> On Wed, Feb 10, 2016 at 8:28 AM, Dave Page <dp...@pgadmin.org> wrote: >>>>> >>>>>> Hi >>>>>> >>>>>> On Wed, Feb 10, 2016 at 12:14 AM, Sergey Busel <sbu...@gmail.com> >>>>>> wrote: >>>>>> >>>>>>> Dave, >>>>>>> >>>>>>> Here is a patch that does display the query tab name in the title >>>>>>> bar of the output pane. It seems to work with loading and saving the >>>>>>> perspective, too. The trick is to restore the original text on the >>>>>>> output >>>>>>> pane before loading and saving the perspective, and put the query name >>>>>>> back >>>>>>> in there right after. >>>>>>> >>>>>>> Attached is the full patch, not the diff from the previous patch I >>>>>>> sent. Let me know if you have other suggestions. >>>>>>> >>>>>> >>>>>> That seems to solve the problem nicely :-) - Thanks! >>>>>> >>>>>> It does lead to one other issue that I can see - the History tab on >>>>>> the results pane now shows the history for all query tabs, which seems a >>>>>> little odd given that the other 3 tabs on the results panel are query tab >>>>>> specific. Unless there are better ideas, I think the best way to fix this >>>>>> is to include the current tab name in the history, e.g. >>>>>> >>>>>> -- Executing query [Query 1]: >>>>>> SELECT version(); >>>>>> Total query runtime: 16 msec >>>>>> 1 row retrieved. >>>>>> >>>>>> -- Executing query [Query 2]: >>>>>> SELECT * FROM pg_class >>>>>> Total query runtime: 53 msec >>>>>> 311 rows retrieved. >>>>>> >>>>>> -- Executing query [foo.sql]: >>>>>> SELECT * from pg_attribute >>>>>> Total query runtime: 245 msec >>>>>> 2399 rows retrieved. >>>>>> >>>>>> What do you think? >>>>>> >>>>>> >>>>>>> Regards. >>>>>>> >>>>>>> On Fri, Feb 5, 2016 at 7:46 AM, Dave Page <dp...@pgadmin.org> wrote: >>>>>>> >>>>>>>> On Wed, Feb 3, 2016 at 3:07 AM, Sergey Busel <sbu...@gmail.com> >>>>>>>> wrote: >>>>>>>> > - 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 - this seems to be working nicely for me. It would be good if >>>>>>>> the query name could be displayed in the title bar of the output >>>>>>>> pane >>>>>>>> rather than on the results tab (because it really does apply to all >>>>>>>> tabs), but I don't see any sensible way to do that given the weird >>>>>>>> way >>>>>>>> that wxAUI stores it's perspectives. >>>>>>>> >>>>>>>> Ashesh, any ideas? >>>>>>>> >>>>>>>> -- >>>>>>>> Dave Page >>>>>>>> Blog: http://pgsnake.blogspot.com >>>>>>>> Twitter: @pgsnake >>>>>>>> >>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com >>>>>>>> The Enterprise PostgreSQL Company >>>>>>>> >>>>>>> >>>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> Dave Page >>>>>> Blog: http://pgsnake.blogspot.com >>>>>> Twitter: @pgsnake >>>>>> >>>>>> EnterpriseDB UK: http://www.enterprisedb.com >>>>>> The Enterprise PostgreSQL Company >>>>>> >>>>> >>>>> >>>> >>>> >>>> -- >>>> Dave Page >>>> Blog: http://pgsnake.blogspot.com >>>> Twitter: @pgsnake >>>> >>>> EnterpriseDB UK: http://www.enterprisedb.com >>>> The Enterprise PostgreSQL Company >>>> >>> >>> >> > > > -- > 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-09 17:27:05.177452723 -0600 @@ -184,6 +184,87 @@ 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(bool withChangeInd) +{ + wxString title = m_title; + wxString chStr; + if (!withChangeInd) + { + chStr = GetChangeIndicator(); + if (title.EndsWith(chStr)) + title = title.Mid(0, title.Len() - chStr.Len()); + } + return 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-16 13:13:01.777887633 -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\tCtrl-T"), _("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...")); @@ -388,7 +397,7 @@ UpdateRecentFiles(); - wxAcceleratorEntry entries[15]; + wxAcceleratorEntry entries[16]; entries[0].Set(wxACCEL_CTRL, (int)'E', MNU_EXECUTE); entries[1].Set(wxACCEL_CTRL, (int)'O', MNU_OPEN); @@ -405,8 +414,9 @@ entries[12].Set(wxACCEL_CTRL, (int)'N', MNU_NEW); entries[13].Set(wxACCEL_NORMAL, WXK_F6, MNU_EXECPGS); entries[14].Set(wxACCEL_NORMAL, WXK_F8, MNU_EXECFILE); + entries[15].Set(wxACCEL_CTRL, (int)'T', MNU_NEWSQLTAB); - wxAcceleratorTable accel(15, entries); + wxAcceleratorTable accel(16, entries); SetAcceleratorTable(accel); queryMenu->Enable(MNU_CANCEL, false); @@ -494,13 +504,14 @@ // 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; + sqlQueryExecLast = NULL; + + boxSQL->Add(sqlQueryBook, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1); boxQuery->Add(boxSQL, 1, wxEXPAND | wxRIGHT | wxLEFT | wxBOTTOM, 1); @@ -535,8 +546,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 +595,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 +633,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(); @@ -818,6 +811,9 @@ void frmQuery::OnDefaultView(wxCommandEvent &event) { + // Reset captions to whatever AuiManager expects in perspective + BeginPerspectiveChange(); + manager.LoadPerspective(FRMQUERY_DEFAULT_PERSPECTIVE, true); // Reset the captions for the current language @@ -827,6 +823,8 @@ manager.GetPane(wxT("outputPane")).Caption(_("Output pane")); manager.GetPane(wxT("scratchPad")).Caption(_("Scratch pad")); + EndPerspectiveChange(false); + manager.Update(); // Sync the View menu options @@ -860,10 +858,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 +868,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 +878,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 +888,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 +898,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 +908,7 @@ settings->WriteBool(wxT("frmQuery/ShowLineNumber"), viewMenu->IsChecked(MNU_SHOWLINENUMBER)); - sqlQuery->UpdateLineNumber(); + SqlBookSetViewLineNumbers(event.IsChecked()); } void frmQuery::OnActivate(wxActivateEvent &event) @@ -1080,6 +1063,7 @@ else { conn = (pgConn *)cbConnection->GetClientData(sel); + SqlBookSetDatabase(conn); sqlResult->SetConnection(conn); pgScript->SetConnection(conn); title = wxT("Query - ") + cbConnection->GetValue(); @@ -1421,17 +1405,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 +1653,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()); else fn = _("The text has changed.\nDo you want to save changes?"); wxMessageDialog msg(this, fn, _("Query"), @@ -1683,12 +1669,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 +1718,7 @@ return; } - if (CheckChanged(event.CanVeto()) && event.CanVeto()) + if (SqlBookClose(event.CanVeto()) && event.CanVeto()) { event.Veto(); return; @@ -1749,7 +1735,6 @@ Hide(); - 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 +1761,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 +1812,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 +1869,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 +1914,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 +1930,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 +1962,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 +2003,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 +2046,17 @@ 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(); + SetOutputPaneCaption(true); UpdateRecentFiles(); fileMenu->Enable(MNU_RECENT, (recentFileMenu->GetMenuItemCount() > 0)); } @@ -2257,7 +2237,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 +2247,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 +2267,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 +2417,7 @@ } query.Replace(wxT("$SELECTION$"), selection); } + execQuery(query); sqlQuery->SetFocus(); } @@ -2495,7 +2476,7 @@ sqlQuery->StartStyling(0, wxSTC_INDICS_MASK); sqlQuery->SetStyling(sqlQuery->GetText().Length(), 0); - if (!changed) + if (!sqlQuery->IsChanged()) setExtendedTitle(); aborted = false; @@ -2519,6 +2500,12 @@ } } + // Remember the tab from which execute was called. By the time query completes, SQL tab selection may change. + sqlQueryExec = sqlQuery; + // Because the output pane is clear during query execution, we clear the result source title on Output Pane. + sqlQueryExecLast = NULL; + SetOutputPaneCaption(true); + // We must do this lot before the query starts, otherwise // it might not happen once the main thread gets busy with // other stuff. @@ -2528,7 +2515,7 @@ msgResult->Clear(); msgResult->SetFont(settings->GetSQLFont()); - msgHistory->AppendText(_("-- Executing query:\n")); + msgHistory->AppendText(wxString::Format(_("-- Executing query [%s]:\n"), sqlQueryExec->GetTitle(false).c_str())); msgHistory->AppendText(query); msgHistory->AppendText(wxT("\n")); Update(); @@ -2852,9 +2839,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 +2849,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 +2968,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 +3163,11 @@ updateMenu(); } + // Change the output pane caption so the user knows which tab the result came from + sqlQueryExecLast = sqlQueryExec; + SetOutputPaneCaption(true); + + sqlQueryExec = NULL; sqlQuery->SetFocus(); } @@ -3478,8 +3470,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 +3515,332 @@ } } +void frmQuery::BeginPerspectiveChange() +{ + manager.GetPane(_("outputPane")).Caption(_("Output pane")); +} + +void frmQuery::EndPerspectiveChange(bool update) +{ + SetOutputPaneCaption(update); +} + +void frmQuery::SetOutputPaneCaption(bool update) +{ + wxString caption; + wxString title; + + if (sqlQueryExecLast == NULL) + caption = _("Output pane"); + else + { + // We don't want to make it look like Output Pane has been changed, + // so request the title without the change indicator + title = sqlQueryExecLast->GetTitle(false); + caption = wxString::Format(_("Output pane [%s]"), title.c_str()); + } + + manager.GetPane(wxT("outputPane")).Caption(caption); + + if (update) + manager.Update(); +} + +// 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 = wxDynamicCast(sqlQueryBook->GetPage(curpage), ctlSQLBox); + if (sqlQuery != NULL) + { + // Update UI with chosen query's info + SetEOLModeDisplay(sqlQuery->GetEOLMode()); + setExtendedTitle(); + + sqlQuery->SetFocus(); + } + } + 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; + } + + // If removing the tab for which results are displayed, reset the output pane's caption + if (sqlQuery == sqlQueryExecLast) + { + sqlQueryExecLast = NULL; + SetOutputPaneCaption(true); + } + + 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(ctlSQLBox *box) +{ + if (box == NULL) + box = sqlQuery; + + if (box != NULL) + { + box->Disconnect(wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(frmQuery::OnFocus)); + box->Disconnect(wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(frmQuery::OnFocus)); + } +} + +bool frmQuery::SqlBookRemovePage() +{ + size_t pageidx; + if (sqlQueryBook->GetPageCount() > 0) + { + // If removing the tab for which results are displayed, reset the output pane's caption + if (sqlQuery == sqlQueryExecLast) + { + sqlQueryExecLast = NULL; + SetOutputPaneCaption(true); + } + + 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 = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + box->SetAutoIndent(b); + } +} + +void frmQuery::SqlBookSetWrapMode(bool b) +{ + size_t i, cnt; + ctlSQLBox *box; + + for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i) + { + box = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + 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 = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + box->SetIndentationGuides(b); + } +} + +void frmQuery::SqlBookSetViewWhiteSpace(bool b) +{ + size_t i, cnt; + ctlSQLBox *box; + + for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i) + { + box = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + 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 = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + 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 = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + box->UpdateLineNumber(); + } +} + +void frmQuery::SqlBookSetDatabase(pgConn *con) +{ + size_t i, cnt; + ctlSQLBox *box; + + for (i = 0, cnt = sqlQueryBook->GetPageCount(); i < cnt; ++i) + { + box = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + 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) +{ + size_t i = 0; + size_t cnt = sqlQueryBook->GetPageCount(); + ctlSQLBox *box; + + // See if we have any unsaved changes and prompt to save them + for (i = 0; i < cnt; ++i) + { + box = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL && box->IsChanged()) + { + sqlQueryBook->SetSelection(i); + if (CheckChanged(canVeto)) + return true; + } + } + + // If we got here, the window will be closed + for (i = 0; i < cnt; ++i) + { + box = wxDynamicCast(sqlQueryBook->GetPage(i), ctlSQLBox); + if (box != NULL) + SqlBookDisconnectPage(box); + } + + 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-09 17:10:59.050767933 -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(bool withChangeInd = true); + 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-16 13:43:07.284616324 -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,11 @@ wxButton *btnDeleteAll; wxArrayString histoQueries; + ctlAuiNotebook *sqlQueryBook; //container for all SQL tabs + size_t sqlQueryCounter; //for initial tab names + ctlSQLBox *sqlQueryExec; //currently executing SQL tab + ctlSQLBox *sqlQueryExecLast; //output pane shows results for this SQL tab + // Query timing/status update wxTimer timer; wxLongLong elapsedQuery, startTimeQuery; @@ -261,6 +266,30 @@ 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(ctlSQLBox *box = NULL); + 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 BeginPerspectiveChange(); + void EndPerspectiveChange(bool update = false); + void SetOutputPaneCaption(bool update = false); + wxWindow *currentControl(); wxMenu *queryMenu; wxMenu *favouritesMenu; @@ -279,9 +308,6 @@ bool lastFileFormat; bool m_loadingfile; - // Complements dirty flag, showing last origin of query's modification (see enum ORIGIN_..) - int origin; - // A simple mutex-like flag to prevent concurrent script execution. // Required because the pgScript parser isn't currently thread-safe :-( static bool ms_pgScriptRunning; 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