////////////////////////////////////////////////////////////////////////// // // pgAdmin III - PostgreSQL Tools // // Copyright (C) 2002 - 2016, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // // dlgClasses.cpp - Some dialogue base classes // ////////////////////////////////////////////////////////////////////////// // wxWindows headers #include #include #include // App headers #include "pgAdmin3.h" #include "frm/frmMain.h" #include "db/pgConn.h" #include "schema/pgObject.h" #include "schema/pgDatabase.h" #include "utils/sysProcess.h" #include "frm/menu.h" #include "db/pgQueryThread.h" #include WX_DEFINE_LIST(windowList); #define STATUSBAR_XRCNAME wxT("unkStatusBar") #define STATUSBAR_CONTAINER STATUSBAR_XRCNAME wxT("_container") BEGIN_EVENT_TABLE(pgDialog, wxDialog) EVT_BUTTON (wxID_CANCEL, pgDialog::OnCancel) EVT_CLOSE( pgDialog::OnClose) END_EVENT_TABLE() void pgDialog::AddStatusBar() { if (!statusBar) { long flags = 0; if (GetWindowStyle() & wxRESIZE_BORDER) flags = wxST_SIZEGRIP; statusBar = new wxStatusBar(this, -1, flags); wxWindow *statusBarContainer = FindWindow(STATUSBAR_CONTAINER); if (statusBarContainer) { wxXmlResource::Get()->AttachUnknownControl(STATUSBAR_XRCNAME, statusBar); } else { int sbHeight = statusBar->GetSize().y; wxSize size = GetSize(); size.y += sbHeight; SetSize(size); size = GetClientSize(); statusBar->Move(0, size.y - sbHeight); } } } void pgDialog::PostCreation() { if (!statusBar && FindWindow(STATUSBAR_CONTAINER)) AddStatusBar(); if (GetWindowStyle() & wxRESIZE_BORDER) // is designed with sizers; don't change return; if (!btnCancel) return; wxSize size = btnCancel->GetSize(); wxPoint pos = btnCancel->GetPosition(); int height = pos.y + size.GetHeight() + ConvertDialogToPixels(wxSize(0, 3)).y; if (statusBar) height += statusBar->GetSize().GetHeight(); int right = pos.x + ConvertDialogToPixels(wxSize(50, 0)).x - size.GetWidth(); btnCancel->Move(right, pos.y); if (btnOK) { size = btnOK->GetSize(); right -= size.GetWidth() + ConvertDialogToPixels(wxSize(3, 0)).x; btnOK->Move(right, pos.y); } // On OS X, reverse the buttons for UI consistency #ifdef __WXMAC__ wxPoint pos2; pos = btnCancel->GetPosition(); if (btnOK) { pos2 = btnOK->GetPosition(); btnOK->Move(pos.x, pos.y); } btnCancel->Move(pos2.x, pos2.y); #endif int w, h; size = GetSize(); GetClientSize(&w, &h); SetSize(size.GetWidth(), size.GetHeight() + height - h); } void pgDialog::RestorePosition(int defaultX, int defaultY, int defaultW, int defaultH, int minW, int minH) { if (minW == -1) minW = GetSize().GetX(); if (minH == -1) minH = GetSize().GetY(); wxPoint pos(settings->Read(dlgName, wxPoint(defaultX, defaultY))); wxSize size; if ((this->GetWindowStyle() & wxRESIZE_BORDER) == wxRESIZE_BORDER) { if (defaultW < 0) size = settings->Read(dlgName, GetSize()); else size = settings->Read(dlgName, wxSize(defaultW, defaultH)); } else { size = GetSize(); } bool posDefault = (pos.x == -1 && pos.y == -1); CheckOnScreen(this, pos, size, minW, minH); SetSize(pos.x, pos.y, size.x, size.y); if (posDefault) CenterOnParent(); } void pgDialog::SavePosition() { #ifndef __WXGTK__ if (!IsIconized()) { #endif wxPoint pos = GetPosition(); if (pos.x < 0) pos.x = 0; if (pos.y < 0) pos.y = 0; settings->WriteSizePoint(dlgName, GetSize(), pos); #ifndef __WXGTK__ } #endif } void pgDialog::LoadResource(wxWindow *parent, const wxChar *name) { if (name) dlgName = name; wxXmlResource::Get()->LoadDialog(this, parent, dlgName); PostCreation(); } void pgDialog::OnClose(wxCloseEvent &event) { if (IsModal()) EndModal(wxID_CANCEL); else Destroy(); } void pgDialog::OnCancel(wxCommandEvent &ev) { if (IsModal()) EndModal(wxID_CANCEL); else Destroy(); } /////////////////////////////////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(pgFrame, wxFrame) EVT_MENU(MNU_EXIT, pgFrame::OnExit) EVT_MENU(MNU_RECENT + 1, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 2, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 3, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 4, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 5, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 6, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 7, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 8, pgFrame::OnRecent) EVT_MENU(MNU_RECENT + 9, pgFrame::OnRecent) #ifdef __WXGTK__ EVT_KEY_DOWN( pgFrame::OnKeyDown) #endif END_EVENT_TABLE() pgFrame::pgFrame(wxFrame *parent, const wxString &title, const wxPoint &pos, const wxSize &size, long flags) : wxFrame(parent, -1, title, pos, size, flags) { changed = false; recentFileMenu = 0; menuFactories = 0; } pgFrame::~pgFrame() { if (!dlgName.IsEmpty()) SavePosition(); if (menuFactories) delete menuFactories; } void pgFrame::RemoveFrame(wxWindow *frame) { frames.DeleteObject(frame); } void pgFrame::OnAction(wxCommandEvent &ev) { actionFactory *af = menuFactories->GetFactory(ev.GetId()); if (af) { wxWindow *wnd = af->StartDialog((frmMain *)this, 0); if (wnd) AddFrame(wnd); } } // Event handlers void pgFrame::OnKeyDown(wxKeyEvent &event) { event.m_metaDown = false; event.Skip(); } void pgFrame::OnExit(wxCommandEvent &event) { Close(); } void pgFrame::OnHelp(wxCommandEvent &WXUNUSED(event)) { wxString page = GetHelpPage(); DisplayHelp(page, HELP_PGADMIN); } void pgFrame::OnRecent(wxCommandEvent &event) { int fileNo = event.GetId() - MNU_RECENT; wxString newPath = settings->Read(recentKey + wxString::Format(wxT("/%d"), fileNo), wxT("")); if (!newPath.IsNull()) { if (CheckChanged(true)) return; lastPath = newPath; int dirsep; dirsep = lastPath.Find(wxFILE_SEP_PATH, true); lastDir = lastPath.Mid(0, dirsep); lastFilename = lastPath.Mid(dirsep + 1); OpenLastFile(); } } void pgFrame::UpdateRecentFiles(bool updatefile) { if (!recentFileMenu) return; if (recentKey.IsEmpty()) recentKey = dlgName + wxT("/RecentFiles"); wxString lastFiles[10]; // 0 will be unused for convenience int i, maxFiles = 9; int recentIndex = maxFiles; for (i = 1 ; i <= maxFiles ; i++) { lastFiles[i] = settings->Read(recentKey + wxString::Format(wxT("/%d"), i), wxT("")); if (!lastPath.IsNull() && lastPath.IsSameAs(lastFiles[i], wxARE_FILENAMES_CASE_SENSITIVE)) recentIndex = i; } while (i <= maxFiles) lastFiles[i++] = wxT(""); if (recentIndex > 1 && !lastPath.IsNull() && updatefile) { for (i = recentIndex ; i > 1 ; i--) lastFiles[i] = lastFiles[i - 1]; lastFiles[1] = lastPath; } i = recentFileMenu->GetMenuItemCount(); while (i) { wxMenuItem *item = recentFileMenu->Remove(MNU_RECENT + i); if (item) delete item; i--; } for (i = 1 ; i <= maxFiles ; i++) { if (updatefile) settings->Write(recentKey + wxString::Format(wxT("/%d"), i), lastFiles[i]); if (!lastFiles[i].IsNull()) recentFileMenu->Append(MNU_RECENT + i, wxT("&") + wxString::Format(wxT("%d"), i) + wxT(" ") + lastFiles[i]); } } void pgFrame::RestorePosition(int defaultX, int defaultY, int defaultW, int defaultH, int minW, int minH) { bool maximized = false; wxPoint pos(settings->Read(dlgName, wxPoint(defaultX, defaultY))); wxSize size; if (defaultW < 0) size = GetSize(); else size = settings->Read(dlgName, wxSize(defaultW, defaultH)); bool posDefault = (pos.x == -1 && pos.y == -1); CheckOnScreen(this, pos, size, minW, minH); SetSize(pos.x, pos.y, size.x, size.y); if (posDefault) CenterOnParent(); settings->Read(dlgName + wxT("/Maximized"), &maximized, false); if (maximized) Maximize(); } void pgFrame::SavePosition() { #ifndef __WXGTK__ if (!IsIconized()) { #endif wxPoint pos = GetPosition(); if (pos.x < 0) pos.x = 0; if (pos.y < 0) pos.y = 0; settings->WriteSizePoint(dlgName, GetSize(), pos); settings->WriteBool(dlgName + wxT("/Maximized"), IsMaximized()); #ifndef __WXGTK__ } #endif } ////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(DialogWithHelp, pgDialog) EVT_MENU(MNU_HELP, DialogWithHelp::OnHelp) EVT_BUTTON(wxID_HELP, DialogWithHelp::OnHelp) END_EVENT_TABLE(); DialogWithHelp::DialogWithHelp(frmMain *frame) : pgDialog() { mainForm = frame; wxAcceleratorEntry entries[2]; entries[0].Set(wxACCEL_NORMAL, WXK_F1, MNU_HELP); // this is for GTK because Meta (usually Numlock) is interpreted like Alt // there are too many controls to reset m_Meta in all of them entries[1].Set(wxACCEL_ALT, WXK_F1, MNU_HELP); wxAcceleratorTable accel(2, entries); SetAcceleratorTable(accel); } void DialogWithHelp::OnHelp(wxCommandEvent &ev) { wxString page = GetHelpPage(); if (!page.IsEmpty()) { if (page.StartsWith(wxT("pg/"))) DisplayHelp(page.Mid(3), HELP_POSTGRESQL); else if (page.StartsWith(wxT("slony/"))) DisplayHelp(page.Mid(6), HELP_SLONY); else DisplayHelp(page, HELP_PGADMIN); } } ////////////////////////////////////////////////////////////////77 BEGIN_EVENT_TABLE(ExecutionDialog, DialogWithHelp) EVT_BUTTON (wxID_OK, ExecutionDialog::OnOK) EVT_BUTTON (wxID_CANCEL, ExecutionDialog::OnCancel) EVT_CLOSE( ExecutionDialog::OnClose) END_EVENT_TABLE() ExecutionDialog::ExecutionDialog(frmMain *frame, pgObject *_object) : DialogWithHelp(frame) { thread = 0; object = _object; txtMessages = 0; bIsAborted = false; bIsExecutionStarted = false; bIsExecutionCompleted = false; pgDatabase *db = object->GetDatabase(); wxString applicationname = appearanceFactory->GetLongAppName() + (" - Execution Tool"); conn = db->CreateConn(applicationname); } void ExecutionDialog::EnableOK(const bool enable) { btnOK->Enable(enable); } void ExecutionDialog::OnClose(wxCloseEvent &event) { Abort(); // If execution is started and not yet complete it means thread is still // running, so we should not delete the connection object and destroy the dialog. if(bIsExecutionStarted && !bIsExecutionCompleted) return; delete conn; if (IsModal()) EndModal(-1); else Destroy(); } void ExecutionDialog::OnCancel(wxCommandEvent &ev) { if (thread) { btnCancel->Disable(); Abort(); btnCancel->Enable(); btnOK->Enable(); } else { delete conn; if (IsModal()) EndModal(-1); else Destroy(); } } void ExecutionDialog::Abort() { // If bIsAborted is true it means abort is still in progress if (thread && !bIsAborted) { bIsAborted = true; if (thread->IsRunning()) { thread->CancelExecution(); thread->Wait(); } delete thread; thread = 0; } } void ExecutionDialog::OnOK(wxCommandEvent &ev) { #ifdef __WXGTK__ if (!btnOK->IsEnabled()) return; #endif if (!thread) { wxString sql = GetSql(); if (sql.IsEmpty()) return; btnOK->Disable(); // Reset the variables bIsAborted = false; bIsExecutionStarted = false; bIsExecutionCompleted = false; thread = new pgQueryThread(conn, sql); if (thread->Create() != wxTHREAD_NO_ERROR) { Abort(); return; } wxLongLong startTime = wxGetLocalTimeMillis(); thread->Run(); // When execution is started then set the variable bIsExecutionStarted = true; wxNotebook *nb = CTRL_NOTEBOOK("nbNotebook"); if (nb) nb->SetSelection(nb->GetPageCount() - 1); if (txtMessages) txtMessages->AppendText("SQL: "+ sql + wxT("\n")); while (thread && thread->IsRunning()) { wxMilliSleep(10); // here could be the animation if (txtMessages) { wxString msg = thread->GetMessagesAndClear(); if (!msg.IsEmpty()) txtMessages->AppendText(msg + wxT("\n")); } wxTheApp->Yield(true); } if (thread) { bool isOk = (thread->ReturnCode() == PGRES_COMMAND_OK || thread->ReturnCode() == PGRES_TUPLES_OK); if (txtMessages) { txtMessages->AppendText(thread->GetMessagesAndClear()); } if (thread->DataSet() != NULL) { wxLogInfo(wxString::Format(wxT("%d rows."), thread->DataSet()->NumRows())); } if (isOk) { if (txtMessages) txtMessages->AppendText( _("Total query runtime: ") + ElapsedTimeToStr( wxGetLocalTimeMillis() - startTime ) ); btnOK->SetLabel(_("Done")); btnCancel->Disable(); } else { if (txtMessages) txtMessages->AppendText(conn->GetLastError()); Abort(); } } else if (txtMessages) txtMessages->AppendText(_("\nCancelled.\n")); btnOK->Enable(); bIsExecutionCompleted = true; } else { Abort(); delete conn; Destroy(); } } #define TIMER_ID 4442 BEGIN_EVENT_TABLE(ExternProcessDialog, DialogWithHelp) EVT_BUTTON(wxID_OK, ExternProcessDialog::OnOK) EVT_BUTTON(wxID_CANCEL, ExternProcessDialog::OnCancel) EVT_CLOSE( ExternProcessDialog::OnClose) EVT_END_PROCESS(-1, ExternProcessDialog::OnEndProcess) EVT_TIMER(TIMER_ID, ExternProcessDialog::OnPollProcess) END_EVENT_TABLE() ExternProcessDialog::ExternProcessDialog(frmMain *frame) : DialogWithHelp(frame) { txtMessages = 0; process = 0; done = false; processID = 0; timer = new wxTimer(this, TIMER_ID); } ExternProcessDialog::~ExternProcessDialog() { Abort(); delete timer; } void ExternProcessDialog::OnOK(wxCommandEvent &ev) { #ifdef __WXGTK__ if (!btnOK->IsEnabled()) return; #endif if (!done) Execute(0); else { Abort(); pgDialog::OnCancel(ev); } } bool ExternProcessDialog::Execute(int step, bool finalStep) { btnOK->Disable(); final = finalStep; if (txtMessages) txtMessages->AppendText(GetDisplayCmd(step) + END_OF_LINE); if (process) { delete process; process = NULL; processID = 0; } process = new sysProcess(this); process->SetEnvironment(environment); processID = wxExecute(GetCmd(step), wxEXEC_ASYNC, process); if (processID) { wxNotebook *nb = CTRL_NOTEBOOK("nbNotebook"); if (nb) nb->SetSelection(nb->GetPageCount() - 1); if (txtMessages) { checkStreams(); timer->Start(100L); } return true; } else { delete process; process = NULL; processID = 0; return false; } } void ExternProcessDialog::OnCancel(wxCommandEvent &ev) { if (process) { btnCancel->Disable(); Abort(); wxMilliSleep(100); if (txtMessages) txtMessages->AppendText(END_OF_LINE + wxString(_("Cancelled on user request.")) + END_OF_LINE + END_OF_LINE); } else pgDialog::OnCancel(ev); } void ExternProcessDialog::OnClose(wxCloseEvent &ev) { btnCancel->Disable(); Abort(); pgDialog::OnClose(ev); } void ExternProcessDialog::OnPollProcess(wxTimerEvent &event) { checkStreams(); } void ExternProcessDialog::checkStreams() { if (txtMessages && process) { txtMessages->AppendText(process->ReadErrorStream()); txtMessages->AppendText(process->ReadInputStream()); } } void ExternProcessDialog::OnEndProcess(wxProcessEvent &ev) { if (process) { if (!ev.GetExitCode()) { if (final) btnOK->SetLabel(_("Done")); done = true; } } timer->Stop(); if (txtMessages) { checkStreams(); txtMessages->AppendText(END_OF_LINE + wxString::Format(_("Process returned exit code %d."), ev.GetExitCode()) + END_OF_LINE); } if (process) { delete process; processID = 0; process = 0; } btnOK->Enable(); btnCancel->Enable(); } void ExternProcessDialog::Abort() { timer->Stop(); if (process) { done = false; // Kill the child process started with wxExecute in Execute method wxKill(processID); processID = 0; } }