pgadmin3/dlg/dlgClasses.cpp
levinsv 4af765213c support PG11
Поддержка PostgreSQL 11 только для Windows
2018-10-10 22:59:25 +05:00

778 lines
15 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// 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 <wx/wx.h>
#include <wx/settings.h>
#include <wx/xrc/xmlres.h>
// 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/listimpl.cpp>
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);
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;
}
}