mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
support PG11
Поддержка PostgreSQL 11 только для Windows
This commit is contained in:
commit
4af765213c
1765 changed files with 407959 additions and 0 deletions
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
*.sbr
|
||||
*.obj
|
||||
*.cod
|
||||
*.dll
|
||||
*.tlog
|
||||
*.7z
|
||||
*.bsc
|
||||
Release/pgAdmin3.Build.CppClean.log
|
||||
Release/pgAdmin3.ilk
|
||||
Release/pgAdmin3.log
|
||||
Release/pgAdmin3.map
|
||||
Release/pgAdmin3.pch
|
||||
Release/pgAdmin3.pdb
|
||||
Release/pgAdmin3.res
|
||||
Release/vc110.pdb
|
||||
Release/Win32/libssh2/vc110.pdb
|
||||
Release/x64/libssh2/vc110.pdb
|
||||
Release/pgAdmin3.lastbuildstate
|
||||
Release/hist.log
|
||||
Release/1.sql
|
||||
Release/1.xml
|
||||
Release/33.xml
|
||||
BIN
Debug/Win32/libssh2/vc100.idb
Normal file
BIN
Debug/Win32/libssh2/vc100.idb
Normal file
Binary file not shown.
56
Debug/pgAdmin3.Build.CppClean.log
Normal file
56
Debug/pgAdmin3.Build.CppClean.log
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregate-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregate.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\aggregates.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\back.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\backup.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\baddatabase.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\cast-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\cast.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\casts.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalog-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalog.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobject-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobject.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogobjects.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\catalogs.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\check.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\checkbad.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\checked.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clearAll.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_copy.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_cut.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\clip_paste.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\closeddatabase-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\closeddatabase.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collation-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collation.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\collations.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\column-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\column.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\columns.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\configuration.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\configurations.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\connect.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\constraints.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\continue.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\conversion.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\conversions.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\create.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\database-sm.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\database.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\databases.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddColumn.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddColumnCursor.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddAddForeignKey.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddcancel.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddDeleteTableCursor.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddDown.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkey.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyfromuk.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyuniquekey.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddforeignkeyuniquekeyfromuk.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddgendiagram.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMaximizeTable.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMinimizeTable.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\include\images\ddMinMaxCursor.pngc
|
||||
D:\PostgreSQL\pgadmin3\pgadmin\Debug\pgAdmin3.write.1.tlog
|
||||
2
Debug/pgAdmin3.lastbuildstate
Normal file
2
Debug/pgAdmin3.lastbuildstate
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#v4.0:v100:false
|
||||
Debug|Win32|D:\PostgreSQL\pgadmin3\|
|
||||
5
Debug/pgAdmin3.log
Normal file
5
Debug/pgAdmin3.log
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
"git" не является внутренней или внешней
|
||||
командой, исполняемой программой или пакетным файлом.
|
||||
Not generating svnversion.h, not changed.
|
||||
precomp.cpp
|
||||
d:\wxwidgets-2.8.12\include\wx\setup.h(20): fatal error C1083: Cannot open include file: '../../../lib/vc_dll/mswud/wx/setup.h': No such file or directory
|
||||
0
Debug/pgAdmin3.unsuccessfulbuild
Normal file
0
Debug/pgAdmin3.unsuccessfulbuild
Normal file
BIN
Debug/vc100.idb
Normal file
BIN
Debug/vc100.idb
Normal file
Binary file not shown.
BIN
Debug/vc100.pdb
Normal file
BIN
Debug/vc100.pdb
Normal file
Binary file not shown.
BIN
Release/pgAdmin3.exe
Normal file
BIN
Release/pgAdmin3.exe
Normal file
Binary file not shown.
513
agent/dlgJob.cpp
Normal file
513
agent/dlgJob.cpp
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dlgJob.cpp - PostgreSQL Job Property
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/misc.h"
|
||||
#include "agent/dlgJob.h"
|
||||
#include "agent/dlgStep.h"
|
||||
#include "agent/dlgSchedule.h"
|
||||
#include "agent/pgaJob.h"
|
||||
#include "agent/pgaStep.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
#include "frm/frmMain.h"
|
||||
|
||||
|
||||
// pointer to controls
|
||||
#define txtID CTRL_TEXT("txtID")
|
||||
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
|
||||
#define cbJobclass CTRL_COMBOBOX("cbJobclass")
|
||||
#define txtHostAgent CTRL_TEXT("txtHostAgent")
|
||||
#define txtCreated CTRL_TEXT("txtCreated")
|
||||
#define txtChanged CTRL_TEXT("txtChanged")
|
||||
#define txtNextrun CTRL_TEXT("txtNextrun")
|
||||
#define txtLastrun CTRL_TEXT("txtLastrun")
|
||||
#define txtLastresult CTRL_TEXT("txtLastResult")
|
||||
|
||||
#define lstSteps CTRL_LISTVIEW("lstSteps")
|
||||
#define btnChangeStep CTRL_BUTTON("btnChangeStep")
|
||||
#define btnAddStep CTRL_BUTTON("btnAddStep")
|
||||
#define btnRemoveStep CTRL_BUTTON("btnRemoveStep")
|
||||
|
||||
#define lstSchedules CTRL_LISTVIEW("lstSchedules")
|
||||
#define btnChangeSchedule CTRL_BUTTON("btnChangeSchedule")
|
||||
#define btnAddSchedule CTRL_BUTTON("btnAddSchedule")
|
||||
#define btnRemoveSchedule CTRL_BUTTON("btnRemoveSchedule")
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(dlgJob, dlgAgentProperty)
|
||||
EVT_CHECKBOX(XRCID("chkEnabled"), dlgProperty::OnChange)
|
||||
EVT_COMBOBOX(XRCID("cbJobclass"), dlgProperty::OnChange)
|
||||
EVT_TEXT(XRCID("txtHostAgent"), dlgProperty::OnChange)
|
||||
|
||||
EVT_LIST_ITEM_SELECTED(XRCID("lstSteps"), dlgJob::OnSelChangeStep)
|
||||
EVT_BUTTON(XRCID("btnChangeStep"), dlgJob::OnChangeStep)
|
||||
EVT_BUTTON(XRCID("btnAddStep"), dlgJob::OnAddStep)
|
||||
EVT_BUTTON(XRCID("btnRemoveStep"), dlgJob::OnRemoveStep)
|
||||
|
||||
EVT_LIST_ITEM_SELECTED(XRCID("lstSchedules"), dlgJob::OnSelChangeSchedule)
|
||||
EVT_BUTTON(XRCID("btnChangeSchedule"), dlgJob::OnChangeSchedule)
|
||||
EVT_BUTTON(XRCID("btnAddSchedule"), dlgJob::OnAddSchedule)
|
||||
EVT_BUTTON(XRCID("btnRemoveSchedule"), dlgJob::OnRemoveSchedule)
|
||||
|
||||
#ifdef __WXMAC__
|
||||
EVT_SIZE( dlgJob::OnChangeSize)
|
||||
#endif
|
||||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
dlgProperty *pgaJobFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
|
||||
{
|
||||
return new dlgJob(this, frame, (pgaJob *)node);
|
||||
}
|
||||
|
||||
|
||||
dlgJob::dlgJob(pgaFactory *f, frmMain *frame, pgaJob *node)
|
||||
: dlgAgentProperty(f, frame, wxT("dlgJob"))
|
||||
{
|
||||
job = node;
|
||||
|
||||
txtID->Disable();
|
||||
txtCreated->Disable();
|
||||
txtChanged->Disable();
|
||||
txtNextrun->Disable();
|
||||
txtLastrun->Disable();
|
||||
txtLastresult->Disable();
|
||||
lstSteps->CreateColumns(0, _("Step"), _("Comment"), 90);
|
||||
lstSteps->AddColumn(wxT("cmd"), 0);
|
||||
lstSteps->AddColumn(wxT("id"), 0);
|
||||
|
||||
lstSchedules->CreateColumns(0, _("Schedule"), _("Comment"), 90);
|
||||
lstSchedules->AddColumn(wxT("cmd"), 0);
|
||||
lstSchedules->AddColumn(wxT("id"), 0);
|
||||
btnChangeStep->Disable();
|
||||
btnRemoveStep->Disable();
|
||||
btnChangeSchedule->Disable();
|
||||
btnRemoveSchedule->Disable();
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgJob::GetObject()
|
||||
{
|
||||
return job;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __WXMAC__
|
||||
void dlgJob::OnChangeSize(wxSizeEvent &ev)
|
||||
{
|
||||
lstSteps->SetSize(wxDefaultCoord, wxDefaultCoord,
|
||||
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
|
||||
lstSchedules->SetSize(wxDefaultCoord, wxDefaultCoord,
|
||||
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
|
||||
if (GetAutoLayout())
|
||||
{
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int dlgJob::Go(bool modal)
|
||||
{
|
||||
int returncode;
|
||||
|
||||
pgSet *jcl = connection->ExecuteSet(wxT("SELECT jclname FROM pgagent.pga_jobclass"));
|
||||
if (jcl)
|
||||
{
|
||||
while (!jcl->Eof())
|
||||
{
|
||||
cbJobclass->Append(jcl->GetVal(0));
|
||||
jcl->MoveNext();
|
||||
}
|
||||
delete jcl;
|
||||
}
|
||||
|
||||
if (job)
|
||||
{
|
||||
// edit mode
|
||||
recId = job->GetRecId();
|
||||
txtID->SetValue(NumToStr(recId));
|
||||
cbJobclass->SetValue(job->GetJobclass());
|
||||
chkEnabled->SetValue(job->GetEnabled());
|
||||
txtHostAgent->SetValue(job->GetHostAgent());
|
||||
txtCreated->SetValue(DateToStr(job->GetCreated()));
|
||||
txtChanged->SetValue(DateToStr(job->GetChanged()));
|
||||
txtNextrun->SetValue(DateToStr(job->GetNextrun()));
|
||||
txtLastrun->SetValue(DateToStr(job->GetLastrun()));
|
||||
txtLastresult->SetValue(job->GetLastresult());
|
||||
|
||||
wxCookieType cookie;
|
||||
pgObject *data = 0;
|
||||
|
||||
wxTreeItemId item, stepsItem, schedulesItem;
|
||||
item = mainForm->GetBrowser()->GetFirstChild(job->GetId(), cookie);
|
||||
while (item)
|
||||
{
|
||||
data = mainForm->GetBrowser()->GetObject(item);
|
||||
if (data->GetMetaType() == PGM_STEP)
|
||||
stepsItem = item;
|
||||
else if (data->GetMetaType() == PGM_SCHEDULE)
|
||||
schedulesItem = item;
|
||||
|
||||
item = mainForm->GetBrowser()->GetNextChild(job->GetId(), cookie);
|
||||
}
|
||||
|
||||
if (stepsItem)
|
||||
{
|
||||
pgCollection *coll = (pgCollection *)data;
|
||||
// make sure all columns are appended
|
||||
coll->ShowTreeDetail(mainForm->GetBrowser());
|
||||
// this is the columns collection
|
||||
item = mainForm->GetBrowser()->GetFirstChild(stepsItem, cookie);
|
||||
|
||||
// add columns
|
||||
while (item)
|
||||
{
|
||||
data = mainForm->GetBrowser()->GetObject(item);
|
||||
if (data->IsCreatedBy(stepFactory))
|
||||
{
|
||||
pgaStep *step = (pgaStep *)data;
|
||||
int pos = lstSteps->AppendItem(stepFactory.GetIconId(), step->GetName(), step->GetComment());
|
||||
lstSteps->SetItem(pos, 3, NumToStr((long)step));
|
||||
previousSteps.Add(NumToStr((long)step));
|
||||
}
|
||||
item = mainForm->GetBrowser()->GetNextChild(stepsItem, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
if (schedulesItem)
|
||||
{
|
||||
pgCollection *coll = (pgCollection *)data;
|
||||
// make sure all columns are appended
|
||||
coll->ShowTreeDetail(mainForm->GetBrowser());
|
||||
// this is the columns collection
|
||||
item = mainForm->GetBrowser()->GetFirstChild(schedulesItem, cookie);
|
||||
|
||||
// add columns
|
||||
while (item)
|
||||
{
|
||||
data = mainForm->GetBrowser()->GetObject(item);
|
||||
if (data->IsCreatedBy(scheduleFactory))
|
||||
{
|
||||
pgaSchedule *schedule = (pgaSchedule *)data;
|
||||
int pos = lstSchedules->AppendItem(scheduleFactory.GetIconId(), schedule->GetName(), schedule->GetComment());
|
||||
lstSchedules->SetItem(pos, 3, NumToStr((long)schedule));
|
||||
previousSchedules.Add(NumToStr((long)schedule));
|
||||
}
|
||||
item = mainForm->GetBrowser()->GetNextChild(schedulesItem, cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode
|
||||
cbJobclass->SetSelection(0);
|
||||
btnChangeStep->Hide();
|
||||
btnChangeSchedule->Hide();
|
||||
}
|
||||
|
||||
returncode = dlgProperty::Go(modal);
|
||||
|
||||
SetSqlReadOnly(true);
|
||||
|
||||
// This fixes a UI glitch on MacOS X
|
||||
// Because of the new layout code, the Columns pane doesn't size itself properly
|
||||
SetSize(GetSize().GetWidth() + 1, GetSize().GetHeight());
|
||||
SetSize(GetSize().GetWidth() - 1, GetSize().GetHeight());
|
||||
|
||||
return returncode;
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgJob::CreateObject(pgCollection *collection)
|
||||
{
|
||||
pgObject *obj = jobFactory.CreateObjects(collection, 0, wxT(" WHERE jobid=") + NumToStr(recId) + wxT("\n"));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::CheckChange()
|
||||
{
|
||||
bool enable = true;
|
||||
wxString name = GetName();
|
||||
if (job)
|
||||
{
|
||||
enable = txtComment->GetValue() != job->GetComment()
|
||||
|| name != job->GetName()
|
||||
|| chkEnabled->GetValue() != job->GetEnabled()
|
||||
|| txtHostAgent->GetValue() != job->GetHostAgent();
|
||||
if (!enable)
|
||||
{
|
||||
enable = !GetUpdateSql().IsEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
if (statusBar)
|
||||
statusBar->SetStatusText(wxEmptyString);
|
||||
|
||||
CheckValid(enable, !txtName->GetValue().IsEmpty(), _("Please specify name."));
|
||||
|
||||
EnableOK(enable);
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnChangeStep(wxCommandEvent &ev)
|
||||
{
|
||||
long pos = lstSteps->GetSelection();
|
||||
pgaStep *obj = (pgaStep *) StrToLong(lstSteps->GetText(pos, 3));
|
||||
|
||||
dlgStep step(&stepFactory, mainForm, obj, job);
|
||||
step.CenterOnParent();
|
||||
step.SetConnection(connection);
|
||||
|
||||
if (step.Go(true) != wxID_CANCEL)
|
||||
{
|
||||
lstSteps->SetItem(pos, 0, step.GetName());
|
||||
lstSteps->SetItem(pos, 1, step.GetComment());
|
||||
|
||||
if (lstSteps->GetText(pos, 3).IsEmpty())
|
||||
{
|
||||
wxString *stepSql = new wxString(step.GetInsertSql());
|
||||
lstSteps->SetItemData(pos, (long)stepSql);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString *stepSql = new wxString(step.GetUpdateSql());
|
||||
lstSteps->SetItemData(pos, (long)stepSql);
|
||||
}
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnSelChangeStep(wxListEvent &ev)
|
||||
{
|
||||
btnChangeStep->Enable();
|
||||
btnRemoveStep->Enable();
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnAddStep(wxCommandEvent &ev)
|
||||
{
|
||||
dlgStep step(&stepFactory, mainForm, NULL, job);
|
||||
step.CenterOnParent();
|
||||
step.SetConnection(connection);
|
||||
if (step.Go(true) != wxID_CANCEL)
|
||||
{
|
||||
int pos = lstSteps->AppendItem(stepFactory.GetIconId(), step.GetName(), step.GetComment());
|
||||
wxString *stepSql = new wxString(step.GetInsertSql());
|
||||
lstSteps->SetItemData(pos, (long)stepSql);
|
||||
CheckChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnRemoveStep(wxCommandEvent &ev)
|
||||
{
|
||||
delete (wxString *)lstSteps->GetItemData(lstSteps->GetSelection());
|
||||
lstSteps->DeleteCurrentItem();
|
||||
|
||||
btnChangeStep->Disable();
|
||||
btnRemoveStep->Disable();
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnSelChangeSchedule(wxListEvent &ev)
|
||||
{
|
||||
btnChangeSchedule->Enable();
|
||||
btnRemoveSchedule->Enable();
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnChangeSchedule(wxCommandEvent &ev)
|
||||
{
|
||||
long pos = lstSchedules->GetSelection();
|
||||
pgaSchedule *obj = (pgaSchedule *) StrToLong(lstSchedules->GetText(pos, 3));
|
||||
|
||||
dlgSchedule schedule(&scheduleFactory, mainForm, obj, job);
|
||||
schedule.CenterOnParent();
|
||||
schedule.SetConnection(connection);
|
||||
|
||||
if (schedule.Go(true) != wxID_CANCEL)
|
||||
{
|
||||
lstSchedules->SetItem(pos, 0, schedule.GetName());
|
||||
lstSchedules->SetItem(pos, 1, schedule.GetComment());
|
||||
|
||||
if (lstSchedules->GetText(pos, 3).IsEmpty())
|
||||
{
|
||||
wxString *scheduleSql = new wxString(schedule.GetInsertSql());
|
||||
lstSchedules->SetItemData(pos, (long)scheduleSql);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString *scheduleSql = new wxString(schedule.GetUpdateSql());
|
||||
lstSchedules->SetItemData(pos, (long)scheduleSql);
|
||||
}
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnAddSchedule(wxCommandEvent &ev)
|
||||
{
|
||||
dlgSchedule schedule(&scheduleFactory, mainForm, NULL, job);
|
||||
schedule.CenterOnParent();
|
||||
schedule.SetConnection(connection);
|
||||
if (schedule.Go(true) != wxID_CANCEL)
|
||||
{
|
||||
int pos = lstSchedules->AppendItem(scheduleFactory.GetIconId(), schedule.GetName(), schedule.GetComment());
|
||||
wxString *scheduleSql = new wxString(schedule.GetInsertSql());
|
||||
lstSchedules->SetItemData(pos, (long)scheduleSql);
|
||||
CheckChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dlgJob::OnRemoveSchedule(wxCommandEvent &ev)
|
||||
{
|
||||
delete (wxString *)lstSchedules->GetItemData(lstSchedules->GetSelection());
|
||||
lstSchedules->DeleteCurrentItem();
|
||||
|
||||
btnChangeSchedule->Disable();
|
||||
btnRemoveSchedule->Disable();
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
wxString dlgJob::GetInsertSql()
|
||||
{
|
||||
wxString sql;
|
||||
|
||||
if (!job)
|
||||
{
|
||||
sql = wxT("INSERT INTO pgagent.pga_job (jobid, jobjclid, jobname, jobdesc, jobenabled, jobhostagent)\n")
|
||||
wxT("SELECT <JobId>, jcl.jclid, ") + qtDbString(GetName()) +
|
||||
wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ") + BoolToStr(chkEnabled->GetValue()) +
|
||||
wxT(", ") + qtDbString(txtHostAgent->GetValue()) + wxT("\n")
|
||||
wxT(" FROM pgagent.pga_jobclass jcl WHERE jclname=") + qtDbString(cbJobclass->GetValue()) + wxT(";\n");
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
wxString dlgJob::GetUpdateSql()
|
||||
{
|
||||
wxString sql, name;
|
||||
name = GetName();
|
||||
|
||||
if (job)
|
||||
{
|
||||
// edit mode
|
||||
wxString vars;
|
||||
|
||||
if (name != job->GetName())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jobname = ") + qtDbString(name));
|
||||
}
|
||||
if (cbJobclass->GetValue().Trim() != job->GetJobclass())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jobjclid= (SELECT jclid FROM pgagent.pga_jobclass WHERE jclname=") + qtDbString(cbJobclass->GetValue()) + wxT(")"));
|
||||
}
|
||||
if (chkEnabled->GetValue() != job->GetEnabled())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jobenabled = ") + BoolToStr(chkEnabled->GetValue()));
|
||||
}
|
||||
if (txtHostAgent->GetValue() != job->GetHostAgent())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jobhostagent = ") + qtDbString(txtHostAgent->GetValue()));
|
||||
}
|
||||
if (txtComment->GetValue() != job->GetComment())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jobdesc = ") + qtDbString(txtComment->GetValue()));
|
||||
}
|
||||
|
||||
if (!vars.IsEmpty())
|
||||
sql = wxT("UPDATE pgagent.pga_job SET ") + vars + wxT("\n")
|
||||
wxT(" WHERE jobid=") + NumToStr(recId) + wxT(";\n");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode
|
||||
// done by GetInsertSql
|
||||
}
|
||||
|
||||
int pos, index;
|
||||
|
||||
wxArrayString tmpSteps = previousSteps;
|
||||
for (pos = 0 ; pos < lstSteps->GetItemCount() ; pos++)
|
||||
{
|
||||
wxString str = lstSteps->GetText(pos, 3);
|
||||
if (!str.IsEmpty())
|
||||
{
|
||||
index = tmpSteps.Index(str);
|
||||
if (index >= 0)
|
||||
tmpSteps.RemoveAt(index);
|
||||
}
|
||||
|
||||
if(lstSteps->GetItemData(pos))
|
||||
{
|
||||
str = *(wxString *)lstSteps->GetItemData(pos);
|
||||
if (!str.IsEmpty())
|
||||
sql += str;
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0 ; index < (int)tmpSteps.GetCount() ; index++)
|
||||
{
|
||||
sql += wxT("DELETE FROM pgagent.pga_jobstep WHERE jstid=")
|
||||
+ NumToStr(((pgaStep *)StrToLong(tmpSteps.Item(index)))->GetRecId()) + wxT(";\n");
|
||||
}
|
||||
|
||||
wxArrayString tmpSchedules = previousSchedules;
|
||||
for (pos = 0 ; pos < lstSchedules->GetItemCount() ; pos++)
|
||||
{
|
||||
wxString str = lstSchedules->GetText(pos, 3);
|
||||
if (!str.IsEmpty())
|
||||
{
|
||||
index = tmpSchedules.Index(str);
|
||||
if (index >= 0)
|
||||
tmpSchedules.RemoveAt(index);
|
||||
}
|
||||
if(lstSchedules->GetItemData(pos))
|
||||
{
|
||||
str = *(wxString *)lstSchedules->GetItemData(pos);
|
||||
if (!str.IsEmpty())
|
||||
sql += str;
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0 ; index < (int)tmpSchedules.GetCount() ; index++)
|
||||
{
|
||||
sql += wxT("DELETE FROM pgagent.pga_schedule WHERE jscid=")
|
||||
+ NumToStr(((pgaStep *)StrToLong(tmpSchedules.Item(index)))->GetRecId()) + wxT(";\n");
|
||||
}
|
||||
|
||||
return sql;
|
||||
|
||||
}
|
||||
859
agent/dlgSchedule.cpp
Normal file
859
agent/dlgSchedule.cpp
Normal file
|
|
@ -0,0 +1,859 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dlgSchedule.cpp - PostgreSQL Schedule Property
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// App headers
|
||||
#include "utils/misc.h"
|
||||
#include "agent/dlgSchedule.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
|
||||
// image for de/-select all
|
||||
#include "images/check.pngc"
|
||||
#include "images/uncheck.pngc"
|
||||
|
||||
// pointer to controls
|
||||
#define txtID CTRL_TEXT("txtID")
|
||||
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
|
||||
#define calStart CTRL_CALENDAR("calStart")
|
||||
#define timStart CTRL_TIME("timStart")
|
||||
#define calEnd CTRL_CALENDAR("calEnd")
|
||||
#define timEnd CTRL_TIME("timEnd")
|
||||
#define chkWeekdays CTRL_CHECKLISTBOX("chkWeekdays")
|
||||
#define chkMonthdays CTRL_CHECKLISTBOX("chkMonthdays")
|
||||
#define chkMonths CTRL_CHECKLISTBOX("chkMonths")
|
||||
#define chkHours CTRL_CHECKLISTBOX("chkHours")
|
||||
#define chkMinutes CTRL_CHECKLISTBOX("chkMinutes")
|
||||
#define lstExceptions CTRL_LISTVIEW("lstExceptions")
|
||||
#define timException CTRL_TIME("timException")
|
||||
#define calException CTRL_CALENDAR("calException")
|
||||
#define btnAddException CTRL_BUTTON("btnAddException")
|
||||
#define btnChangeException CTRL_BUTTON("btnChangeException")
|
||||
#define btnRemoveException CTRL_BUTTON("btnRemoveException")
|
||||
#define btnWeekdays CTRL_BUTTON("btnWeekdays")
|
||||
#define btnMonthdays CTRL_BUTTON("btnMonthdays")
|
||||
#define btnMonths CTRL_BUTTON("btnMonths")
|
||||
#define btnHours CTRL_BUTTON("btnHours")
|
||||
#define btnMinutes CTRL_BUTTON("btnMinutes")
|
||||
|
||||
BEGIN_EVENT_TABLE(dlgSchedule, dlgAgentProperty)
|
||||
EVT_CHECKBOX(XRCID("chkEnabled"), dlgSchedule::OnChangeCom)
|
||||
EVT_CALENDAR_SEL_CHANGED(XRCID("calStart"), dlgSchedule::OnChangeCal)
|
||||
EVT_TEXT(XRCID("timStart"), dlgSchedule::OnChangeCom)
|
||||
EVT_CALENDAR_SEL_CHANGED(XRCID("calEnd"), dlgSchedule::OnChangeCal)
|
||||
EVT_TEXT(XRCID("timEnd"), dlgSchedule::OnChangeCom)
|
||||
EVT_LIST_ITEM_SELECTED(XRCID("lstExceptions"), dlgSchedule::OnSelChangeException)
|
||||
EVT_BUTTON(XRCID("btnAddException"), dlgSchedule::OnAddException)
|
||||
EVT_BUTTON(XRCID("btnChangeException"), dlgSchedule::OnChangeException)
|
||||
EVT_BUTTON(XRCID("btnRemoveException"), dlgSchedule::OnRemoveException)
|
||||
EVT_BUTTON(XRCID("btnWeekdays"), dlgSchedule::OnSelectAllWeekdays)
|
||||
EVT_BUTTON(XRCID("btnMonthdays"), dlgSchedule::OnSelectAllMonthdays)
|
||||
EVT_BUTTON(XRCID("btnMonths"), dlgSchedule::OnSelectAllMonths)
|
||||
EVT_BUTTON(XRCID("btnHours"), dlgSchedule::OnSelectAllHours)
|
||||
EVT_BUTTON(XRCID("btnMinutes"), dlgSchedule::OnSelectAllMinutes)
|
||||
EVT_CHECKLISTBOX(XRCID("chkWeekdays"), dlgSchedule::OnChangeCom)
|
||||
EVT_CHECKLISTBOX(XRCID("chkMonthdays"), dlgSchedule::OnChangeCom)
|
||||
EVT_CHECKLISTBOX(XRCID("chkMonths"), dlgSchedule::OnChangeCom)
|
||||
EVT_CHECKLISTBOX(XRCID("chkHours"), dlgSchedule::OnChangeCom)
|
||||
EVT_CHECKLISTBOX(XRCID("chkMinutes"), dlgSchedule::OnChangeCom)
|
||||
#ifdef __WXMAC__
|
||||
EVT_SIZE( dlgSchedule::OnChangeSize)
|
||||
#endif
|
||||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
dlgProperty *pgaScheduleFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
|
||||
{
|
||||
return new dlgSchedule(this, frame, (pgaSchedule *)node, (pgaJob *)parent);
|
||||
}
|
||||
|
||||
|
||||
dlgSchedule::dlgSchedule(pgaFactory *f, frmMain *frame, pgaSchedule *node, pgaJob *j)
|
||||
: dlgAgentProperty(f, frame, wxT("dlgSchedule"))
|
||||
{
|
||||
schedule = node;
|
||||
job = j;
|
||||
if (job)
|
||||
jobId = job->GetRecId();
|
||||
else
|
||||
jobId = 0;
|
||||
|
||||
lstExceptions->CreateColumns(0, _("Date"), _("Time"), 90);
|
||||
lstExceptions->AddColumn(wxT("dirty"), 0);
|
||||
lstExceptions->AddColumn(wxT("id"), 0);
|
||||
|
||||
txtID->Disable();
|
||||
|
||||
btnChangeException->Disable();
|
||||
btnRemoveException->Disable();
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < 24 ; i++)
|
||||
chkHours->Append(wxString::Format(wxT("%02d"), i));
|
||||
|
||||
for (i = 0 ; i < 60 ; i++)
|
||||
chkMinutes->Append(wxString::Format(wxT("%02d"), i));
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgSchedule::GetObject()
|
||||
{
|
||||
return schedule;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __WXMAC__
|
||||
void dlgSchedule::OnChangeSize(wxSizeEvent &ev)
|
||||
{
|
||||
lstExceptions->SetSize(wxDefaultCoord, wxDefaultCoord,
|
||||
ev.GetSize().GetWidth(), ev.GetSize().GetHeight() - 350);
|
||||
if (GetAutoLayout())
|
||||
{
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int dlgSchedule::Go(bool modal)
|
||||
{
|
||||
int returncode;
|
||||
|
||||
if (schedule)
|
||||
{
|
||||
// edit mode
|
||||
recId = schedule->GetRecId();
|
||||
txtID->SetValue(NumToStr(recId));
|
||||
chkEnabled->SetValue(schedule->GetEnabled());
|
||||
calStart->SetValue(schedule->GetStart().GetDateOnly());
|
||||
timStart->SetTime(schedule->GetStart());
|
||||
if (schedule->GetEnd().IsValid())
|
||||
{
|
||||
calEnd->SetValue(schedule->GetEnd().GetDateOnly());
|
||||
timEnd->SetTime(schedule->GetEnd());
|
||||
}
|
||||
else
|
||||
{
|
||||
calEnd->SetValue(wxInvalidDateTime);
|
||||
timEnd->SetTime(wxInvalidDateTime);
|
||||
timEnd->Disable();
|
||||
}
|
||||
|
||||
unsigned int x;
|
||||
for (x = 0; x < schedule->GetMonths().Length(); x++ )
|
||||
if (schedule->GetMonths()[x] == 't') chkMonths->Check(x, true);
|
||||
|
||||
for (x = 0; x < schedule->GetMonthdays().Length(); x++ )
|
||||
if (schedule->GetMonthdays()[x] == 't') chkMonthdays->Check(x, true);
|
||||
|
||||
for (x = 0; x < schedule->GetWeekdays().Length(); x++ )
|
||||
if (schedule->GetWeekdays()[x] == 't') chkWeekdays->Check(x, true);
|
||||
|
||||
for (x = 0; x < schedule->GetHours().Length(); x++ )
|
||||
if (schedule->GetHours()[x] == 't') chkHours->Check(x, true);
|
||||
|
||||
for (x = 0; x < schedule->GetMinutes().Length(); x++ )
|
||||
if (schedule->GetMinutes()[x] == 't') chkMinutes->Check(x, true);
|
||||
|
||||
wxString id, dateToken, timeToken;
|
||||
wxDateTime val;
|
||||
long pos = 0;
|
||||
wxStringTokenizer tkz(schedule->GetExceptions(), wxT("|"));
|
||||
|
||||
while (tkz.HasMoreTokens() )
|
||||
{
|
||||
dateToken.Empty();
|
||||
timeToken.Empty();
|
||||
|
||||
// First is the ID
|
||||
id = tkz.GetNextToken();
|
||||
|
||||
// Look for a date
|
||||
if (tkz.HasMoreTokens())
|
||||
dateToken = tkz.GetNextToken();
|
||||
|
||||
// Look for a time
|
||||
if (tkz.HasMoreTokens())
|
||||
timeToken = tkz.GetNextToken();
|
||||
|
||||
if (!dateToken.IsEmpty() && !timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseDate(dateToken);
|
||||
val.ParseTime(timeToken);
|
||||
pos = lstExceptions->AppendItem(0, val.FormatDate(), val.FormatTime());
|
||||
}
|
||||
else if (!dateToken.IsEmpty() && timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseDate(dateToken);
|
||||
pos = lstExceptions->AppendItem(0, val.FormatDate(), _("<any>"));
|
||||
}
|
||||
else if (dateToken.IsEmpty() && !timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseTime(timeToken);
|
||||
pos = lstExceptions->AppendItem(0, _("<any>"), val.FormatTime());
|
||||
}
|
||||
|
||||
lstExceptions->SetItem(pos, 2, BoolToStr(false));
|
||||
lstExceptions->SetItem(pos, 3, id);
|
||||
|
||||
}
|
||||
|
||||
wxNotifyEvent ev;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode
|
||||
}
|
||||
|
||||
// setup de-/select buttons
|
||||
InitSelectAll();
|
||||
|
||||
returncode = dlgProperty::Go(modal);
|
||||
|
||||
SetSqlReadOnly(true);
|
||||
|
||||
return returncode;
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgSchedule::CreateObject(pgCollection *collection)
|
||||
{
|
||||
pgObject *obj = scheduleFactory.CreateObjects(collection, 0, wxT(" AND jscid=") + NumToStr(recId) + wxT("\n"));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnChangeCal(wxCalendarEvent &ev)
|
||||
{
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnChangeCom(wxCommandEvent &ev)
|
||||
{
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::CheckChange()
|
||||
{
|
||||
timEnd->Enable(calEnd->GetValue().IsValid());
|
||||
|
||||
wxString name = GetName();
|
||||
bool enable = true;
|
||||
|
||||
if (statusBar)
|
||||
statusBar->SetStatusText(wxEmptyString);
|
||||
|
||||
InitSelectAll();
|
||||
|
||||
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
|
||||
CheckValid(enable, calStart->GetValue().IsValid(), _("Please specify start date."));
|
||||
|
||||
if (enable)
|
||||
{
|
||||
EnableOK(!GetSql().IsEmpty());
|
||||
}
|
||||
else
|
||||
EnableOK(false);
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnSelChangeException(wxListEvent &ev)
|
||||
{
|
||||
int sel = lstExceptions->GetSelection();
|
||||
if (sel >= 0)
|
||||
{
|
||||
wxString exDate = lstExceptions->GetText(sel, 0);
|
||||
wxString exTime = lstExceptions->GetText(sel, 1);
|
||||
wxDateTime val, null;
|
||||
|
||||
if (exDate == _("<any>"))
|
||||
calException->SetValue(null);
|
||||
else
|
||||
{
|
||||
val.ParseDate(exDate);
|
||||
calException->SetValue(val);
|
||||
}
|
||||
if (exTime == _("<any>"))
|
||||
timException->SetTime(null);
|
||||
else
|
||||
{
|
||||
val.ParseTime(exTime);
|
||||
timException->SetTime(val);
|
||||
}
|
||||
|
||||
btnChangeException->Enable();
|
||||
btnRemoveException->Enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnAddException(wxCommandEvent &ev)
|
||||
{
|
||||
if (!calException->GetValue().IsValid() && timException->GetValue().IsNull())
|
||||
{
|
||||
wxMessageBox(_("You must enter a valid date and/or time!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
wxString exDate, exTime;
|
||||
|
||||
if (calException->GetValue().IsValid())
|
||||
exDate = calException->GetValue().FormatDate();
|
||||
else
|
||||
exDate = _("<any>");
|
||||
|
||||
if (!timException->GetValue().IsNull())
|
||||
exTime = timException->GetValue().Format();
|
||||
else
|
||||
exTime = _("<any>");
|
||||
|
||||
for (int pos = 0; pos < lstExceptions->GetItemCount(); pos++)
|
||||
{
|
||||
|
||||
if (lstExceptions->GetText(pos, 0) == exDate &&
|
||||
lstExceptions->GetText(pos, 1) == exTime)
|
||||
{
|
||||
wxMessageBox(_("The specified exception already exists!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(pos, 0) == exDate &&
|
||||
lstExceptions->GetText(pos, 1) == _("<any>"))
|
||||
{
|
||||
wxMessageBox(_("An exception already exists for any time on this date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(pos, 1) == exTime &&
|
||||
lstExceptions->GetText(pos, 0) == _("<any>"))
|
||||
{
|
||||
wxMessageBox(_("An exception already exists for this time on any date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lstExceptions->AppendItem(0, exDate, exTime);
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnChangeException(wxCommandEvent &ev)
|
||||
{
|
||||
if (!calException->GetValue().IsValid() && timException->GetValue().IsNull())
|
||||
{
|
||||
wxMessageBox(_("You must enter a valid date and/or time!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
wxString exDate, exTime;
|
||||
|
||||
if (calException->GetValue().IsValid())
|
||||
exDate = calException->GetValue().FormatDate();
|
||||
else
|
||||
exDate = _("<any>");
|
||||
|
||||
if (!timException->GetValue().IsNull())
|
||||
exTime = timException->GetValue().Format();
|
||||
else
|
||||
exTime = _("<any>");
|
||||
|
||||
long item = lstExceptions->GetFocusedItem();
|
||||
|
||||
for (int pos = 0; pos < lstExceptions->GetItemCount(); pos++)
|
||||
{
|
||||
if (item != pos)
|
||||
{
|
||||
if (lstExceptions->GetText(pos, 0) == exDate &&
|
||||
lstExceptions->GetText(pos, 1) == exTime)
|
||||
{
|
||||
wxMessageBox(_("The specified exception already exists!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(pos, 0) == exDate &&
|
||||
lstExceptions->GetText(pos, 1) == _("<any>"))
|
||||
{
|
||||
wxMessageBox(_("An exception already exists for any time on this date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(pos, 1) == exTime &&
|
||||
lstExceptions->GetText(pos, 0) == _("<any>"))
|
||||
{
|
||||
wxMessageBox(_("An exception already exists for this time on any date!"), _("Add exception"), wxICON_EXCLAMATION | wxOK, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lstExceptions->SetItem(item, 0, exDate);
|
||||
lstExceptions->SetItem(item, 1, exTime);
|
||||
lstExceptions->SetItem(item, 2, BoolToStr(true));
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnRemoveException(wxCommandEvent &ev)
|
||||
{
|
||||
if (lstExceptions->GetText(lstExceptions->GetFocusedItem(), 3) != wxEmptyString)
|
||||
{
|
||||
deleteExceptions.Add(lstExceptions->GetText(lstExceptions->GetFocusedItem(), 3));
|
||||
}
|
||||
lstExceptions->DeleteCurrentItem();
|
||||
|
||||
btnChangeException->Disable();
|
||||
btnRemoveException->Disable();
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
wxString dlgSchedule::GetComment()
|
||||
{
|
||||
return txtComment->GetValue();
|
||||
}
|
||||
|
||||
|
||||
wxString dlgSchedule::GetInsertSql()
|
||||
{
|
||||
wxString sql;
|
||||
if (!schedule)
|
||||
{
|
||||
wxString name = GetName();
|
||||
wxString jscjobid, list = wxT("NULL");
|
||||
if (jobId)
|
||||
jscjobid = NumToStr(jobId);
|
||||
else
|
||||
jscjobid = wxT("<JobId>");
|
||||
|
||||
// Build the various arrays of values
|
||||
sql = wxT("INSERT INTO pgagent.pga_schedule (jscid, jscjobid, jscname, jscdesc, jscminutes, jschours, jscweekdays, jscmonthdays, jscmonths, jscenabled, jscstart, jscend)\n")
|
||||
wxT("VALUES(<SchId>, ") + jscjobid + wxT(", ") + qtDbString(name) + wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ")
|
||||
+ wxT("'") + ChkListBox2PgArray(chkMinutes) + wxT("', ")
|
||||
+ wxT("'") + ChkListBox2PgArray(chkHours) + wxT("', ")
|
||||
+ wxT("'") + ChkListBox2PgArray(chkWeekdays) + wxT("', ")
|
||||
+ wxT("'") + ChkListBox2PgArray(chkMonthdays) + wxT("', ")
|
||||
+ wxT("'") + ChkListBox2PgArray(chkMonths) + wxT("', ")
|
||||
+ BoolToStr(chkEnabled->GetValue()) + wxT(", ")
|
||||
+ wxT("'") + DateToAnsiStr(calStart->GetValue() + timStart->GetValue()) + wxT("'");
|
||||
|
||||
if (calEnd->GetValue().IsValid())
|
||||
sql += wxT(", '") + DateToAnsiStr(calEnd->GetValue() + timEnd->GetValue()) + wxT("'");
|
||||
else
|
||||
sql += wxT(", NULL");
|
||||
|
||||
sql += wxT(");\n");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
wxString dlgSchedule::GetUpdateSql()
|
||||
{
|
||||
wxString sql, name;
|
||||
name = GetName();
|
||||
|
||||
if (schedule)
|
||||
{
|
||||
// edit mode
|
||||
wxString vars;
|
||||
|
||||
if (name != schedule->GetName())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscname = ") + qtDbString(name));
|
||||
}
|
||||
if (txtComment->GetValue() != schedule->GetComment())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscdesc = ") + qtDbString(txtComment->GetValue()));
|
||||
}
|
||||
|
||||
if ((!chkEnabled->IsChecked() && schedule->GetEnabled()) || (chkEnabled->IsChecked() && !schedule->GetEnabled()))
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscenabled = ") + BoolToStr(chkEnabled->IsChecked()));
|
||||
}
|
||||
|
||||
if (calStart->GetValue() + timStart->GetValue() != schedule->GetStart())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscstart = '") + DateToAnsiStr(calStart->GetValue() + timStart->GetValue()) + wxT("'"));
|
||||
}
|
||||
|
||||
if (calEnd->GetValue().IsValid())
|
||||
{
|
||||
if (schedule->GetEnd().IsValid())
|
||||
{
|
||||
if (calEnd->GetValue() + timEnd->GetValue() != schedule->GetEnd())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscend = '") + DateToAnsiStr(calEnd->GetValue() + timEnd->GetValue()) + wxT("'"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscend = '") + DateToAnsiStr(calEnd->GetValue() + wxTimeSpan()) + wxT("'"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (schedule->GetEnd().IsValid())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscend = NULL"));
|
||||
}
|
||||
}
|
||||
|
||||
if (ChkListBox2StrArray(chkMinutes) != schedule->GetMinutes())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscminutes = '") + ChkListBox2PgArray(chkMinutes) + wxT("'"));
|
||||
}
|
||||
|
||||
if (ChkListBox2StrArray(chkHours) != schedule->GetHours())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jschours = '") + ChkListBox2PgArray(chkHours) + wxT("'"));
|
||||
}
|
||||
|
||||
if (ChkListBox2StrArray(chkWeekdays) != schedule->GetWeekdays())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscweekdays = '") + ChkListBox2PgArray(chkWeekdays) + wxT("'"));
|
||||
}
|
||||
|
||||
if (ChkListBox2StrArray(chkMonthdays) != schedule->GetMonthdays())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscmonthdays = '") + ChkListBox2PgArray(chkMonthdays) + wxT("'"));
|
||||
}
|
||||
|
||||
if (ChkListBox2StrArray(chkMonths) != schedule->GetMonths())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jscmonths = '") + ChkListBox2PgArray(chkMonths) + wxT("'"));
|
||||
}
|
||||
|
||||
if (!vars.IsEmpty())
|
||||
sql = wxT("UPDATE pgagent.pga_schedule SET ") + vars + wxT("\n")
|
||||
wxT(" WHERE jscid=") + NumToStr(recId) + wxT(";\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode
|
||||
// Handled by GetInsertSQL
|
||||
}
|
||||
|
||||
unsigned int x = 0;
|
||||
int y = 0;
|
||||
wxDateTime tmpDateTime;
|
||||
wxString newDate, newTime;
|
||||
|
||||
// Remove old exceptions
|
||||
for (x = 0; x < deleteExceptions.Count(); x++)
|
||||
{
|
||||
sql += wxT("DELETE FROM pgagent.pga_exception\n WHERE jexid = ") + deleteExceptions[x] + wxT(";\n");
|
||||
}
|
||||
|
||||
// Update dirty exceptions
|
||||
for (y = 0; y < lstExceptions->GetItemCount(); y++)
|
||||
{
|
||||
if (lstExceptions->GetText(y, 2) == BoolToStr(true) &&
|
||||
lstExceptions->GetText(y, 3) != wxEmptyString)
|
||||
{
|
||||
if (lstExceptions->GetText(y, 0) == _("<any>"))
|
||||
newDate = wxT("null");
|
||||
else
|
||||
{
|
||||
tmpDateTime.ParseFormat(lstExceptions->GetText(y, 0), wxT("%x"));
|
||||
newDate = wxT("'") + tmpDateTime.FormatISODate() + wxT("'");
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(y, 1) == _("<any>"))
|
||||
newTime = wxT("null");
|
||||
else
|
||||
{
|
||||
tmpDateTime.ParseTime(lstExceptions->GetText(y, 1));
|
||||
newTime = wxT("'") + tmpDateTime.FormatISOTime() + wxT("'");
|
||||
}
|
||||
|
||||
sql += wxT("UPDATE pgagent.pga_exception SET jexdate = ") + newDate +
|
||||
wxT(", jextime = ") + newTime + wxT("\n WHERE jexid = ") +
|
||||
lstExceptions->GetText(y, 3) + wxT(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new exceptions
|
||||
for (y = 0; y < lstExceptions->GetItemCount(); y++)
|
||||
{
|
||||
if (lstExceptions->GetText(y, 2) == wxEmptyString &&
|
||||
lstExceptions->GetText(y, 3) == wxEmptyString)
|
||||
{
|
||||
if (lstExceptions->GetText(y, 0) == _("<any>"))
|
||||
newDate = wxT("null");
|
||||
else
|
||||
{
|
||||
tmpDateTime.ParseFormat(lstExceptions->GetText(y, 0), wxT("%x"));
|
||||
newDate = wxT("'") + tmpDateTime.FormatISODate() + wxT("'");
|
||||
}
|
||||
|
||||
if (lstExceptions->GetText(y, 1) == _("<any>"))
|
||||
newTime = wxT("null");
|
||||
else
|
||||
{
|
||||
tmpDateTime.ParseTime(lstExceptions->GetText(y, 1));
|
||||
newTime = wxT("'") + tmpDateTime.FormatISOTime() + wxT("'");
|
||||
}
|
||||
|
||||
sql += wxT("INSERT INTO pgagent.pga_exception (jexscid, jexdate, jextime)\n VALUES (")
|
||||
+ NumToStr(recId) + wxT(", ") + newDate + wxT(", ") + newTime + wxT(");\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
const wxString dlgSchedule::ChkListBox2PgArray(wxCheckListBox *lb)
|
||||
{
|
||||
wxString res = wxT("{");
|
||||
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (lb->IsChecked(x))
|
||||
res += wxT("t,");
|
||||
else
|
||||
res += wxT("f,");
|
||||
}
|
||||
if (res.Length() > 1)
|
||||
res.RemoveLast();
|
||||
|
||||
res += wxT("}");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
const wxString dlgSchedule::ChkListBox2StrArray(wxCheckListBox *lb)
|
||||
{
|
||||
wxString res;
|
||||
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (lb->IsChecked(x))
|
||||
res += wxT("t");
|
||||
else
|
||||
res += wxT("f");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::OnSelectAll(wxCommandEvent &ev, int origin)
|
||||
{
|
||||
bool check = false;
|
||||
wxBitmapButton *btn;
|
||||
wxCheckListBox *lb;
|
||||
wxString tooltip;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case 1:
|
||||
btn = ((wxBitmapButton *)btnWeekdays);
|
||||
lb = chkWeekdays;
|
||||
break;
|
||||
case 2:
|
||||
btn = ((wxBitmapButton *)btnMonthdays);
|
||||
lb = chkMonthdays;
|
||||
break;
|
||||
case 3:
|
||||
btn = ((wxBitmapButton *)btnMonths);
|
||||
lb = chkMonths;
|
||||
break;
|
||||
case 4:
|
||||
btn = ((wxBitmapButton *)btnHours);
|
||||
lb = chkHours;
|
||||
break;
|
||||
case 5:
|
||||
btn = ((wxBitmapButton *)btnMinutes);
|
||||
lb = chkMinutes;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
lb->Check(x, check);
|
||||
}
|
||||
|
||||
CheckChange();
|
||||
}
|
||||
|
||||
|
||||
void dlgSchedule::InitSelectAll()
|
||||
{
|
||||
bool check = false;
|
||||
wxBitmapButton *btn;
|
||||
wxCheckListBox *lb;
|
||||
wxString tooltip;
|
||||
|
||||
btn = ((wxBitmapButton *)btnWeekdays);
|
||||
lb = chkWeekdays;
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
btn->SetBitmapLabel(*check_png_bmp);
|
||||
tooltip = _("Select all week days");
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->SetBitmapLabel(*uncheck_png_bmp);
|
||||
tooltip = _("Deselect all week days");
|
||||
}
|
||||
btn->SetToolTip(tooltip);
|
||||
|
||||
check = false;
|
||||
btn = ((wxBitmapButton *)btnMonthdays);
|
||||
lb = chkMonthdays;
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
btn->SetBitmapLabel(*check_png_bmp);
|
||||
tooltip = _("Select all month days");
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->SetBitmapLabel(*uncheck_png_bmp);
|
||||
tooltip = _("Deselect all month days");
|
||||
}
|
||||
btn->SetToolTip(tooltip);
|
||||
|
||||
check = false;
|
||||
btn = ((wxBitmapButton *)btnMonths);
|
||||
lb = chkMonths;
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
btn->SetBitmapLabel(*check_png_bmp);
|
||||
tooltip = _("Select all months");
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->SetBitmapLabel(*uncheck_png_bmp);
|
||||
tooltip = _("Deselect all months");
|
||||
}
|
||||
btn->SetToolTip(tooltip);
|
||||
|
||||
check = false;
|
||||
btn = ((wxBitmapButton *)btnHours);
|
||||
lb = chkHours;
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
btn->SetBitmapLabel(*check_png_bmp);
|
||||
tooltip = _("Select all hours");
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->SetBitmapLabel(*uncheck_png_bmp);
|
||||
tooltip = _("Deselect all hours");
|
||||
}
|
||||
btn->SetToolTip(tooltip);
|
||||
|
||||
|
||||
check = false;
|
||||
btn = ((wxBitmapButton *)btnMinutes);
|
||||
lb = chkMinutes;
|
||||
for (unsigned int x = 0; x < lb->GetCount(); x++)
|
||||
{
|
||||
if (!lb->IsChecked(x))
|
||||
{
|
||||
check = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
btn->SetBitmapLabel(*check_png_bmp);
|
||||
tooltip = _("Select all minutes");
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->SetBitmapLabel(*uncheck_png_bmp);
|
||||
tooltip = _("Deselect all minutes");
|
||||
}
|
||||
btn->SetToolTip(tooltip);
|
||||
}
|
||||
483
agent/dlgStep.cpp
Normal file
483
agent/dlgStep.cpp
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dlgStep.cpp - PostgreSQL Step Property
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/misc.h"
|
||||
#include "agent/dlgStep.h"
|
||||
#include "agent/pgaStep.h"
|
||||
#include "dlg/dlgSelectDatabase.h"
|
||||
#include "schema/pgTable.h"
|
||||
|
||||
|
||||
// pointer to controls
|
||||
#define txtID CTRL_TEXT("txtID")
|
||||
#define chkEnabled CTRL_CHECKBOX("chkEnabled")
|
||||
#define rbxKind CTRL_RADIOBOX("rbxKind")
|
||||
#define rbxOnError CTRL_RADIOBOX("rbxOnError")
|
||||
#define pnlDefinition CTRL_PANEL("pnlDefinition")
|
||||
#define txtSqlBox CTRL_TEXT("txtSqlBox")
|
||||
#define cbDatabase CTRL_COMBOBOX2("cbDatabase")
|
||||
#define txtConnStr CTRL_TEXT("txtConnStr")
|
||||
#define btnSelDatabase CTRL_BUTTON("btnSelDatabase")
|
||||
#define rbRemoteConn CTRL_RADIOBUTTON("rbRemoteConn")
|
||||
#define rbLocalConn CTRL_RADIOBUTTON("rbLocalConn")
|
||||
|
||||
#define CTL_SQLBOX 188
|
||||
|
||||
BEGIN_EVENT_TABLE(dlgStep, dlgAgentProperty)
|
||||
EVT_CHECKBOX(XRCID("chkEnabled"), dlgProperty::OnChange)
|
||||
EVT_COMBOBOX(XRCID("cbDatabase"), dlgProperty::OnChange)
|
||||
EVT_RADIOBOX(XRCID("rbxKind"), dlgProperty::OnChange)
|
||||
EVT_RADIOBOX(XRCID("rbxOnError"), dlgProperty::OnChange)
|
||||
EVT_TEXT(XRCID("txtConnStr"), dlgProperty::OnChange)
|
||||
EVT_STC_MODIFIED(CTL_SQLBOX, dlgProperty::OnChangeStc)
|
||||
EVT_BUTTON(XRCID("btnSelDatabase"), dlgStep::OnSelectDatabase)
|
||||
EVT_RADIOBUTTON(XRCID("rbRemoteConn"), dlgStep::OnSelRemoteConn)
|
||||
EVT_RADIOBUTTON(XRCID("rbLocalConn"), dlgStep::OnSelLocalConn)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
|
||||
dlgProperty *pgaStepFactory::CreateDialog(frmMain *frame, pgObject *node, pgObject *parent)
|
||||
{
|
||||
return new dlgStep(this, frame, (pgaStep *)node, (pgaJob *)parent);
|
||||
}
|
||||
|
||||
|
||||
dlgStep::dlgStep(pgaFactory *f, frmMain *frame, pgaStep *node, pgaJob *j)
|
||||
: dlgAgentProperty(f, frame, wxT("dlgStep"))
|
||||
{
|
||||
step = node;
|
||||
job = j;
|
||||
if (job)
|
||||
jobId = job->GetRecId();
|
||||
else
|
||||
jobId = 0;
|
||||
|
||||
sqlBox = new ctlSQLBox(pnlDefinition, CTL_SQLBOX, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxSUNKEN_BORDER | wxTE_RICH2);
|
||||
|
||||
wxWindow *placeholder = CTRL_TEXT("txtSqlBox");
|
||||
wxSizer *sizer = placeholder->GetContainingSizer();
|
||||
sizer->Add(sqlBox, 1, wxRIGHT | wxGROW, 5);
|
||||
sizer->Detach(placeholder);
|
||||
delete placeholder;
|
||||
sizer->Layout();
|
||||
|
||||
|
||||
txtID->Disable();
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgStep::GetObject()
|
||||
{
|
||||
return step;
|
||||
}
|
||||
|
||||
|
||||
int dlgStep::Go(bool modal)
|
||||
{
|
||||
int returncode;
|
||||
|
||||
hasConnStrSupport = connection->TableHasColumn(wxT("pgagent"), wxT("pga_jobstep"), wxT("jstconnstr"));
|
||||
cbDatabase->Append(wxT(" "));
|
||||
cbDatabase->SetSelection(0);
|
||||
|
||||
pgSet *db = connection->ExecuteSet(wxT("SELECT datname FROM pg_database"));
|
||||
if (db)
|
||||
{
|
||||
while (!db->Eof())
|
||||
{
|
||||
cbDatabase->Append(db->GetVal(0));
|
||||
db->MoveNext();
|
||||
}
|
||||
delete db;
|
||||
}
|
||||
|
||||
if (step)
|
||||
{
|
||||
// edit mode
|
||||
recId = step->GetRecId();
|
||||
txtID->SetValue(NumToStr(recId));
|
||||
|
||||
if (step->HasConnectionString())
|
||||
{
|
||||
rbRemoteConn->SetValue(true);
|
||||
txtConnStr->Enable(true);
|
||||
txtConnStr->ChangeValue(step->GetConnStr());
|
||||
btnSelDatabase->Enable(true);
|
||||
cbDatabase->Enable(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
rbLocalConn->SetValue(true);
|
||||
if (step->GetDbname().IsEmpty())
|
||||
cbDatabase->SetSelection(0);
|
||||
else
|
||||
cbDatabase->SetValue(step->GetDbname());
|
||||
}
|
||||
|
||||
rbxKind->SetSelection(wxString(wxT("sb")).Find(step->GetKindChar()));
|
||||
rbxOnError->SetSelection(wxString(wxT("fsi")).Find(step->GetOnErrorChar()));
|
||||
sqlBox->SetText(step->GetCode());
|
||||
|
||||
chkEnabled->SetValue(step->GetEnabled());
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode
|
||||
rbLocalConn->SetValue(true);
|
||||
cbDatabase->Enable(true);
|
||||
btnSelDatabase->Enable(false);
|
||||
txtConnStr->Enable(false);
|
||||
if (!hasConnStrSupport)
|
||||
rbLocalConn->Enable(false);
|
||||
}
|
||||
|
||||
returncode = dlgProperty::Go(modal);
|
||||
|
||||
SetSqlReadOnly(true);
|
||||
|
||||
return returncode;
|
||||
}
|
||||
|
||||
|
||||
pgObject *dlgStep::CreateObject(pgCollection *collection)
|
||||
{
|
||||
wxString name = GetName();
|
||||
|
||||
pgObject *obj = stepFactory.CreateObjects(collection, 0, wxT(" AND jstid=") + NumToStr(recId) + wxT("\n"));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
void dlgStep::CheckChange()
|
||||
{
|
||||
wxString name = GetName();
|
||||
bool enable;
|
||||
if (step)
|
||||
{
|
||||
enable = name != step->GetName()
|
||||
|| chkEnabled->GetValue() != step->GetEnabled()
|
||||
|| rbxKind->GetSelection() != wxString(wxT("sb")).Find(step->GetKindChar())
|
||||
|| rbxOnError->GetSelection() != wxString(wxT("fsi")).Find(step->GetOnErrorChar())
|
||||
|| txtComment->GetValue() != step->GetComment()
|
||||
|| sqlBox->GetText() != step->GetCode();
|
||||
|
||||
if (!enable && rbxKind->GetSelection() == 0)
|
||||
{
|
||||
if (hasConnStrSupport)
|
||||
{
|
||||
if (step->HasConnectionString())
|
||||
{
|
||||
if (rbRemoteConn->GetValue())
|
||||
enable = txtConnStr->GetValue().Trim() != step->GetConnStr();
|
||||
else
|
||||
enable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rbRemoteConn->GetValue())
|
||||
enable = true;
|
||||
else
|
||||
enable = cbDatabase->GetValue().Trim() != step->GetDbname();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
enable = cbDatabase->GetValue().Trim() != step->GetDbname();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
enable = true;
|
||||
}
|
||||
|
||||
if (statusBar)
|
||||
statusBar->SetStatusText(wxEmptyString);
|
||||
|
||||
CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
|
||||
CheckValid(enable, sqlBox->GetLength() > 0, _("Please specify code to execute."));
|
||||
|
||||
// Disable/enable the database combo
|
||||
if (rbxKind->GetSelection() == 1)
|
||||
{
|
||||
rbRemoteConn->Enable(false);
|
||||
rbLocalConn->Enable(false);
|
||||
// I don't see any reason to make
|
||||
// the database combobox selection to 0
|
||||
//cbDatabase->SetSelection(0);
|
||||
txtConnStr->Enable(false);
|
||||
btnSelDatabase->Enable(false);
|
||||
cbDatabase->Enable(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasConnStrSupport)
|
||||
{
|
||||
rbRemoteConn->Enable(true);
|
||||
rbLocalConn->Enable(true);
|
||||
if (rbRemoteConn->GetValue())
|
||||
{
|
||||
wxString validConnStr;
|
||||
|
||||
btnSelDatabase->Enable(true);
|
||||
txtConnStr->Enable(true);
|
||||
cbDatabase->Enable(false);
|
||||
CheckValid(enable, !txtConnStr->GetValue().Trim().IsEmpty(), _("Please select a connection string."));
|
||||
CheckValid(enable, dlgSelectDatabase::getValidConnectionString(txtConnStr->GetValue().Trim(), validConnStr), _("Please enter a valid connection string"));
|
||||
}
|
||||
else
|
||||
{
|
||||
cbDatabase->Enable(true);
|
||||
btnSelDatabase->Enable(false);
|
||||
txtConnStr->Enable(false);
|
||||
CheckValid(enable, !cbDatabase->GetValue().Trim().IsEmpty(), _("Please select a database."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cbDatabase->Enable(true);
|
||||
// Make sure both radio buttons are disabled
|
||||
rbRemoteConn->Enable(false);
|
||||
rbLocalConn->Enable(false);
|
||||
CheckValid(enable, !cbDatabase->GetValue().Trim().IsEmpty(), _("Please select a database."));
|
||||
}
|
||||
}
|
||||
|
||||
EnableOK(enable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxString dlgStep::GetComment()
|
||||
{
|
||||
return txtComment->GetValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxString dlgStep::GetInsertSql()
|
||||
{
|
||||
wxString sql;
|
||||
|
||||
if (!step)
|
||||
{
|
||||
wxString name = GetName();
|
||||
wxString kind = wxT("sb")[rbxKind->GetSelection()];
|
||||
wxString onerror = wxT("fsi")[rbxOnError->GetSelection()];
|
||||
wxString db, connstr;
|
||||
wxString jstjobid;
|
||||
if (jobId)
|
||||
jstjobid = NumToStr(jobId);
|
||||
else
|
||||
jstjobid = wxT("<JobId>");
|
||||
// SQL script expected
|
||||
if (rbxKind->GetSelection() == 0)
|
||||
{
|
||||
if (hasConnStrSupport && rbRemoteConn->GetValue())
|
||||
{
|
||||
connstr = qtDbString(txtConnStr->GetValue().Trim());
|
||||
db = wxT("''");
|
||||
}
|
||||
else
|
||||
{
|
||||
db = qtDbString(cbDatabase->GetValue().Trim());
|
||||
connstr = wxT("''");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
db = wxT("''");
|
||||
connstr = wxT("''");
|
||||
}
|
||||
|
||||
sql = wxT("INSERT INTO pgagent.pga_jobstep (jstid, jstjobid, jstname, jstdesc, jstenabled, jstkind, jstonerror, jstcode, jstdbname");
|
||||
if (hasConnStrSupport)
|
||||
sql += wxT(", jstconnstr");
|
||||
sql += wxT(")\n ") \
|
||||
wxT("SELECT <StpId>, ") + jstjobid + wxT(", ") + qtDbString(name) + wxT(", ") + qtDbString(txtComment->GetValue()) + wxT(", ")
|
||||
+ BoolToStr(chkEnabled->GetValue()) + wxT(", ") + qtDbString(kind) + wxT(", ")
|
||||
+ qtDbString(onerror) + wxT(", ") + qtDbString(sqlBox->GetText()) + wxT(", ") + db;
|
||||
if (hasConnStrSupport)
|
||||
{
|
||||
sql += wxT(", ") + connstr;
|
||||
}
|
||||
sql += wxT(";\n");
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
wxString dlgStep::GetUpdateSql()
|
||||
{
|
||||
wxString sql;
|
||||
|
||||
if (step)
|
||||
{
|
||||
// edit mode
|
||||
wxString name = GetName();
|
||||
wxString kind = wxT("sb")[rbxKind->GetSelection()];
|
||||
|
||||
wxString vars;
|
||||
if (name != step->GetName())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstname=") + qtDbString(name));
|
||||
}
|
||||
if (chkEnabled->GetValue() != step->GetEnabled())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstenabled=") + BoolToStr(chkEnabled->GetValue()));
|
||||
}
|
||||
if (hasConnStrSupport && kind == wxT("s"))
|
||||
{
|
||||
if (rbRemoteConn->GetValue())
|
||||
{
|
||||
if (step->HasConnectionString())
|
||||
{
|
||||
if (txtConnStr->GetValue().Trim() != step->GetConnStr())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstconnstr=") + qtDbString(txtConnStr->GetValue().Trim()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstconnstr=") + qtDbString(txtConnStr->GetValue().Trim()) + wxT(", "));
|
||||
vars.Append(wxT("jstdbname=''"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (step->HasConnectionString())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()) + wxT(", "));
|
||||
vars.Append(wxT("jstconnstr=''"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cbDatabase->GetValue().Trim() != step->GetDbname())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (kind == wxT("s"))
|
||||
{
|
||||
if (cbDatabase->GetValue().Trim() != step->GetDbname())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstdbname=") + qtDbString(cbDatabase->GetValue().Trim()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstdbname=''"));
|
||||
if (hasConnStrSupport)
|
||||
vars.Append(wxT(", jstconnstr=''"));
|
||||
}
|
||||
if (rbxKind->GetSelection() != kind)
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstkind=") + qtDbString(kind));
|
||||
}
|
||||
if (rbxOnError->GetSelection() != wxString(wxT("fsi")).Find(step->GetOnErrorChar()))
|
||||
{
|
||||
wxString onerror = wxT("fsi")[rbxOnError->GetSelection()];
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstonerror='") + onerror + wxT("'"));
|
||||
}
|
||||
if (txtComment->GetValue() != step->GetComment())
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstdesc=") + qtDbString(txtComment->GetValue()));
|
||||
}
|
||||
if (sqlBox->GetText() != step->GetCode())
|
||||
{
|
||||
{
|
||||
if (!vars.IsEmpty())
|
||||
vars.Append(wxT(", "));
|
||||
vars.Append(wxT("jstcode=") + qtDbString(sqlBox->GetText()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!vars.IsEmpty())
|
||||
sql = wxT("UPDATE pgagent.pga_jobstep\n")
|
||||
wxT(" SET ") + vars + wxT("\n")
|
||||
wxT(" WHERE jstid=") + NumToStr(step->GetRecId()) +
|
||||
wxT(";\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// create mode; handled by GetInsertSql()
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
bool dlgStep::IsUpToDate()
|
||||
{
|
||||
if (step && !step->IsUpToDate())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void dlgStep::OnSelectDatabase(wxCommandEvent &ev)
|
||||
{
|
||||
dlgSelectDatabase dlgSD(this, wxID_ANY);
|
||||
if (dlgSD.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxString strConnStr = dlgSD.getConnInfo();
|
||||
if (!strConnStr.IsEmpty())
|
||||
txtConnStr->SetValue(strConnStr);
|
||||
}
|
||||
}
|
||||
|
||||
void dlgStep::OnSelRemoteConn(wxCommandEvent &ev)
|
||||
{
|
||||
if (rbRemoteConn->GetValue())
|
||||
{
|
||||
cbDatabase->Enable(false);
|
||||
btnSelDatabase->Enable(true);
|
||||
txtConnStr->Enable(true);
|
||||
}
|
||||
dlgProperty::OnChange(ev);
|
||||
}
|
||||
|
||||
void dlgStep::OnSelLocalConn(wxCommandEvent &ev)
|
||||
{
|
||||
if (rbLocalConn->GetValue())
|
||||
{
|
||||
cbDatabase->Enable(true);
|
||||
btnSelDatabase->Enable(false);
|
||||
txtConnStr->Enable(false);
|
||||
}
|
||||
dlgProperty::OnChange(ev);
|
||||
}
|
||||
|
||||
|
||||
23
agent/module.mk
Normal file
23
agent/module.mk
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/agent/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
agent/dlgJob.cpp \
|
||||
agent/dlgSchedule.cpp \
|
||||
agent/dlgStep.cpp \
|
||||
agent/pgaJob.cpp \
|
||||
agent/pgaSchedule.cpp \
|
||||
agent/pgaStep.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
agent/module.mk
|
||||
|
||||
|
||||
397
agent/pgaJob.cpp
Normal file
397
agent/pgaJob.cpp
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// pgaJob.h - PostgreSQL Agent Job
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/misc.h"
|
||||
#include "frm/frmMain.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgCollection.h"
|
||||
#include "schema/pgDatabase.h"
|
||||
#include "agent/pgaJob.h"
|
||||
#include "agent/pgaStep.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
|
||||
pgaJob::pgaJob(const wxString &newName)
|
||||
: pgServerObject(jobFactory, newName)
|
||||
{
|
||||
}
|
||||
|
||||
wxString pgaJob::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent job");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent job");
|
||||
break;
|
||||
case PROPERTIESREPORT:
|
||||
message = _("pgAgent job properties report");
|
||||
break;
|
||||
case PROPERTIES:
|
||||
message = _("pgAgent job properties");
|
||||
break;
|
||||
case DDLREPORT:
|
||||
message = _("pgAgent job DDL report");
|
||||
break;
|
||||
case DEPENDENCIESREPORT:
|
||||
message = _("pgAgent job dependencies report");
|
||||
break;
|
||||
case DEPENDENCIES:
|
||||
message = _("pgAgent job dependencies");
|
||||
break;
|
||||
case DEPENDENTSREPORT:
|
||||
message = _("pgAgent job dependents report");
|
||||
break;
|
||||
case DEPENDENTS:
|
||||
message = _("pgAgent job dependents");
|
||||
break;
|
||||
case DROPEXCLUDINGDEPS:
|
||||
message = wxString::Format(_("Are you sure you wish to drop job \"%s\"?"),
|
||||
GetFullIdentifier().c_str());
|
||||
break;
|
||||
case DROPTITLE:
|
||||
message = _("Drop job?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
|
||||
message += wxT(" - ") + GetName();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
int pgaJob::GetIconId()
|
||||
{
|
||||
if (GetEnabled())
|
||||
return jobFactory.GetIconId();
|
||||
else
|
||||
return jobFactory.GetDisabledId();
|
||||
}
|
||||
|
||||
|
||||
wxMenu *pgaJob::GetNewMenu()
|
||||
{
|
||||
wxMenu *menu = pgObject::GetNewMenu();
|
||||
if (1) // check priv.
|
||||
{
|
||||
stepFactory.AppendMenu(menu);
|
||||
scheduleFactory.AppendMenu(menu);
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
bool pgaJob::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
||||
{
|
||||
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_job WHERE jobid=") + NumToStr(GetRecId()));
|
||||
}
|
||||
|
||||
|
||||
void pgaJob::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
||||
{
|
||||
if (!expandedKids)
|
||||
{
|
||||
expandedKids = true;
|
||||
|
||||
browser->RemoveDummyChild(this);
|
||||
|
||||
// Log
|
||||
wxLogInfo(wxT("Adding child objects to Job."));
|
||||
|
||||
browser->AppendCollection(this, scheduleFactory);
|
||||
browser->AppendCollection(this, stepFactory);
|
||||
}
|
||||
|
||||
if (properties)
|
||||
{
|
||||
CreateListColumns(properties);
|
||||
|
||||
properties->AppendItem(_("Name"), GetName());
|
||||
properties->AppendItem(_("ID"), GetRecId());
|
||||
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
|
||||
properties->AppendItem(_("Host agent"), GetHostAgent());
|
||||
properties->AppendItem(_("Job class"), GetJobclass());
|
||||
properties->AppendItem(_("Created"), GetCreated());
|
||||
properties->AppendItem(_("Changed"), GetChanged());
|
||||
properties->AppendItem(_("Next run"), GetNextrun());
|
||||
properties->AppendItem(_("Last run"), GetLastrun());
|
||||
properties->AppendItem(_("Last result"), GetLastresult());
|
||||
if (!GetCurrentAgent().IsEmpty())
|
||||
properties->AppendItem(_("Running at"), GetCurrentAgent());
|
||||
else
|
||||
properties->AppendItem(_("Running at"), _("Not currently running"));
|
||||
|
||||
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaJob::Refresh(ctlTree *browser, const wxTreeItemId item)
|
||||
{
|
||||
pgObject *job = 0;
|
||||
|
||||
pgObject *obj = browser->GetObject(browser->GetItemParent(item));
|
||||
if (obj && obj->IsCollection())
|
||||
job = jobFactory.CreateObjects((pgCollection *)obj, 0, wxT("\n WHERE j.jobid=") + NumToStr(GetRecId()));
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaJobFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
||||
{
|
||||
pgaJob *job = 0;
|
||||
|
||||
pgSet *jobs = collection->GetConnection()->ExecuteSet(
|
||||
wxT("SELECT j.*, cl.*, ag.*, sub.jlgstatus AS joblastresult ")
|
||||
wxT(" FROM pgagent.pga_job j JOIN")
|
||||
wxT(" pgagent.pga_jobclass cl ON cl.jclid=jobjclid LEFT OUTER JOIN")
|
||||
wxT(" pgagent.pga_jobagent ag ON ag.jagpid=jobagentid LEFT OUTER JOIN")
|
||||
wxT(" (SELECT DISTINCT ON (jlgjobid) jlgstatus, jlgjobid")
|
||||
wxT(" FROM pgagent.pga_joblog")
|
||||
wxT(" ORDER BY jlgjobid, jlgid desc) sub ON sub.jlgjobid = j.jobid ")
|
||||
+ restriction +
|
||||
wxT("ORDER BY jobname;"));
|
||||
|
||||
if (jobs)
|
||||
{
|
||||
while (!jobs->Eof())
|
||||
{
|
||||
wxString status;
|
||||
if (jobs->GetVal(wxT("joblastresult")) == wxT("r"))
|
||||
status = _("Running");
|
||||
else if (jobs->GetVal(wxT("joblastresult")) == wxT("s"))
|
||||
status = _("Successful");
|
||||
else if (jobs->GetVal(wxT("joblastresult")) == wxT("f"))
|
||||
status = _("Failed");
|
||||
else if (jobs->GetVal(wxT("joblastresult")) == wxT("d"))
|
||||
status = _("Aborted");
|
||||
else if (jobs->GetVal(wxT("joblastresult")) == wxT("i"))
|
||||
status = _("No steps");
|
||||
else
|
||||
status = _("Unknown");
|
||||
|
||||
job = new pgaJob(jobs->GetVal(wxT("jobname")));
|
||||
job->iSetServer(collection->GetServer());
|
||||
job->iSetRecId(jobs->GetLong(wxT("jobid")));
|
||||
job->iSetComment(jobs->GetVal(wxT("jobdesc")));
|
||||
|
||||
job->iSetEnabled(jobs->GetBool(wxT("jobenabled")));
|
||||
job->iSetJobclass(jobs->GetVal(wxT("jclname")));
|
||||
job->iSetHostAgent(jobs->GetVal(wxT("jobhostagent")));
|
||||
job->iSetCreated(jobs->GetDateTime(wxT("jobcreated")));
|
||||
job->iSetChanged(jobs->GetDateTime(wxT("jobchanged")));
|
||||
job->iSetNextrun(jobs->GetDateTime(wxT("jobnextrun")));
|
||||
job->iSetLastrun(jobs->GetDateTime(wxT("joblastrun")));
|
||||
job->iSetLastresult(status);
|
||||
job->iSetCurrentAgent(jobs->GetVal(wxT("jagstation")));
|
||||
|
||||
if (browser)
|
||||
{
|
||||
browser->AppendObject(collection, job);
|
||||
jobs->MoveNext();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
delete jobs;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
void pgaJob::ShowStatistics(frmMain *form, ctlListView *statistics)
|
||||
{
|
||||
wxString sql =
|
||||
wxT("SELECT jlgid")
|
||||
wxT(", jlgstatus")
|
||||
wxT(", jlgstart")
|
||||
wxT(", jlgduration")
|
||||
wxT(", (jlgstart + jlgduration) AS endtime")
|
||||
wxT(" FROM pgagent.pga_joblog\n")
|
||||
wxT(" WHERE jlgjobid = ") + NumToStr(GetRecId()) +
|
||||
wxT(" ORDER BY jlgstart DESC") +
|
||||
wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
|
||||
|
||||
if (statistics)
|
||||
{
|
||||
wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
|
||||
|
||||
// Add the statistics view columns
|
||||
statistics->ClearAll();
|
||||
statistics->AddColumn(_("Run"), 50);
|
||||
statistics->AddColumn(_("Status"), 60);
|
||||
statistics->AddColumn(_("Start time"), 95);
|
||||
statistics->AddColumn(_("End time"), 95);
|
||||
statistics->AddColumn(_("Duration"), 70);
|
||||
|
||||
pgSet *stats = GetConnection()->ExecuteSet(sql);
|
||||
wxString status;
|
||||
wxDateTime startTime;
|
||||
wxDateTime endTime;
|
||||
|
||||
if (stats)
|
||||
{
|
||||
while (!stats->Eof())
|
||||
{
|
||||
if (stats->GetVal(1) == wxT("r"))
|
||||
status = _("Running");
|
||||
else if (stats->GetVal(1) == wxT("s"))
|
||||
status = _("Successful");
|
||||
else if (stats->GetVal(1) == wxT("f"))
|
||||
status = _("Failed");
|
||||
else if (stats->GetVal(1) == wxT("d"))
|
||||
status = _("Aborted");
|
||||
else if (stats->GetVal(1) == wxT("i"))
|
||||
status = _("No steps");
|
||||
else
|
||||
status = _("Unknown");
|
||||
|
||||
startTime.ParseDateTime(stats->GetVal(2));
|
||||
endTime.ParseDateTime(stats->GetVal(4));
|
||||
|
||||
long pos = statistics->AppendItem(stats->GetVal(0), status, startTime.Format());
|
||||
if (stats->GetVal(4).Length() > 0)
|
||||
statistics->SetItem(pos, 3, endTime.Format());
|
||||
statistics->SetItem(pos, 4, stats->GetVal(3));
|
||||
|
||||
stats->MoveNext();
|
||||
}
|
||||
delete stats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool pgaJob::RunNow()
|
||||
{
|
||||
if (!GetConnection()->ExecuteVoid(wxT("UPDATE pgagent.pga_job SET jobnextrun = now() WHERE jobid=") + NumToStr(GetRecId())))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
pgaJobCollection::pgaJobCollection(pgaFactory *factory, pgServer *sv)
|
||||
: pgServerObjCollection(factory, sv)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
wxString pgaJobCollection::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent jobs");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent jobs");
|
||||
break;
|
||||
case OBJECTSLISTREPORT:
|
||||
message = _("pgAgent jobs list report");
|
||||
break;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
pgaJobObject::pgaJobObject(pgaJob *_job, pgaFactory &factory, const wxString &newName)
|
||||
: pgServerObject(factory, newName)
|
||||
{
|
||||
job = _job;
|
||||
server = job->GetServer();
|
||||
}
|
||||
|
||||
|
||||
pgaJobObjCollection::pgaJobObjCollection(pgaFactory *factory, pgaJob *_job)
|
||||
: pgServerObjCollection(factory, _job->GetServer())
|
||||
{
|
||||
job = _job;
|
||||
}
|
||||
|
||||
|
||||
bool pgaJobObjCollection::CanCreate()
|
||||
{
|
||||
return job->CanCreate();
|
||||
}
|
||||
|
||||
|
||||
pgCollection *pgaJobObjFactory::CreateCollection(pgObject *obj)
|
||||
{
|
||||
return new pgaJobObjCollection(GetCollectionFactory(), (pgaJob *)obj);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
#include "images/job.pngc"
|
||||
#include "images/jobs.pngc"
|
||||
#include "images/jobdisabled.pngc"
|
||||
|
||||
pgaJobFactory::pgaJobFactory()
|
||||
: pgServerObjFactory(__("pgAgent Job"), __("New Job"), __("Create a new Job."), job_png_img)
|
||||
{
|
||||
metaType = PGM_JOB;
|
||||
disabledId = addIcon(jobdisabled_png_img);
|
||||
}
|
||||
|
||||
|
||||
pgCollection *pgaJobFactory::CreateCollection(pgObject *obj)
|
||||
{
|
||||
return new pgaJobCollection(GetCollectionFactory(), (pgServer *)obj);
|
||||
}
|
||||
|
||||
|
||||
pgaJobFactory jobFactory;
|
||||
static pgaCollectionFactory cf(&jobFactory, __("Jobs"), jobs_png_img);
|
||||
|
||||
runNowFactory::runNowFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
|
||||
{
|
||||
mnu->Append(id, _("&Run now"), _("Reschedule the job to run now."));
|
||||
}
|
||||
|
||||
|
||||
wxWindow *runNowFactory::StartDialog(frmMain *form, pgObject *obj)
|
||||
{
|
||||
if (!((pgaJob *)(obj))->RunNow())
|
||||
{
|
||||
wxLogError(_("Failed to reschedule the job."));
|
||||
}
|
||||
|
||||
form->Refresh(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool runNowFactory::CheckEnable(pgObject *obj)
|
||||
{
|
||||
if (obj)
|
||||
{
|
||||
if (obj->GetMetaType() == PGM_JOB && !obj->IsCollection())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
576
agent/pgaSchedule.cpp
Normal file
576
agent/pgaSchedule.cpp
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// pgaSchedule.cpp - PostgreSQL Agent Schedule
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/arrimpl.cpp>
|
||||
|
||||
// App headers
|
||||
#include "utils/misc.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgDatabase.h"
|
||||
#include "schema/pgCollection.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
#include "agent/pgaStep.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
|
||||
|
||||
pgaSchedule::pgaSchedule(pgCollection *_collection, const wxString &newName)
|
||||
: pgaJobObject(_collection->GetJob(), scheduleFactory, newName)
|
||||
{
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent schedule");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent schedule");
|
||||
break;
|
||||
case PROPERTIESREPORT:
|
||||
message = _("pgAgent schedule properties report");
|
||||
break;
|
||||
case PROPERTIES:
|
||||
message = _("pgAgent schedule properties");
|
||||
break;
|
||||
case DDLREPORT:
|
||||
message = _("pgAgent schedule DDL report");
|
||||
break;
|
||||
case DEPENDENCIESREPORT:
|
||||
message = _("pgAgent schedule dependencies report");
|
||||
break;
|
||||
case DEPENDENCIES:
|
||||
message = _("pgAgent schedule dependencies");
|
||||
break;
|
||||
case DEPENDENTSREPORT:
|
||||
message = _("pgAgent schedule dependents report");
|
||||
break;
|
||||
case DEPENDENTS:
|
||||
message = _("pgAgent schedule dependents");
|
||||
break;
|
||||
case DROPEXCLUDINGDEPS:
|
||||
message = wxString::Format(_("Are you sure you wish to drop schedule \"%s\"?"),
|
||||
GetFullIdentifier().c_str());
|
||||
break;
|
||||
case DROPTITLE:
|
||||
message = _("Drop schedule?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
|
||||
message += wxT(" - ") + GetName();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
bool pgaSchedule::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
||||
{
|
||||
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_schedule WHERE jscid=") + NumToStr(GetRecId()));
|
||||
}
|
||||
|
||||
|
||||
void pgaSchedule::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
||||
{
|
||||
if (!expandedKids)
|
||||
{
|
||||
expandedKids = true;
|
||||
}
|
||||
|
||||
if (properties)
|
||||
{
|
||||
CreateListColumns(properties);
|
||||
|
||||
properties->AppendItem(_("Name"), GetName());
|
||||
properties->AppendItem(_("ID"), GetRecId());
|
||||
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
|
||||
|
||||
properties->AppendItem(_("Start date"), GetStart());
|
||||
properties->AppendItem(_("End date"), GetEnd());
|
||||
properties->AppendItem(_("Minutes"), GetMinutesString());
|
||||
properties->AppendItem(_("Hours"), GetHoursString());
|
||||
properties->AppendItem(_("Weekdays"), GetWeekdaysString());
|
||||
properties->AppendItem(_("Monthdays"), GetMonthdaysString());
|
||||
properties->AppendItem(_("Months"), GetMonthsString());
|
||||
properties->AppendItem(_("Exceptions"), GetExceptionsString());
|
||||
|
||||
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaSchedule::Refresh(ctlTree *browser, const wxTreeItemId item)
|
||||
{
|
||||
pgObject *schedule = 0;
|
||||
|
||||
pgCollection *coll = browser->GetParentCollection(item);
|
||||
if (coll)
|
||||
schedule = scheduleFactory.CreateObjects(coll, 0, wxT("\n AND jscid=") + NumToStr(GetRecId()));
|
||||
|
||||
return schedule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaScheduleFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
||||
{
|
||||
pgaSchedule *schedule = 0;
|
||||
wxString tmp;
|
||||
|
||||
pgSet *schedules = collection->GetConnection()->ExecuteSet(
|
||||
wxT("SELECT * FROM pgagent.pga_schedule\n")
|
||||
wxT(" WHERE jscjobid=") + NumToStr(collection->GetJob()->GetRecId()) + wxT("\n")
|
||||
+ restriction +
|
||||
wxT(" ORDER BY jscname"));
|
||||
|
||||
if (schedules)
|
||||
{
|
||||
while (!schedules->Eof())
|
||||
{
|
||||
|
||||
schedule = new pgaSchedule(collection, schedules->GetVal(wxT("jscname")));
|
||||
schedule->iSetRecId(schedules->GetLong(wxT("jscid")));
|
||||
schedule->iSetStart(schedules->GetDateTime(wxT("jscstart")));
|
||||
schedule->iSetEnd(schedules->GetDateTime(wxT("jscend")));
|
||||
schedule->iSetEnabled(schedules->GetBool(wxT("jscenabled")));
|
||||
|
||||
tmp = schedules->GetVal(wxT("jscminutes"));
|
||||
tmp.Replace(wxT("{"), wxT(""));
|
||||
tmp.Replace(wxT("}"), wxT(""));
|
||||
tmp.Replace(wxT(","), wxT(""));
|
||||
schedule->iSetMinutes(tmp);
|
||||
|
||||
tmp = schedules->GetVal(wxT("jschours"));
|
||||
tmp.Replace(wxT("{"), wxT(""));
|
||||
tmp.Replace(wxT("}"), wxT(""));
|
||||
tmp.Replace(wxT(","), wxT(""));
|
||||
schedule->iSetHours(tmp);
|
||||
|
||||
tmp = schedules->GetVal(wxT("jscweekdays"));
|
||||
tmp.Replace(wxT("{"), wxT(""));
|
||||
tmp.Replace(wxT("}"), wxT(""));
|
||||
tmp.Replace(wxT(","), wxT(""));
|
||||
schedule->iSetWeekdays(tmp);
|
||||
|
||||
tmp = schedules->GetVal(wxT("jscmonthdays"));
|
||||
tmp.Replace(wxT("{"), wxT(""));
|
||||
tmp.Replace(wxT("}"), wxT(""));
|
||||
tmp.Replace(wxT(","), wxT(""));
|
||||
schedule->iSetMonthdays(tmp);
|
||||
|
||||
tmp = schedules->GetVal(wxT("jscmonths"));
|
||||
tmp.Replace(wxT("{"), wxT(""));
|
||||
tmp.Replace(wxT("}"), wxT(""));
|
||||
tmp.Replace(wxT(","), wxT(""));
|
||||
schedule->iSetMonths(tmp);
|
||||
|
||||
schedule->iSetComment(schedules->GetVal(wxT("jscdesc")));
|
||||
|
||||
pgSet *exceptions = collection->GetConnection()->ExecuteSet(
|
||||
wxT("SELECT * FROM pgagent.pga_exception\n")
|
||||
wxT(" WHERE jexscid=") + NumToStr(schedule->GetRecId()) + wxT("\n"));
|
||||
|
||||
tmp.Empty();
|
||||
if (exceptions)
|
||||
{
|
||||
while (!exceptions->Eof())
|
||||
{
|
||||
tmp += exceptions->GetVal(wxT("jexid"));
|
||||
tmp += wxT("|");
|
||||
tmp += exceptions->GetVal(wxT("jexdate"));
|
||||
tmp += wxT("|");
|
||||
tmp += exceptions->GetVal(wxT("jextime"));
|
||||
tmp += wxT("|");
|
||||
|
||||
exceptions->MoveNext();
|
||||
}
|
||||
}
|
||||
schedule->iSetExceptions(tmp);
|
||||
delete exceptions;
|
||||
|
||||
if (browser)
|
||||
{
|
||||
browser->AppendObject(collection, schedule);
|
||||
schedules->MoveNext();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
delete schedules;
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetMinutesString()
|
||||
{
|
||||
size_t x = 0;
|
||||
bool isWildcard = true;
|
||||
wxString res, tmp;
|
||||
|
||||
for (x = 0; x <= minutes.Length(); x++)
|
||||
{
|
||||
if (minutes[x] == 't')
|
||||
{
|
||||
tmp.Printf(wxT("%.2d, "), (int)x);
|
||||
res += tmp;
|
||||
isWildcard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWildcard)
|
||||
{
|
||||
res = _("Every minute");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Length() > 2)
|
||||
{
|
||||
res.RemoveLast();
|
||||
res.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetHoursString()
|
||||
{
|
||||
size_t x = 0;
|
||||
bool isWildcard = true;
|
||||
wxString res, tmp;
|
||||
|
||||
for (x = 0; x <= hours.Length(); x++)
|
||||
{
|
||||
if (hours[x] == 't')
|
||||
{
|
||||
tmp.Printf(wxT("%.2d, "), (int)x);
|
||||
res += tmp;
|
||||
isWildcard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWildcard)
|
||||
{
|
||||
res = _("Every hour");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Length() > 2)
|
||||
{
|
||||
res.RemoveLast();
|
||||
res.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetWeekdaysString()
|
||||
{
|
||||
size_t x = 0;
|
||||
bool isWildcard = true;
|
||||
wxString res;
|
||||
|
||||
for (x = 0; x <= weekdays.Length(); x++)
|
||||
{
|
||||
if (weekdays[x] == 't')
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case 0:
|
||||
res += _("Sunday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 1:
|
||||
res += _("Monday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 2:
|
||||
res += _("Tuesday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 3:
|
||||
res += _("Wednesday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 4:
|
||||
res += _("Thursday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 5:
|
||||
res += _("Friday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 6:
|
||||
res += _("Saturday");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
default:
|
||||
res += _("The mythical 8th day!");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
}
|
||||
isWildcard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWildcard)
|
||||
{
|
||||
res = _("Any day of the week");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Length() > 2)
|
||||
{
|
||||
res.RemoveLast();
|
||||
res.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetMonthdaysString()
|
||||
{
|
||||
size_t x = 0;
|
||||
bool isWildcard = true;
|
||||
wxString res, tmp;
|
||||
|
||||
for (x = 0; x <= monthdays.Length(); x++)
|
||||
{
|
||||
if (monthdays[x] == 't')
|
||||
{
|
||||
if (x < 31)
|
||||
{
|
||||
tmp.Printf(wxT("%.2d, "), (int)(x + 1));
|
||||
res += tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
res += _("Last day");
|
||||
res += wxT(", ");
|
||||
}
|
||||
isWildcard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWildcard)
|
||||
{
|
||||
res = _("Every day");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Length() > 2)
|
||||
{
|
||||
res.RemoveLast();
|
||||
res.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetMonthsString()
|
||||
{
|
||||
size_t x = 0;
|
||||
bool isWildcard = true;
|
||||
wxString res;
|
||||
|
||||
for (x = 0; x <= months.Length(); x++)
|
||||
{
|
||||
if (months[x] == 't')
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case 0:
|
||||
res += _("January");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 1:
|
||||
res += _("February");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 2:
|
||||
res += _("March");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 3:
|
||||
res += _("April");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 4:
|
||||
res += _("May");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 5:
|
||||
res += _("June");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 6:
|
||||
res += _("July");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 7:
|
||||
res += _("August");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 8:
|
||||
res += _("September");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 9:
|
||||
res += _("October");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 10:
|
||||
res += _("November");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
case 11:
|
||||
res += _("December");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
default:
|
||||
res += _("The mythical 13th month!");
|
||||
res += wxT(", ");
|
||||
break;
|
||||
}
|
||||
isWildcard = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWildcard)
|
||||
{
|
||||
res = _("Every month");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res.Length() > 2)
|
||||
{
|
||||
res.RemoveLast();
|
||||
res.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString pgaSchedule::GetExceptionsString()
|
||||
{
|
||||
wxString tmp, token, dateToken, timeToken;
|
||||
wxDateTime val;
|
||||
wxStringTokenizer tkz(exceptions, wxT("|"));
|
||||
|
||||
while (tkz.HasMoreTokens() )
|
||||
{
|
||||
|
||||
dateToken.Empty();
|
||||
timeToken.Empty();
|
||||
|
||||
// First is the ID which can be ignored
|
||||
token = tkz.GetNextToken();
|
||||
|
||||
// Look for a date
|
||||
if (tkz.HasMoreTokens())
|
||||
dateToken = tkz.GetNextToken();
|
||||
|
||||
// Look for a time
|
||||
if (tkz.HasMoreTokens())
|
||||
timeToken = tkz.GetNextToken();
|
||||
|
||||
if (tmp.IsEmpty())
|
||||
tmp += wxT("[");
|
||||
else
|
||||
tmp += wxT(", [");
|
||||
|
||||
if (!dateToken.IsEmpty() && !timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseDate(dateToken);
|
||||
val.ParseTime(timeToken);
|
||||
tmp += val.Format();
|
||||
}
|
||||
else if (!dateToken.IsEmpty() && timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseDate(dateToken);
|
||||
tmp += val.FormatDate();
|
||||
}
|
||||
else if (dateToken.IsEmpty() && !timeToken.IsEmpty())
|
||||
{
|
||||
val.ParseTime(timeToken);
|
||||
tmp += val.FormatTime();
|
||||
}
|
||||
|
||||
tmp += wxT("]");
|
||||
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
pgaScheduleCollection::pgaScheduleCollection(pgaFactory *factory, pgaJob *job)
|
||||
: pgaJobObjCollection(factory, job)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
wxString pgaScheduleCollection::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent schedules");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent schedules");
|
||||
break;
|
||||
case OBJECTSLISTREPORT:
|
||||
message = _("pgAgent schedules list report");
|
||||
break;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
#include "images/schedule.pngc"
|
||||
#include "images/schedules.pngc"
|
||||
|
||||
pgaScheduleFactory::pgaScheduleFactory()
|
||||
: pgaJobObjFactory(__("Schedule"), __("New Schedule"), __("Create a new Schedule."), schedule_png_img)
|
||||
{
|
||||
metaType = PGM_SCHEDULE;
|
||||
}
|
||||
|
||||
|
||||
pgCollection *pgaScheduleFactory::CreateCollection(pgObject *obj)
|
||||
{
|
||||
return new pgaScheduleCollection(GetCollectionFactory(), (pgaJob *)obj);
|
||||
}
|
||||
|
||||
|
||||
pgaScheduleFactory scheduleFactory;
|
||||
static pgaCollectionFactory cf(&scheduleFactory, __("Schedules"), schedules_png_img);
|
||||
327
agent/pgaStep.cpp
Normal file
327
agent/pgaStep.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// pgaStep.cpp - PostgreSQL Agent Step
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/misc.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgDatabase.h"
|
||||
#include "schema/pgCollection.h"
|
||||
#include "agent/pgaStep.h"
|
||||
#include "agent/pgaSchedule.h"
|
||||
|
||||
pgaStep::pgaStep(pgCollection *_collection, const wxString &newName)
|
||||
: pgaJobObject(_collection->GetJob(), stepFactory, newName)
|
||||
{
|
||||
}
|
||||
|
||||
wxString pgaStep::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent step");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent step");
|
||||
break;
|
||||
case PROPERTIESREPORT:
|
||||
message = _("pgAgent step properties report");
|
||||
break;
|
||||
case PROPERTIES:
|
||||
message = _("pgAgent step properties");
|
||||
break;
|
||||
case DDLREPORT:
|
||||
message = _("pgAgent step DDL report");
|
||||
break;
|
||||
case DEPENDENCIESREPORT:
|
||||
message = _("pgAgent step dependencies report");
|
||||
break;
|
||||
case DEPENDENCIES:
|
||||
message = _("pgAgent step dependencies");
|
||||
break;
|
||||
case DEPENDENTSREPORT:
|
||||
message = _("pgAgent step dependents report");
|
||||
break;
|
||||
case DEPENDENTS:
|
||||
message = _("pgAgent step dependents");
|
||||
break;
|
||||
case DROPEXCLUDINGDEPS:
|
||||
message = wxString::Format(_("Are you sure you wish to drop step \"%s\"?"),
|
||||
GetFullIdentifier().c_str());
|
||||
break;
|
||||
case DROPTITLE:
|
||||
message = _("Drop step?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!message.IsEmpty() && !(kindOfMessage == DROPEXCLUDINGDEPS || kindOfMessage == DROPTITLE))
|
||||
message += wxT(" - ") + GetName();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
bool pgaStep::IsUpToDate()
|
||||
{
|
||||
wxString sql = wxT("SELECT xmin FROM pgagent.pga_jobstep WHERE jstid = ") + NumToStr(GetRecId());
|
||||
if (!GetConnection() || GetConnection()->ExecuteScalar(sql) != NumToStr(GetXid()))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pgaStep::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
|
||||
{
|
||||
return GetConnection()->ExecuteVoid(wxT("DELETE FROM pgagent.pga_jobstep WHERE jstid=") + NumToStr(GetRecId()));
|
||||
}
|
||||
|
||||
|
||||
void pgaStep::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
|
||||
{
|
||||
if (!expandedKids)
|
||||
{
|
||||
expandedKids = true;
|
||||
}
|
||||
|
||||
if (properties)
|
||||
{
|
||||
CreateListColumns(properties);
|
||||
|
||||
properties->AppendItem(_("Name"), GetName());
|
||||
properties->AppendItem(_("ID"), GetRecId());
|
||||
properties->AppendYesNoItem(_("Enabled"), GetEnabled());
|
||||
properties->AppendItem(_("Kind"), GetKind());
|
||||
if (GetConnStr().IsEmpty())
|
||||
properties->AppendItem(_("Database"), GetDbname());
|
||||
else
|
||||
properties->AppendItem(_("Connection String"), GetConnStr());
|
||||
properties->AppendItem(_("Code"), GetCode());
|
||||
properties->AppendItem(_("On error"), GetOnError());
|
||||
|
||||
properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaStep::Refresh(ctlTree *browser, const wxTreeItemId item)
|
||||
{
|
||||
pgObject *step = 0;
|
||||
|
||||
pgCollection *coll = browser->GetParentCollection(item);
|
||||
if (coll)
|
||||
step = stepFactory.CreateObjects(coll, 0, wxT("\n AND jstid=") + NumToStr(GetRecId()));
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
|
||||
|
||||
pgObject *pgaStepFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
|
||||
{
|
||||
pgaStep *step = 0;
|
||||
|
||||
pgSet *steps = collection->GetConnection()->ExecuteSet(
|
||||
wxT("SELECT xmin, * FROM pgagent.pga_jobstep\n")
|
||||
wxT(" WHERE jstjobid=") + NumToStr(collection->GetJob()->GetRecId()) + wxT("\n")
|
||||
+ restriction +
|
||||
wxT(" ORDER BY jstname"));
|
||||
|
||||
if (steps)
|
||||
{
|
||||
while (!steps->Eof())
|
||||
{
|
||||
|
||||
step = new pgaStep(collection, steps->GetVal(wxT("jstname")));
|
||||
step->iSetRecId(steps->GetLong(wxT("jstid")));
|
||||
step->iSetXid(steps->GetOid(wxT("xmin")));
|
||||
step->iSetDbname(steps->GetVal(wxT("jstdbname")));
|
||||
if (steps->HasColumn(wxT("jstconnstr")))
|
||||
step->iSetConnStr(steps->GetVal(wxT("jstconnstr")));
|
||||
step->iSetCode(steps->GetVal(wxT("jstcode")));
|
||||
step->iSetEnabled(steps->GetBool(wxT("jstenabled")));
|
||||
|
||||
wxChar kindc = *steps->GetVal(wxT("jstkind")).c_str();
|
||||
wxString kinds;
|
||||
switch (kindc)
|
||||
{
|
||||
case 'b':
|
||||
kinds = _("Batch");
|
||||
break;
|
||||
case 's':
|
||||
kinds = wxT("SQL");
|
||||
break;
|
||||
}
|
||||
step->iSetKindChar(kindc);
|
||||
step->iSetKind(kinds);
|
||||
|
||||
wxChar onerrc = *steps->GetVal(wxT("jstonerror")).c_str();
|
||||
wxString onerrs;
|
||||
switch (onerrc)
|
||||
{
|
||||
case 's':
|
||||
onerrs = _("Succeed");
|
||||
break;
|
||||
case 'f':
|
||||
onerrs = _("Fail");
|
||||
break;
|
||||
case 'i':
|
||||
onerrs = _("Ignore");
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
step->iSetOnErrorChar(onerrc);
|
||||
step->iSetOnError(onerrs);
|
||||
step->iSetComment(steps->GetVal(wxT("jstdesc")));
|
||||
|
||||
|
||||
if (browser)
|
||||
{
|
||||
browser->AppendObject(collection, step);
|
||||
steps->MoveNext();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
delete steps;
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
|
||||
void pgaStep::ShowStatistics(frmMain *form, ctlListView *statistics)
|
||||
{
|
||||
wxString sql =
|
||||
wxT("SELECT jsljlgid")
|
||||
wxT(", jslstatus")
|
||||
wxT(", jslresult")
|
||||
wxT(", jslstart")
|
||||
wxT(", jslduration")
|
||||
wxT(", (jslstart + jslduration) AS endtime")
|
||||
wxT(", jsloutput")
|
||||
wxT(" FROM pgagent.pga_jobsteplog\n")
|
||||
wxT(" WHERE jsljstid = ") + NumToStr(GetRecId()) +
|
||||
wxT(" ORDER BY jslstart DESC")
|
||||
wxT(" LIMIT ") + NumToStr(settings->GetMaxRows());
|
||||
|
||||
if (statistics)
|
||||
{
|
||||
wxLogInfo(wxT("Displaying statistics for job %s"), GetFullIdentifier().c_str());
|
||||
|
||||
// Add the statistics view columns
|
||||
statistics->ClearAll();
|
||||
statistics->AddColumn(_("Run"), 50);
|
||||
statistics->AddColumn(_("Status"), 60);
|
||||
statistics->AddColumn(_("Result"), 60);
|
||||
statistics->AddColumn(_("Start time"), 95);
|
||||
statistics->AddColumn(_("End time"), 95);
|
||||
statistics->AddColumn(_("Duration"), 70);
|
||||
statistics->AddColumn(_("Output"), 200);
|
||||
|
||||
pgSet *stats = GetConnection()->ExecuteSet(sql);
|
||||
wxString status;
|
||||
wxDateTime startTime;
|
||||
wxDateTime endTime;
|
||||
|
||||
if (stats)
|
||||
{
|
||||
while (!stats->Eof())
|
||||
{
|
||||
if (stats->GetVal(1) == wxT("r"))
|
||||
status = _("Running");
|
||||
else if (stats->GetVal(1) == wxT("s"))
|
||||
status = _("Successful");
|
||||
else if (stats->GetVal(1) == wxT("f"))
|
||||
status = _("Failed");
|
||||
else if (stats->GetVal(1) == wxT("i"))
|
||||
status = _("Ignored");
|
||||
else if (stats->GetVal(1) == wxT("d"))
|
||||
status = _("Aborted");
|
||||
else
|
||||
status = _("Unknown");
|
||||
|
||||
startTime.ParseDateTime(stats->GetVal(3));
|
||||
endTime.ParseDateTime(stats->GetVal(5));
|
||||
|
||||
long pos = statistics->AppendItem(stats->GetVal(0), status, stats->GetVal(2));
|
||||
statistics->SetItem(pos, 3, startTime.Format());
|
||||
if (stats->GetVal(5).Length() > 0)
|
||||
statistics->SetItem(pos, 4, endTime.Format());
|
||||
statistics->SetItem(pos, 5, stats->GetVal(4));
|
||||
statistics->SetItem(pos, 6, stats->GetVal(6));
|
||||
|
||||
stats->MoveNext();
|
||||
}
|
||||
delete stats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
pgaStepCollection::pgaStepCollection(pgaFactory *factory, pgaJob *job)
|
||||
: pgaJobObjCollection(factory, job)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
wxString pgaStepCollection::GetTranslatedMessage(int kindOfMessage) const
|
||||
{
|
||||
wxString message = wxEmptyString;
|
||||
|
||||
switch (kindOfMessage)
|
||||
{
|
||||
case RETRIEVINGDETAILS:
|
||||
message = _("Retrieving details on pgAgent steps");
|
||||
break;
|
||||
case REFRESHINGDETAILS:
|
||||
message = _("Refreshing pgAgent steps");
|
||||
break;
|
||||
case OBJECTSLISTREPORT:
|
||||
message = _("pgAgent steps list report");
|
||||
break;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
#include "images/step.pngc"
|
||||
#include "images/steps.pngc"
|
||||
|
||||
pgaStepFactory::pgaStepFactory()
|
||||
: pgaJobObjFactory(__("Step"), __("New Step"), __("Create a new Step."), step_png_img)
|
||||
{
|
||||
metaType = PGM_STEP;
|
||||
}
|
||||
|
||||
|
||||
pgCollection *pgaStepFactory::CreateCollection(pgObject *obj)
|
||||
{
|
||||
return new pgaStepCollection(GetCollectionFactory(), (pgaJob *)obj);
|
||||
}
|
||||
|
||||
|
||||
pgaStepFactory stepFactory;
|
||||
static pgaCollectionFactory cf(&stepFactory, __("Steps"), steps_png_img);
|
||||
519
ctl/calbox.cpp
Normal file
519
ctl/calbox.cpp
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// calbox.cpp - Date-picker control box
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#include "ctl/calbox.h"
|
||||
|
||||
#if !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
#define TXTCTRL_FLAGS wxNO_BORDER
|
||||
#define CALBORDER 0
|
||||
#define TXTPOSX 0
|
||||
#define TXTPOSY 1
|
||||
#elif defined(__WXGTK__)
|
||||
#define TXTCTRL_FLAGS 0
|
||||
#define CALBORDER 4
|
||||
#define TXTPOSX 0
|
||||
#define TXTPOSY 0
|
||||
#else
|
||||
#define TXTCTRL_FLAGS 0
|
||||
#define CALBORDER 4
|
||||
#define TXTPOSX 0
|
||||
#define TXTPOSY 0
|
||||
#endif
|
||||
|
||||
|
||||
#define CTRLID_TXT 101
|
||||
#define CTRLID_CAL 102
|
||||
#define CTRLID_BTN 103
|
||||
#define CTRLID_PAN 104
|
||||
|
||||
BEGIN_EVENT_TABLE(wxCalendarBox, wxControl)
|
||||
EVT_BUTTON(CTRLID_BTN, wxCalendarBox::OnClick)
|
||||
EVT_TEXT(CTRLID_TXT, wxCalendarBox::OnText)
|
||||
EVT_CHILD_FOCUS(wxCalendarBox::OnChildSetFocus)
|
||||
EVT_SIZE(wxCalendarBox::OnSize)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxCalendarBox, wxControl)
|
||||
|
||||
|
||||
|
||||
wxCalendarBox::wxCalendarBox(wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxDateTime &date,
|
||||
const wxPoint &pos,
|
||||
const wxSize &size,
|
||||
long style,
|
||||
const wxString &name)
|
||||
{
|
||||
Init();
|
||||
Create(parent, id, date, pos, size, style, name);
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::Create(wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxDateTime &date,
|
||||
const wxPoint &pos,
|
||||
const wxSize &size,
|
||||
long style,
|
||||
const wxString &name)
|
||||
{
|
||||
wxString txt;
|
||||
if (date.IsValid())
|
||||
txt = date.FormatISODate();
|
||||
|
||||
if ( !wxControl::Create(parent, id, pos, size,
|
||||
style | wxCLIP_CHILDREN | wxWANTS_CHARS,
|
||||
wxDefaultValidator, name) )
|
||||
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
InheritAttributes();
|
||||
|
||||
wxSize cs = GetClientSize();
|
||||
wxSize bs = ConvertDialogToPixels(wxSize(10, 0));
|
||||
|
||||
wxBitmap bmp(8, 4);
|
||||
{
|
||||
wxMemoryDC dc;
|
||||
|
||||
dc.SelectObject(bmp);
|
||||
dc.SetBrush(wxBrush(GetBackgroundColour()));
|
||||
dc.SetPen(wxPen(GetBackgroundColour()));
|
||||
dc.DrawRectangle(0, 0, 8, 4);
|
||||
|
||||
dc.SetBrush(wxBrush(GetForegroundColour()));
|
||||
dc.SetPen(wxPen(GetForegroundColour()));
|
||||
wxPoint pt[3] = { wxPoint(0, 0), wxPoint(6, 0), wxPoint(3, 3) };
|
||||
dc.DrawPolygon(3, pt);
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
}
|
||||
|
||||
m_txt = new wxTextCtrl(this, CTRLID_TXT, txt, wxDefaultPosition, size, TXTCTRL_FLAGS);
|
||||
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnEditKey), 0, this);
|
||||
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(wxCalendarBox::OnKillFocus), 0, this);
|
||||
SetFormat(wxT("%Y-%m-%d"));
|
||||
|
||||
m_btn = new wxBitmapButton(this, CTRLID_BTN, bmp, wxDefaultPosition, bs);
|
||||
|
||||
m_dlg = new wxDialog(this, CTRLID_CAL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER);
|
||||
m_dlg->SetFont(GetFont());
|
||||
|
||||
wxPanel *panel = new wxPanel(m_dlg, CTRLID_PAN, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER | wxCLIP_CHILDREN);
|
||||
m_cal = new pgCompatCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime, wxPoint(0, 0), wxDefaultSize, wxSUNKEN_BORDER);
|
||||
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
|
||||
m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxCalendarBox::OnCalKey), 0, this);
|
||||
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
|
||||
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
|
||||
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
|
||||
m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED, wxCalendarEventHandler(wxCalendarBox::OnSelChange), 0, this);
|
||||
|
||||
wxWindow *yearControl = m_cal->GetYearControl();
|
||||
|
||||
Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS, wxFocusEventHandler(wxCalendarBox::OnSetFocus));
|
||||
|
||||
wxClientDC dc(yearControl);
|
||||
dc.SetFont(m_font);
|
||||
wxCoord width, dummy;
|
||||
dc.GetTextExtent(wxT("2000"), &width, &dummy);
|
||||
width += ConvertDialogToPixels(wxSize(20, 0)).x;
|
||||
|
||||
wxSize calSize = m_cal->GetBestSize();
|
||||
wxSize yearSize = yearControl->GetSize();
|
||||
yearSize.x = width;
|
||||
|
||||
wxPoint yearPosition = yearControl->GetPosition();
|
||||
|
||||
width = yearPosition.x + yearSize.x + 2 + CALBORDER / 2;
|
||||
if (width < calSize.x - 4)
|
||||
width = calSize.x - 4;
|
||||
|
||||
int calPos = (width - calSize.x) / 2;
|
||||
if (calPos == -1)
|
||||
{
|
||||
calPos = 0;
|
||||
width += 2;
|
||||
}
|
||||
m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
|
||||
yearControl->SetSize(width - yearSize.x - CALBORDER / 2, yearPosition.y, yearSize.x, yearSize.y);
|
||||
m_cal->GetMonthControl()->Move(0, 0);
|
||||
|
||||
SetInitialSize(size);
|
||||
|
||||
panel->SetClientSize(width + CALBORDER / 2, calSize.y - 2 + CALBORDER);
|
||||
m_dlg->SetClientSize(panel->GetSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::Init()
|
||||
{
|
||||
m_dlg = NULL;
|
||||
m_txt = NULL;
|
||||
m_cal = NULL;
|
||||
m_btn = NULL;
|
||||
|
||||
m_dropped = false;
|
||||
m_ignoreDrop = false;
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::Destroy()
|
||||
{
|
||||
if (m_cal)
|
||||
m_cal->Destroy();
|
||||
if (m_dlg)
|
||||
m_dlg->Destroy();
|
||||
if (m_txt)
|
||||
m_txt->Destroy();
|
||||
if (m_btn)
|
||||
m_btn->Destroy();
|
||||
|
||||
m_dlg = NULL;
|
||||
m_txt = NULL;
|
||||
m_cal = NULL;
|
||||
m_btn = NULL;
|
||||
|
||||
return wxControl::Destroy();
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::DoMoveWindow(int x, int y, int w, int h)
|
||||
{
|
||||
wxControl::DoMoveWindow(x, y, w, h);
|
||||
if (m_dropped)
|
||||
{
|
||||
DropDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxSize wxCalendarBox::DoGetBestSize() const
|
||||
{
|
||||
int bh = m_btn->GetBestSize().y;
|
||||
int eh = m_txt->GetBestSize().y;
|
||||
return wxSize(100, bh > eh ? bh : eh);
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnSize(wxSizeEvent &event)
|
||||
{
|
||||
if ( m_btn )
|
||||
{
|
||||
wxSize sz = GetClientSize();
|
||||
|
||||
wxSize bs = m_btn->GetSize();
|
||||
int eh = m_txt->GetBestSize().y;
|
||||
|
||||
m_txt->SetSize(TXTPOSX, TXTPOSY, sz.x - bs.x - TXTPOSX, sz.y > eh ? eh - TXTPOSY : sz.y - TXTPOSY);
|
||||
m_btn->SetSize(sz.x - bs.x, 0, bs.x, sz.y);
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::Show(bool show)
|
||||
{
|
||||
if ( !wxControl::Show(show) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!show)
|
||||
{
|
||||
if (m_dlg)
|
||||
{
|
||||
m_dlg->Hide();
|
||||
m_dropped = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::Enable(bool enable)
|
||||
{
|
||||
if ( !wxControl::Enable(enable) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_cal)
|
||||
{
|
||||
if (enable)
|
||||
m_cal->Show();
|
||||
else
|
||||
m_cal->Hide();
|
||||
}
|
||||
|
||||
if (m_btn)
|
||||
m_btn->Enable(enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::SetFormat(const wxChar *fmt)
|
||||
{
|
||||
wxDateTime dt;
|
||||
dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
|
||||
wxString str = dt.Format(fmt);
|
||||
const wxChar *p = (const wxChar *) str;
|
||||
|
||||
m_format = wxEmptyString;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
int n = wxAtoi(p);
|
||||
if (n == dt.GetDay())
|
||||
{
|
||||
m_format.Append(wxT("%d"));
|
||||
p += 2;
|
||||
}
|
||||
else if (n == (int)dt.GetMonth() + 1)
|
||||
{
|
||||
m_format.Append(wxT("%m"));
|
||||
p += 2;
|
||||
}
|
||||
else if (n == dt.GetYear())
|
||||
{
|
||||
m_format.Append(wxT("%Y"));
|
||||
p += 4;
|
||||
}
|
||||
else
|
||||
m_format.Append(*p++);
|
||||
}
|
||||
|
||||
if (m_txt)
|
||||
{
|
||||
wxArrayString valArray;
|
||||
wxChar c;
|
||||
for (c = '0'; c <= '9'; c++)
|
||||
valArray.Add(wxString(c, 1));
|
||||
const wxChar *p = (const wxChar *) m_format;
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '%')
|
||||
p += 2;
|
||||
else
|
||||
valArray.Add(wxString(*p++, 1));
|
||||
}
|
||||
wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
|
||||
tv.SetIncludes(valArray);
|
||||
|
||||
m_txt->SetValidator(tv);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
wxDateTime wxCalendarBox::GetValue()
|
||||
{
|
||||
wxDateTime dt;
|
||||
wxString txt = m_txt->GetValue();
|
||||
|
||||
if (!txt.IsEmpty())
|
||||
dt.ParseFormat(txt, m_format);
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
bool wxCalendarBox::SetValue(const wxDateTime &date)
|
||||
{
|
||||
bool retval = false;
|
||||
|
||||
if (m_cal)
|
||||
{
|
||||
if (date.IsValid())
|
||||
m_txt->SetValue(date.FormatISODate());
|
||||
else
|
||||
m_txt->SetValue(wxEmptyString);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::DropDown(bool down)
|
||||
{
|
||||
if (m_dlg)
|
||||
{
|
||||
if (down)
|
||||
{
|
||||
if (m_txt->GetValue().IsEmpty())
|
||||
m_cal->SetDate(wxDateTime::Today());
|
||||
else
|
||||
{
|
||||
wxDateTime dt;
|
||||
dt.ParseFormat(m_txt->GetValue(), m_format);
|
||||
m_cal->SetDate(dt);
|
||||
}
|
||||
wxPoint pos = GetParent()->ClientToScreen(GetPosition());
|
||||
|
||||
m_dlg->Move(pos.x, pos.y + GetSize().y);
|
||||
m_dlg->Show();
|
||||
m_dropped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_dropped)
|
||||
m_dlg->Hide();
|
||||
m_dropped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnChildSetFocus(wxChildFocusEvent &ev)
|
||||
{
|
||||
ev.Skip();
|
||||
m_ignoreDrop = false;
|
||||
|
||||
wxWindow *w = (wxWindow *)ev.GetEventObject();
|
||||
while (w)
|
||||
{
|
||||
if (w == m_dlg)
|
||||
return;
|
||||
w = w->GetParent();
|
||||
}
|
||||
|
||||
if (m_dropped)
|
||||
{
|
||||
DropDown(false);
|
||||
if (ev.GetEventObject() == m_btn)
|
||||
m_ignoreDrop = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnClick(wxCommandEvent &event)
|
||||
{
|
||||
if (m_ignoreDrop)
|
||||
{
|
||||
m_ignoreDrop = false;
|
||||
m_txt->SetFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
DropDown();
|
||||
m_cal->SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnSetFocus(wxFocusEvent &ev)
|
||||
{
|
||||
if (m_txt)
|
||||
{
|
||||
m_txt->SetFocus();
|
||||
m_txt->SetSelection(0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnKillFocus(wxFocusEvent &ev)
|
||||
{
|
||||
wxDateTime dt;
|
||||
dt.ParseFormat(m_txt->GetValue(), m_format);
|
||||
if (!dt.IsValid())
|
||||
m_txt->SetValue(wxEmptyString);
|
||||
else
|
||||
m_txt->SetValue(dt.Format(m_format));
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnSelChange(wxCalendarEvent &ev)
|
||||
{
|
||||
if (m_cal)
|
||||
{
|
||||
m_txt->SetValue(m_cal->GetDate().FormatISODate());
|
||||
if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
|
||||
{
|
||||
DropDown(false);
|
||||
m_txt->SetFocus();
|
||||
}
|
||||
}
|
||||
ev.SetEventObject(this);
|
||||
ev.SetId(GetId());
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
GetParent()->GetEventHandler()->ProcessEvent(ev);
|
||||
#else
|
||||
GetParent()->ProcessEvent(ev);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnText(wxCommandEvent &ev)
|
||||
{
|
||||
ev.SetEventObject(this);
|
||||
ev.SetId(GetId());
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
GetParent()->GetEventHandler()->ProcessEvent(ev);
|
||||
#else
|
||||
GetParent()->ProcessEvent(ev);
|
||||
#endif
|
||||
|
||||
// We'll create an additional event if the date is valid.
|
||||
// If the date isn't valid, the user's probably in the middle of typing
|
||||
wxString txt = m_txt->GetValue();
|
||||
wxDateTime dt;
|
||||
if (!txt.IsEmpty())
|
||||
dt.ParseFormat(txt, m_format);
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
wxCalendarEvent cev(m_cal, dt, wxEVT_CALENDAR_SEL_CHANGED);
|
||||
#else
|
||||
wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
|
||||
cev.SetDate(dt);
|
||||
#endif
|
||||
cev.SetEventObject(this);
|
||||
cev.SetId(GetId());
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
GetParent()->GetEventHandler()->ProcessEvent(cev);
|
||||
#else
|
||||
GetParent()->ProcessEvent(cev);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnEditKey(wxKeyEvent &ev)
|
||||
{
|
||||
if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
|
||||
DropDown();
|
||||
else
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void wxCalendarBox::OnCalKey(wxKeyEvent &ev)
|
||||
{
|
||||
if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
|
||||
DropDown(false);
|
||||
else
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
#endif // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
|
||||
41
ctl/ctlAuiNotebook.cpp
Normal file
41
ctl/ctlAuiNotebook.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlAuiNotebook.cpp - Custom AUI Notebook class
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The primary purpose of this class is to pass child focus events from
|
||||
// the notebook to the parent window. This is the only way we can grab
|
||||
// focus events from the page controls.
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/aui/auibook.h>
|
||||
|
||||
// App headers
|
||||
#include <ctl/ctlAuiNotebook.h>
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlAuiNotebook, wxAuiNotebook)
|
||||
EVT_CHILD_FOCUS(ctlAuiNotebook::OnChildFocus)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
// Handle, and pass up child focus events
|
||||
void ctlAuiNotebook::OnChildFocus(wxChildFocusEvent &event)
|
||||
{
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
wxAuiNotebook::OnChildFocusNotebook(event);
|
||||
GetParent()->GetEventHandler()->AddPendingEvent(event);
|
||||
#else
|
||||
wxAuiNotebook::OnChildFocus(event);
|
||||
GetParent()->AddPendingEvent(event);
|
||||
#endif
|
||||
|
||||
}
|
||||
90
ctl/ctlCheckTreeView.cpp
Normal file
90
ctl/ctlCheckTreeView.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlCheckTreeView.cpp - TreeView with Checkboxes
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/busyinfo.h>
|
||||
#include <wx/imaglist.h>
|
||||
#include <wx/wizard.h>
|
||||
#include <wx/treectrl.h>
|
||||
|
||||
// App headers
|
||||
#include "ctl/ctlCheckTreeView.h"
|
||||
#include "images/checked.pngc"
|
||||
#include "images/disabled.pngc"
|
||||
#include "images/unchecked.pngc"
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlCheckTreeView, wxTreeCtrl)
|
||||
EVT_LEFT_DOWN( ctlCheckTreeView::OnLeftClick)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
ctlCheckTreeView::ctlCheckTreeView(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
|
||||
: wxTreeCtrl(parent, id, pos, size, style)
|
||||
{
|
||||
wxImageList *treeimages = new wxImageList(16, 16, true, 3);
|
||||
treeimages->Add(*unchecked_png_img);
|
||||
treeimages->Add(*checked_png_img);
|
||||
treeimages->Add(*disabled_png_img);
|
||||
SetImageList(treeimages);
|
||||
}
|
||||
|
||||
|
||||
void ctlCheckTreeView::OnLeftClick(wxMouseEvent &evt)
|
||||
{
|
||||
int flags;
|
||||
wxTreeItemId node = HitTest(evt.GetPosition(), flags);
|
||||
int newimage = 0;
|
||||
|
||||
if ((flags & wxTREE_HITTEST_ONITEMLABEL) || (flags & wxTREE_HITTEST_ONITEMICON))
|
||||
{
|
||||
if (GetItemImage(node) == 0)
|
||||
newimage = 1;
|
||||
else if (GetItemImage(node) == 1)
|
||||
newimage = 0;
|
||||
|
||||
if (newimage == 0 || newimage == 1)
|
||||
SetParentAndChildImage(node, newimage);
|
||||
if (newimage == 1)
|
||||
SetParentImage(node, newimage);
|
||||
}
|
||||
|
||||
evt.Skip();
|
||||
}
|
||||
|
||||
void ctlCheckTreeView::SetParentAndChildImage(wxTreeItemId node, int newimage)
|
||||
{
|
||||
SetItemImage(node, newimage);
|
||||
wxTreeItemIdValue childData;
|
||||
wxTreeItemId child = GetFirstChild(node, childData);
|
||||
while (child.IsOk())
|
||||
{
|
||||
SetParentAndChildImage(child, newimage);
|
||||
child = GetNextChild(node, childData);
|
||||
}
|
||||
}
|
||||
|
||||
void ctlCheckTreeView::SetParentImage(wxTreeItemId node, int newimage)
|
||||
{
|
||||
if (node.IsOk())
|
||||
{
|
||||
SetItemImage(node, newimage);
|
||||
SetParentImage(GetItemParent(node), newimage);
|
||||
}
|
||||
}
|
||||
|
||||
bool ctlCheckTreeView::IsChecked(const wxTreeItemId &node)
|
||||
{
|
||||
return (GetItemImage(node) == 1);
|
||||
}
|
||||
|
||||
121
ctl/ctlColourPicker.cpp
Normal file
121
ctl/ctlColourPicker.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the BSD Licence
|
||||
//
|
||||
// ctlColourPicker.cpp - Colour Picker with a wxBitmapButton
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/colordlg.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "ctl/ctlColourPicker.h"
|
||||
|
||||
|
||||
void ctlColourPicker::Create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
|
||||
{
|
||||
// Set Default Title
|
||||
m_title = _("Choose the colour");
|
||||
|
||||
// Create the wxBitmapButton
|
||||
((wxBitmapButton *)this)->Create(parent, id, wxNullBitmap, pos, size);
|
||||
|
||||
// Set the handler for a click
|
||||
Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlColourPicker::DoProcessLeftClick) );
|
||||
}
|
||||
|
||||
|
||||
void ctlColourPicker::DoProcessLeftClick(wxMouseEvent &event)
|
||||
{
|
||||
wxColourData clrData;
|
||||
|
||||
// Initialise custom colours
|
||||
for (int index = 0; index < 16; index++)
|
||||
clrData.SetCustomColour(index, settings->GetCustomColour(index));
|
||||
|
||||
// If there is an initial colour, set it for wxColourDialog
|
||||
if (m_colour_clr.IsOk())
|
||||
clrData.SetColour(m_colour_clr);
|
||||
|
||||
// Declare the new dialog
|
||||
wxColourDialog dialog(this, &clrData);
|
||||
|
||||
// and set its title
|
||||
dialog.SetTitle(m_title);
|
||||
|
||||
// Now, show it
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
clrData = dialog.GetColourData();
|
||||
SetColour(clrData.GetColour());
|
||||
|
||||
// Store custom colours
|
||||
for (int index = 0; index < 16; index++)
|
||||
settings->SetCustomColour(index, clrData.GetCustomColour(index).GetAsString(wxC2S_HTML_SYNTAX));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ctlColourPicker::UpdateColour()
|
||||
{
|
||||
if (!m_colour_clr.IsOk())
|
||||
{
|
||||
wxLogError(wxT("ohoh"));
|
||||
wxBitmap empty(1, 1);
|
||||
SetBitmapLabel(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
wxSize sz = GetSize();
|
||||
sz.x -= 2 * GetMarginX();
|
||||
sz.y -= 2 * GetMarginY();
|
||||
|
||||
wxPoint topleft;
|
||||
if ( sz.x < 1 )
|
||||
sz.x = 1;
|
||||
else if ( sz.y < 1 )
|
||||
sz.y = 1;
|
||||
wxBitmap bmp(sz.x, sz.y);
|
||||
|
||||
wxMemoryDC dc(bmp);
|
||||
dc.SetBrush(wxBrush(m_colour_clr));
|
||||
dc.DrawRectangle(topleft, sz);
|
||||
|
||||
((wxBitmapButton *)this)->SetBitmapLabel(bmp);
|
||||
}
|
||||
|
||||
wxColour ctlColourPicker::GetColour()
|
||||
{
|
||||
return m_colour_clr;
|
||||
}
|
||||
|
||||
wxString ctlColourPicker::GetColourString()
|
||||
{
|
||||
if (!m_colour_clr.IsOk())
|
||||
return wxEmptyString;
|
||||
return m_colour_clr.GetAsString();
|
||||
}
|
||||
|
||||
void ctlColourPicker::SetColour(const wxColour &colour)
|
||||
{
|
||||
m_colour_clr = colour;
|
||||
UpdateColour();
|
||||
}
|
||||
|
||||
void ctlColourPicker::SetColour(const wxString &colour)
|
||||
{
|
||||
m_colour_clr = wxColour(colour);
|
||||
UpdateColour();
|
||||
}
|
||||
|
||||
void ctlColourPicker::SetTitle(const wxString &title)
|
||||
{
|
||||
m_title = title;
|
||||
}
|
||||
240
ctl/ctlComboBox.cpp
Normal file
240
ctl/ctlComboBox.cpp
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlComboBox.cpp - enhanced combobox control
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// App headers
|
||||
#include "ctl/ctlComboBox.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgSet.h"
|
||||
|
||||
|
||||
class StringClientData : public wxClientData
|
||||
{
|
||||
public:
|
||||
StringClientData(const wxChar *c)
|
||||
{
|
||||
str = c;
|
||||
}
|
||||
wxString str;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int ctlComboBoxFix::Append(const wxString &item, const wxString &str)
|
||||
{
|
||||
return wxComboBox::Append(item, new StringClientData(str));
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBoxFix::Append(const wxString &item, long l)
|
||||
{
|
||||
return wxComboBox::Append(item, (void *)l);
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBoxFix::Append(const wxString &item, OID oid)
|
||||
{
|
||||
return wxComboBox::Append(item, (void *)oid);
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBoxFix::FillLongKey(pgConn *conn, const wxChar *qry)
|
||||
{
|
||||
int cnt = 0;
|
||||
pgSetIterator set(conn->ExecuteSet(qry));
|
||||
while (set.RowsLeft())
|
||||
{
|
||||
long l = set.GetLong(0);
|
||||
wxString txt = set.GetVal(1);
|
||||
Append(txt, l);
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBoxFix::FillOidKey(pgConn *conn, const wxChar *qry)
|
||||
{
|
||||
int cnt = 0;
|
||||
pgSetIterator set(conn->ExecuteSet(qry));
|
||||
while (set.RowsLeft())
|
||||
{
|
||||
OID oid = set.GetOid(0);
|
||||
wxString txt = set.GetVal(1);
|
||||
Append(txt, oid);
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBoxFix::FillStringKey(pgConn *conn, const wxChar *qry)
|
||||
{
|
||||
int cnt = 0;
|
||||
pgSetIterator set(conn->ExecuteSet(qry));
|
||||
while (set.RowsLeft())
|
||||
{
|
||||
wxString str = set.GetVal(0);
|
||||
wxString txt = set.GetVal(1);
|
||||
Append(txt, str);
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
long ctlComboBoxFix::GetLongKey(int sel)
|
||||
{
|
||||
if (sel < 0)
|
||||
sel = GetSelection();
|
||||
return (long)wxItemContainer::GetClientData(sel);
|
||||
}
|
||||
|
||||
OID ctlComboBoxFix::GetOIDKey(int sel)
|
||||
{
|
||||
if (sel < 0)
|
||||
sel = GetSelection();
|
||||
return (OID)wxItemContainer::GetClientData(sel);
|
||||
}
|
||||
|
||||
wxString ctlComboBoxFix::GetStringKey(int sel)
|
||||
{
|
||||
if (sel < 0)
|
||||
sel = GetSelection();
|
||||
StringClientData *scd = (StringClientData *)wxItemContainer::GetClientObject(sel);
|
||||
if (scd)
|
||||
return scd->str;
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
|
||||
ctlComboBoxFix::ctlComboBoxFix(wxWindow *wnd, int id, wxPoint pos, wxSize siz, long attr)
|
||||
: wxComboBox(wnd, id, wxEmptyString, pos, siz, 0, NULL, attr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool ctlComboBoxFix::SetKey(long val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < GetCount() ; i++)
|
||||
{
|
||||
if (GetLongKey(i) == val)
|
||||
{
|
||||
SetSelection(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SetSelection(wxNOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ctlComboBoxFix::SetKey(OID val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < GetCount() ; i++)
|
||||
{
|
||||
if (GetOIDKey(i) == val)
|
||||
{
|
||||
SetSelection(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SetSelection(wxNOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ctlComboBoxFix::SetKey(const wxString &val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < GetCount() ; i++)
|
||||
{
|
||||
if (GetStringKey(i) == val)
|
||||
{
|
||||
SetSelection(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SetSelection(wxNOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
||||
ctlComboBox::ctlComboBox(wxWindow *wnd, int id, wxPoint pos, wxSize siz, long attr)
|
||||
: ctlComboBoxFix(wnd, id, pos, siz, attr)
|
||||
{
|
||||
#ifdef __WXGTK__
|
||||
SetEditable(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBox::GuessSelection(wxCommandEvent &ev)
|
||||
{
|
||||
if (ev.GetEventType() != wxEVT_COMMAND_TEXT_UPDATED)
|
||||
return GetGuessedSelection();
|
||||
|
||||
wxString str = wxComboBox::GetValue();
|
||||
if (str.Length())
|
||||
{
|
||||
long pos = GetInsertionPoint();
|
||||
|
||||
long sel, count = GetCount();
|
||||
int len = str.Length();
|
||||
for (sel = 0 ; sel < count ; sel++)
|
||||
{
|
||||
if (str == GetString(sel).Left(len))
|
||||
{
|
||||
SetSelection(sel);
|
||||
wxString current = GetString(sel);
|
||||
SetSelection(pos, current.Length());
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int ctlComboBox::GetGuessedSelection() const
|
||||
{
|
||||
int sel = GetCurrentSelection();
|
||||
|
||||
if (sel < 0)
|
||||
sel = FindString(GetValue());
|
||||
return sel;
|
||||
}
|
||||
|
||||
int ctlComboBox::GetSelection() const
|
||||
{
|
||||
int sel = 0;
|
||||
#ifdef __WXMSW__
|
||||
sel = GetCurrentSelection();
|
||||
|
||||
if (sel < 0)
|
||||
#endif
|
||||
sel = FindString(GetValue());
|
||||
return sel;
|
||||
}
|
||||
|
||||
wxString ctlComboBox::GetGuessedStringSelection() const
|
||||
{
|
||||
int sel = GetGuessedSelection();
|
||||
if (sel < 0)
|
||||
return wxEmptyString;
|
||||
else
|
||||
return GetString(sel);
|
||||
}
|
||||
623
ctl/ctlDefaultSecurityPanel.cpp
Normal file
623
ctl/ctlDefaultSecurityPanel.cpp
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlDefaultSecurityPanel.cpp - Panel with default security information
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "ctl/ctlDefaultSecurityPanel.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "dlg/dlgProperty.h"
|
||||
#include "schema/pgGroup.h"
|
||||
#include "schema/pgUser.h"
|
||||
#include "utils/sysLogger.h"
|
||||
|
||||
#include <wx/arrimpl.cpp>
|
||||
|
||||
defaultPrivilegesOn g_defPrivTables('r', wxT("Tables"), wxT("arwdDxt")),
|
||||
g_defPrivSequences('S', wxT("Sequences"), wxT("rwU")),
|
||||
g_defPrivFunctions('f', wxT("Functions"), wxT("X")),
|
||||
g_defPrivTypes('T', wxT("Types"), wxT("U"));
|
||||
|
||||
defaultPrivilegesOn::defaultPrivilegesOn(const wxChar privType, const wxString &privOn, const wxString &privileges)
|
||||
: m_privilegeType(privType), m_privilegesOn(privOn), m_privileges(privileges) {}
|
||||
|
||||
ctlDefaultSecurityPanel::ctlDefaultSecurityPanel(pgConn *conn, wxNotebook *nb, wxImageList *imgList)
|
||||
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize), nbNotebook(NULL)
|
||||
{
|
||||
nb->AddPage(this, _("Default Privileges"));
|
||||
|
||||
wxFlexGridSizer *mainSizer = new wxFlexGridSizer(1, 1, 1, 1);
|
||||
mainSizer->AddGrowableCol(0);
|
||||
mainSizer->AddGrowableRow(0);
|
||||
|
||||
nbNotebook = new wxNotebook(this, -1, wxDefaultPosition, wxDefaultSize, 0, _("Default ACLs"));
|
||||
|
||||
m_defPrivOnTablesPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivTables, imgList);
|
||||
m_defPrivOnSeqsPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivSequences, imgList);
|
||||
m_defPrivOnFuncsPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivFunctions, imgList);
|
||||
if (conn->BackendMinimumVersion(9, 2))
|
||||
m_defPrivOnTypesPanel = new ctlDefaultPrivilegesPanel(this, nbNotebook, g_defPrivTypes, imgList);
|
||||
else
|
||||
m_defPrivOnTypesPanel = NULL;
|
||||
|
||||
mainSizer->Add(nbNotebook, 0, wxEXPAND | wxALL, 2);
|
||||
|
||||
this->SetSizer(mainSizer);
|
||||
mainSizer->Fit(this);
|
||||
}
|
||||
|
||||
void ctlDefaultSecurityPanel::UpdatePrivilegePages(bool createDefPrivs, const wxString &defPrivsOnTables,
|
||||
const wxString &defPrivsOnSeqs, const wxString &defPrivsOnFuncs, const wxString &defPrivsOnTypes)
|
||||
{
|
||||
if (!createDefPrivs)
|
||||
{
|
||||
nbNotebook->Enable(false);
|
||||
return;
|
||||
}
|
||||
m_defPrivOnTablesPanel->Update(defPrivsOnTables);
|
||||
m_defPrivOnSeqsPanel->Update(defPrivsOnSeqs);
|
||||
m_defPrivOnFuncsPanel->Update(defPrivsOnFuncs);
|
||||
if (m_defPrivOnTypesPanel)
|
||||
m_defPrivOnTypesPanel->Update(defPrivsOnTypes);
|
||||
}
|
||||
|
||||
|
||||
wxString ctlDefaultSecurityPanel::GetDefaultPrivileges(const wxString &schemaName)
|
||||
{
|
||||
wxString strDefPrivs;
|
||||
int nPageCount = nbNotebook->GetPageCount();
|
||||
for (int index = 0; index < nPageCount; index++)
|
||||
{
|
||||
strDefPrivs += (dynamic_cast<ctlDefaultPrivilegesPanel *>(nbNotebook->GetPage(index)))->GetDefaultPrivileges(schemaName);
|
||||
}
|
||||
return strDefPrivs;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ctlDefaultPrivilegesPanel
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlDefaultPrivilegesPanel, wxPanel)
|
||||
EVT_LIST_ITEM_SELECTED(CTL_DEFLBPRIV, ctlDefaultPrivilegesPanel::OnPrivSelChange)
|
||||
EVT_BUTTON(CTL_DEFADDPRIV, ctlDefaultPrivilegesPanel::OnAddPriv)
|
||||
EVT_BUTTON(CTL_DEFDELPRIV, ctlDefaultPrivilegesPanel::OnDelPriv)
|
||||
EVT_TEXT(CTL_DEFCBGROUP, ctlDefaultPrivilegesPanel::OnGroupChange)
|
||||
EVT_COMBOBOX(CTL_DEFCBGROUP, ctlDefaultPrivilegesPanel::OnGroupChange)
|
||||
EVT_CHECKBOX(CTL_DEFALLPRIV, ctlDefaultPrivilegesPanel::OnPrivCheckAll)
|
||||
EVT_CHECKBOX(CTL_DEFALLPRIVGRANT, ctlDefaultPrivilegesPanel::OnPrivCheckAllGrant)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 2, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 4, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 6, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 8, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 10, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 12, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 14, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_DEFPRIVCB + 16, ctlDefaultPrivilegesPanel::OnPrivCheck)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
DEFINE_LOCAL_EVENT_TYPE(EVT_DEFAULTSECURITYPANEL_CHANGE)
|
||||
|
||||
ctlDefaultPrivilegesPanel::ctlDefaultPrivilegesPanel(ctlDefaultSecurityPanel *defSecurityPanel, wxNotebook *nb,
|
||||
defaultPrivilegesOn &privOn, wxImageList *imgList)
|
||||
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize), m_defPrivChanged(false),
|
||||
m_privilegeType(privOn), m_defSecurityPanel(defSecurityPanel)
|
||||
{
|
||||
|
||||
nb->AddPage(this, m_privilegeType.m_privilegesOn);
|
||||
|
||||
allPrivileges = 0;
|
||||
privCheckboxes = 0;
|
||||
m_currentSelectedPriv = NULL;
|
||||
|
||||
privilegeCount = m_privilegeType.m_privileges.Length();
|
||||
|
||||
wxFlexGridSizer *item0 = new wxFlexGridSizer(3, 1, 5, 5);
|
||||
item0->AddGrowableCol(0);
|
||||
item0->AddGrowableRow(0);
|
||||
|
||||
privCheckboxes = new wxCheckBox*[privilegeCount * 2];
|
||||
|
||||
wxFlexGridSizer *itemSizer1 = new wxFlexGridSizer(1, 1, 5, 5);
|
||||
itemSizer1->AddGrowableCol(0);
|
||||
itemSizer1->AddGrowableRow(0);
|
||||
|
||||
wxString strGroupLabel = settings->GetShowUsersForPrivileges() ? _("Role/Group") : _("Role");
|
||||
|
||||
lbPrivileges = new ctlListView(this, CTL_DEFLBPRIV, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
|
||||
lbPrivileges->SetImageList(imgList, wxIMAGE_LIST_SMALL);
|
||||
lbPrivileges->AddColumn(_("Role/Group"), 60, wxLIST_FORMAT_LEFT);
|
||||
lbPrivileges->AddColumn(_("Privileges"), 60, wxLIST_FORMAT_LEFT);
|
||||
itemSizer1->Add(lbPrivileges, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
item0->Add(itemSizer1, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxBoxSizer *itemSizer2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnAddPriv = new wxButton(this, CTL_DEFADDPRIV, _("Add/Change"));
|
||||
itemSizer2->Add(btnAddPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
btnDelPriv = new wxButton(this, CTL_DEFDELPRIV, _("Remove"));
|
||||
itemSizer2->Add(btnDelPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
item0->Add(itemSizer2, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
wxStaticBox *sb = new wxStaticBox(this, -1, _("Privileges"));
|
||||
wxBoxSizer *itemSizer3 = new wxStaticBoxSizer( sb, wxVERTICAL );
|
||||
item0->Add(itemSizer3, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxBoxSizer *itemSizer4a = new wxBoxSizer(wxHORIZONTAL);
|
||||
stGroup = new wxStaticText(this, CTL_DEFSTATICGROUP, strGroupLabel);
|
||||
itemSizer4a->Add(stGroup, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
cbGroups = new ctlComboBox(this, CTL_DEFCBGROUP, wxDefaultPosition, wxDefaultSize);
|
||||
cbGroups->Append(wxT("public"));
|
||||
cbGroups->SetSelection(0);
|
||||
itemSizer4a->Add(cbGroups, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
itemSizer3->Add(itemSizer4a, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
/* border size depends on the plateform */
|
||||
#ifdef __WXMSW__
|
||||
int bordersize = 4;
|
||||
#endif
|
||||
#ifdef __WXMAC__
|
||||
int bordersize = 3;
|
||||
#endif
|
||||
#ifdef __WXGTK__
|
||||
int bordersize = 0;
|
||||
#endif
|
||||
|
||||
wxBoxSizer *itemSizer5 = new wxBoxSizer(wxHORIZONTAL);
|
||||
allPrivileges = new wxCheckBox(this, CTL_DEFALLPRIV, wxT("ALL"));
|
||||
itemSizer5->Add(allPrivileges, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
allPrivilegesGrant = new wxCheckBox(this, CTL_DEFALLPRIVGRANT, wxT("WITH GRANT OPTION"));
|
||||
itemSizer5->Add(allPrivilegesGrant, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
allPrivilegesGrant->Disable();
|
||||
itemSizer3->Add(itemSizer5, 0, wxALL, bordersize);
|
||||
|
||||
for (int index = 0; index < privilegeCount; index++)
|
||||
{
|
||||
int i = index * 2;
|
||||
wxChar privilege = m_privilegeType.m_privileges.GetChar(index);
|
||||
wxString priv = pgObject::GetPrivilegeName(privilege);
|
||||
|
||||
wxCheckBox *cb;
|
||||
wxBoxSizer *itemSizer6 = new wxBoxSizer(wxHORIZONTAL);
|
||||
cb = new wxCheckBox(this, CTL_DEFPRIVCB + i, priv);
|
||||
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
privCheckboxes[i++] = cb;
|
||||
cb = new wxCheckBox(this, CTL_DEFPRIVCB + i, wxT("WITH GRANT OPTION"));
|
||||
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
cb->Disable();
|
||||
privCheckboxes[i] = cb;
|
||||
itemSizer3->Add(itemSizer6, 0, wxALL, bordersize);
|
||||
|
||||
}
|
||||
|
||||
this->SetSizer(item0);
|
||||
item0->Fit(this);
|
||||
}
|
||||
|
||||
|
||||
ctlDefaultPrivilegesPanel::~ctlDefaultPrivilegesPanel()
|
||||
{
|
||||
cbGroups->Clear();
|
||||
if (privCheckboxes)
|
||||
delete[] privCheckboxes;
|
||||
}
|
||||
|
||||
// Find the privileges for the selected user from the map
|
||||
// and select the check-boxes accordingly
|
||||
bool ctlDefaultPrivilegesPanel::PrivCheckBoxUpdate(wxString &strRole)
|
||||
{
|
||||
int index = 0;
|
||||
wxString strKey = strRole;
|
||||
defPrivHash::iterator priv = m_privileges.find(strKey);
|
||||
bool canGrant = CanGrant();
|
||||
|
||||
if (priv == m_privileges.end())
|
||||
{
|
||||
m_currentSelectedPriv = NULL;
|
||||
allPrivileges->SetValue(true);
|
||||
allPrivilegesGrant->Enable(canGrant);
|
||||
for (; index < privilegeCount; index++)
|
||||
{
|
||||
privCheckboxes[index * 2]->Enable();
|
||||
privCheckboxes[index * 2]->SetValue(true);
|
||||
privCheckboxes[index * 2 + 1]->Enable(canGrant);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
allPrivileges->SetValue(false);
|
||||
allPrivilegesGrant->SetValue(false);
|
||||
allPrivilegesGrant->Enable(false);
|
||||
}
|
||||
|
||||
wxString currentPrivileges = priv->second.m_modified ? priv->second.m_newPriv : priv->second.m_origPriv;
|
||||
m_currentSelectedPriv = &priv->second;
|
||||
|
||||
for (index = 0; index < privilegeCount; index++)
|
||||
{
|
||||
int privAt = currentPrivileges.Find(m_privilegeType.m_privileges.GetChar(index));
|
||||
if (privAt != wxNOT_FOUND)
|
||||
{
|
||||
privCheckboxes[index * 2]->SetValue(true);
|
||||
privCheckboxes[index * 2 + 1]->Enable(canGrant);
|
||||
if (canGrant && (unsigned int)privAt < currentPrivileges.Length() - 1 && currentPrivileges.GetChar(privAt + 1) == wxT('*'))
|
||||
privCheckboxes[index * 2 + 1]->SetValue(true);
|
||||
else
|
||||
privCheckboxes[index * 2 + 1]->SetValue(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
privCheckboxes[index * 2]->SetValue(false);
|
||||
privCheckboxes[index * 2 + 1]->SetValue(false);
|
||||
privCheckboxes[index * 2 + 1]->Disable();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ctlDefaultPrivilegesPanel::CanGrant()
|
||||
{
|
||||
if (cbGroups->GetValue() == wxT("public"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnGroupChange(wxCommandEvent &ev)
|
||||
{
|
||||
cbGroups->GuessSelection(ev);
|
||||
|
||||
wxString strRole = cbGroups->GetValue();
|
||||
|
||||
if (strRole.IsEmpty())
|
||||
return;
|
||||
|
||||
btnAddPriv->Enable(!PrivCheckBoxUpdate(strRole));
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnPrivCheckAll(wxCommandEvent &ev)
|
||||
{
|
||||
bool all = allPrivileges->GetValue();
|
||||
allPrivilegesGrant->Enable(all && CanGrant());
|
||||
|
||||
for (int i = 0; i < privilegeCount; i++)
|
||||
{
|
||||
privCheckboxes[i * 2]->SetValue(all);
|
||||
CheckGrantOpt(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnPrivCheckAllGrant(wxCommandEvent &ev)
|
||||
{
|
||||
bool grant = allPrivilegesGrant->GetValue();
|
||||
for (int i = 0 ; i < privilegeCount ; i++)
|
||||
privCheckboxes[i * 2 + 1]->SetValue(grant);
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnPrivCheck(wxCommandEvent &ev)
|
||||
{
|
||||
int id = (ev.GetId() - CTL_DEFPRIVCB) / 2;
|
||||
CheckGrantOpt(id);
|
||||
btnAddPriv->Enable();
|
||||
if (!privCheckboxes[id * 2]->GetValue())
|
||||
allPrivileges->SetValue(false);
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::CheckGrantOpt(int id)
|
||||
{
|
||||
bool canGrant = (privCheckboxes[id * 2]->GetValue() && CanGrant());
|
||||
if (canGrant)
|
||||
privCheckboxes[id * 2 + 1]->Enable(true);
|
||||
else
|
||||
{
|
||||
privCheckboxes[id * 2 + 1]->SetValue(false);
|
||||
privCheckboxes[id * 2 + 1]->Disable();
|
||||
}
|
||||
btnAddPriv->Enable();
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnDelPriv(wxCommandEvent &ev)
|
||||
{
|
||||
int pos;
|
||||
wxString strRole;
|
||||
if ((pos = lbPrivileges->GetFirstSelected()) == -1)
|
||||
return;
|
||||
|
||||
wxListItem info;
|
||||
info.m_itemId = pos;
|
||||
info.m_col = 0;
|
||||
info.m_mask = wxLIST_MASK_TEXT;
|
||||
|
||||
if (lbPrivileges->GetItem(info))
|
||||
strRole = info.m_text;
|
||||
|
||||
wxString strKey = strRole;
|
||||
defPrivHash::iterator priv = m_privileges.find(strKey);
|
||||
|
||||
if (priv != m_privileges.end())
|
||||
{
|
||||
priv->second.m_modified = true;
|
||||
priv->second.m_newPriv = wxT("");
|
||||
if (m_currentSelectedPriv == &priv->second)
|
||||
PrivCheckBoxUpdate(strRole);
|
||||
}
|
||||
|
||||
lbPrivileges->DeleteCurrentItem();
|
||||
m_defPrivChanged = true;
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnAddPriv(wxCommandEvent &ev)
|
||||
{
|
||||
wxString strRole, strPriv;
|
||||
bool isPresent = (m_currentSelectedPriv != NULL &&
|
||||
((m_currentSelectedPriv->m_modified &&
|
||||
!m_currentSelectedPriv->m_newPriv.IsEmpty()) ||
|
||||
(!m_currentSelectedPriv->m_modified)))
|
||||
|| lbPrivileges->FindItem(-1, cbGroups->GetGuessedStringSelection()) >= 0;
|
||||
|
||||
if (allPrivileges && allPrivileges->GetValue())
|
||||
{
|
||||
if (allPrivilegesGrant->GetValue())
|
||||
{
|
||||
for (int index = 0; index < privilegeCount; index++)
|
||||
{
|
||||
strPriv += m_privilegeType.m_privileges.GetChar(index);
|
||||
strPriv += wxT('*');
|
||||
}
|
||||
}
|
||||
else
|
||||
strPriv = m_privilegeType.m_privileges;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < privilegeCount; index++)
|
||||
{
|
||||
if (privCheckboxes[index * 2]->GetValue())
|
||||
{
|
||||
strPriv += m_privilegeType.m_privileges.GetChar(index);
|
||||
if(privCheckboxes[index * 2 + 1]->IsEnabled() && privCheckboxes[index * 2 + 1]->GetValue())
|
||||
strPriv += wxT('*');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_currentSelectedPriv)
|
||||
{
|
||||
strRole = cbGroups->GetGuessedStringSelection();
|
||||
|
||||
if (strRole.IsEmpty())
|
||||
return;
|
||||
|
||||
defPrivilege priv;
|
||||
priv.m_username = strRole;
|
||||
priv.m_origPriv = wxT("");
|
||||
priv.m_modified = true;
|
||||
priv.m_newPriv = strPriv;
|
||||
|
||||
wxString strKey = strRole;
|
||||
m_privileges[strKey] = priv;
|
||||
|
||||
defPrivHash::iterator itr = m_privileges.find(strKey);
|
||||
m_currentSelectedPriv = &itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_currentSelectedPriv->m_modified ?
|
||||
m_currentSelectedPriv->m_newPriv == strPriv :
|
||||
m_currentSelectedPriv->m_origPriv == strPriv)
|
||||
{
|
||||
ev.Skip();
|
||||
return;
|
||||
}
|
||||
strRole = m_currentSelectedPriv->m_username;
|
||||
m_currentSelectedPriv->m_modified = true;
|
||||
m_currentSelectedPriv->m_newPriv = strPriv;
|
||||
}
|
||||
|
||||
if (!isPresent)
|
||||
{
|
||||
int icon;
|
||||
if (strRole.IsSameAs(wxT("public"), true))
|
||||
icon = PGICON_PUBLIC;
|
||||
else if (strRole.StartsWith(wxT("group ")))
|
||||
icon = groupFactory.GetIconId();
|
||||
else
|
||||
icon = userFactory.GetIconId();
|
||||
long pos = lbPrivileges->GetItemCount();
|
||||
|
||||
lbPrivileges->InsertItem(pos, strRole, icon);
|
||||
lbPrivileges->SetItem(pos, 1, strPriv);
|
||||
}
|
||||
else
|
||||
{
|
||||
long nCount = lbPrivileges->GetItemCount();
|
||||
wxListItem info;
|
||||
|
||||
for (int index = 0; index < nCount; index++)
|
||||
{
|
||||
wxString strTempRole;
|
||||
|
||||
info.m_itemId = index;
|
||||
info.m_col = 0;
|
||||
info.m_mask = wxLIST_MASK_TEXT;
|
||||
|
||||
if (lbPrivileges->GetItem(info))
|
||||
strTempRole = info.m_text;
|
||||
|
||||
if (strTempRole == strRole)
|
||||
{
|
||||
lbPrivileges->SetItem(index, 1, strPriv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_defPrivChanged = true;
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::OnPrivSelChange(wxListEvent &ev)
|
||||
{
|
||||
if (!cbGroups)
|
||||
return;
|
||||
if (allPrivileges)
|
||||
{
|
||||
allPrivileges->SetValue(false);
|
||||
allPrivilegesGrant->SetValue(false);
|
||||
allPrivilegesGrant->Disable();
|
||||
}
|
||||
long pos = lbPrivileges->GetSelection();
|
||||
if (pos >= 0)
|
||||
{
|
||||
wxString name = lbPrivileges->GetText(pos);
|
||||
wxString value = lbPrivileges->GetText(pos, 1);
|
||||
|
||||
pos = cbGroups->FindString(name);
|
||||
if (pos < 0)
|
||||
{
|
||||
cbGroups->Append(name);
|
||||
pos = cbGroups->GetCount() - 1;
|
||||
}
|
||||
cbGroups->SetSelection(pos);
|
||||
|
||||
int i;
|
||||
for (i = 0 ; i < privilegeCount ; i++)
|
||||
{
|
||||
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
|
||||
{
|
||||
privCheckboxes[i * 2]->Enable();
|
||||
int index = value.Find(m_privilegeType.m_privileges.GetChar(i));
|
||||
if (index >= 0)
|
||||
{
|
||||
privCheckboxes[i * 2]->SetValue(true);
|
||||
privCheckboxes[i * 2 + 1]->SetValue(value.Mid(index + 1, 1) == wxT("*"));
|
||||
}
|
||||
else
|
||||
privCheckboxes[i * 2]->SetValue(false);
|
||||
}
|
||||
CheckGrantOpt(i);
|
||||
}
|
||||
PrivCheckBoxUpdate(name);
|
||||
}
|
||||
btnAddPriv->Enable();
|
||||
}
|
||||
|
||||
|
||||
void ctlDefaultPrivilegesPanel::Update(wxString strDefPrivs)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
|
||||
lbPrivileges->DeleteAllItems();
|
||||
|
||||
m_privileges.clear();
|
||||
|
||||
cbGroups->Clear();
|
||||
cbGroups->Append(wxT("public"));
|
||||
for (; index < m_defSecurityPanel->m_groups.GetCount(); index++)
|
||||
{
|
||||
cbGroups->Append(m_defSecurityPanel->m_groups[index]);
|
||||
}
|
||||
|
||||
if (!strDefPrivs.IsEmpty())
|
||||
{
|
||||
wxString strRole, strPriv;
|
||||
strDefPrivs.Replace(wxT("\\\""), wxT("\""), true);
|
||||
strDefPrivs.Replace(wxT("\\\\"), wxT("\\"), true);
|
||||
|
||||
// Removing starting brace '{' and ending brace '}'
|
||||
strDefPrivs = strDefPrivs.SubString(1, strDefPrivs.Length() - 1);
|
||||
|
||||
long pos = 0;
|
||||
|
||||
while (pgObject::findUserPrivs(strDefPrivs, strRole, strPriv))
|
||||
{
|
||||
int icon;
|
||||
if (strRole.IsSameAs(wxT("public"), true))
|
||||
icon = PGICON_PUBLIC;
|
||||
else if (cbGroups->FindString(strRole) != wxNOT_FOUND)
|
||||
icon = userFactory.GetIconId();
|
||||
else if (cbGroups->FindString(wxT("group ") + strRole) != wxNOT_FOUND)
|
||||
{
|
||||
icon = groupFactory.GetIconId();
|
||||
strRole = wxT("group ") + strRole;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
defPrivilege priv;
|
||||
priv.m_username = strRole;
|
||||
priv.m_origPriv = strPriv;
|
||||
priv.m_modified = false;
|
||||
priv.m_newPriv = wxT("");
|
||||
|
||||
wxString strKey = strRole;
|
||||
m_privileges[strKey] = priv;
|
||||
|
||||
pos = lbPrivileges->GetItemCount();
|
||||
|
||||
lbPrivileges->InsertItem(pos, strRole, icon);
|
||||
lbPrivileges->SetItem(pos, 1, strPriv);
|
||||
|
||||
strRole = wxT("");
|
||||
strPriv = wxT("");
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxString ctlDefaultPrivilegesPanel::GetDefaultPrivileges(const wxString &schemaName)
|
||||
{
|
||||
if(!m_defPrivChanged)
|
||||
return wxT("");
|
||||
|
||||
wxString strDefPrivs;
|
||||
defPrivHash::iterator itr = m_privileges.begin();
|
||||
|
||||
for (; itr != m_privileges.end(); itr++)
|
||||
{
|
||||
if(itr->second.m_modified)
|
||||
{
|
||||
wxString strRole = itr->second.m_username;
|
||||
|
||||
if (strRole.StartsWith(wxT("group ")))
|
||||
{
|
||||
strRole = strRole.Mid(6);
|
||||
strRole = qtIdent(strRole);
|
||||
}
|
||||
else if (strRole != wxT("public"))
|
||||
strRole = qtIdent(strRole);
|
||||
|
||||
wxString strOrigDefPrivs = itr->second.m_origPriv;
|
||||
wxString strNewDefPrivs = itr->second.m_newPriv;
|
||||
|
||||
strDefPrivs += pgObject::GetDefaultPrivileges(m_privilegeType.m_privilegesOn.Upper(), m_privilegeType.m_privileges, schemaName, strOrigDefPrivs, strNewDefPrivs, strRole);
|
||||
}
|
||||
}
|
||||
return strDefPrivs;
|
||||
}
|
||||
|
||||
|
||||
145
ctl/ctlListView.cpp
Normal file
145
ctl/ctlListView.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlListView.cpp - enhanced listview control
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
// App headers
|
||||
#include "ctl/ctlListView.h"
|
||||
#include "utils/misc.h"
|
||||
|
||||
|
||||
ctlListView::ctlListView(wxWindow *p, int id, wxPoint pos, wxSize siz, long attr)
|
||||
: wxListView(p, id, pos, siz, attr | wxLC_REPORT)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
long ctlListView::GetSelection()
|
||||
{
|
||||
return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
}
|
||||
|
||||
|
||||
wxString ctlListView::GetText(long row, long col)
|
||||
{
|
||||
wxListItem item;
|
||||
item.SetId(row);
|
||||
item.SetColumn(col);
|
||||
item.SetMask(wxLIST_MASK_TEXT);
|
||||
GetItem(item);
|
||||
return item.GetText();
|
||||
};
|
||||
|
||||
|
||||
void ctlListView::AddColumn(const wxString &text, int size, int format)
|
||||
{
|
||||
if (size == wxLIST_AUTOSIZE || size == wxLIST_AUTOSIZE_USEHEADER)
|
||||
{
|
||||
InsertColumn(GetColumnCount(), text, format, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertColumn(GetColumnCount(), text, format, ConvertDialogToPixels(wxPoint(size, 0)).x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long ctlListView::AppendItem(int icon, const wxString &val, const wxString &val2, const wxString &val3, const wxString &val4)
|
||||
{
|
||||
long pos = InsertItem(GetItemCount(), val, icon);
|
||||
if (!val2.IsEmpty())
|
||||
SetItem(pos, 1, val2);
|
||||
if (!val3.IsEmpty())
|
||||
SetItem(pos, 2, val3);
|
||||
if (!val4.IsEmpty())
|
||||
SetItem(pos, 3, val4);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
void ctlListView::CreateColumns(wxImageList *images, const wxString &left, const wxString &right, int leftSize)
|
||||
{
|
||||
int rightSize;
|
||||
if (leftSize < 0)
|
||||
{
|
||||
#ifdef __WXMAC__
|
||||
leftSize = rightSize = (GetParent()->GetSize().GetWidth() - 20) / 2;
|
||||
#else
|
||||
leftSize = rightSize = GetSize().GetWidth() / 2;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (leftSize)
|
||||
leftSize = ConvertDialogToPixels(wxPoint(leftSize, 0)).x;
|
||||
|
||||
#ifdef __WXMAC__
|
||||
rightSize = (GetParent()->GetSize().GetWidth() - 20) - leftSize;
|
||||
#else
|
||||
rightSize = GetClientSize().GetWidth() - leftSize;
|
||||
#endif
|
||||
}
|
||||
if (!leftSize)
|
||||
{
|
||||
InsertColumn(0, left, wxLIST_FORMAT_LEFT, GetClientSize().GetWidth());
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertColumn(0, left, wxLIST_FORMAT_LEFT, leftSize);
|
||||
InsertColumn(1, right, wxLIST_FORMAT_LEFT, rightSize);
|
||||
}
|
||||
|
||||
if (images)
|
||||
SetImageList(images, wxIMAGE_LIST_SMALL);
|
||||
}
|
||||
|
||||
|
||||
void ctlListView::CreateColumns(wxImageList *images, const wxString &str1, const wxString &str2, const wxString &str3, int leftSize)
|
||||
{
|
||||
int rightSize;
|
||||
if (leftSize < 0)
|
||||
{
|
||||
#ifdef __WXMAC__
|
||||
leftSize = rightSize = (GetParent()->GetSize().GetWidth() - 20) / 2;
|
||||
#else
|
||||
leftSize = rightSize = GetSize().GetWidth() / 2;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (leftSize)
|
||||
leftSize = ConvertDialogToPixels(wxPoint(leftSize, 0)).x;
|
||||
|
||||
#ifdef __WXMAC__
|
||||
rightSize = (GetParent()->GetSize().GetWidth() - 20) - leftSize;
|
||||
#else
|
||||
rightSize = GetClientSize().GetWidth() - leftSize;
|
||||
#endif
|
||||
}
|
||||
if (!leftSize)
|
||||
{
|
||||
InsertColumn(0, str1, wxLIST_FORMAT_LEFT, GetClientSize().GetWidth());
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertColumn(0, str1, wxLIST_FORMAT_LEFT, leftSize);
|
||||
InsertColumn(1, str2, wxLIST_FORMAT_LEFT, rightSize / 2);
|
||||
InsertColumn(2, str3, wxLIST_FORMAT_LEFT, rightSize / 2);
|
||||
}
|
||||
|
||||
if (images)
|
||||
SetImageList(images, wxIMAGE_LIST_SMALL);
|
||||
}
|
||||
193
ctl/ctlMenuToolbar.cpp
Normal file
193
ctl/ctlMenuToolbar.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlMenuToolbar.cpp - Menu tool bar
|
||||
//
|
||||
// This code is essentially stolen (with the authors permission) from
|
||||
// Paul Nelson (http://www.pnelsoncomposer.com/)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "ctl/ctlMenuToolbar.h"
|
||||
|
||||
#include <wx/listimpl.cpp>
|
||||
WX_DEFINE_LIST(ctlMenuToolList);
|
||||
|
||||
/* XPM */
|
||||
static const char *pulldown_xpm[] =
|
||||
{
|
||||
"16 16 2 1",
|
||||
". c Black",
|
||||
" c None",
|
||||
/* pixels */
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ....... ",
|
||||
" ..... ",
|
||||
" ... ",
|
||||
" . ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" "
|
||||
};
|
||||
|
||||
|
||||
DEFINE_EVENT_TYPE(wxEVT_FILL_MENU)
|
||||
|
||||
//////////////////
|
||||
// ctlMenuButton
|
||||
//////////////////
|
||||
|
||||
void ctlMenuButton::Create(wxWindow *window, wxToolBar *toolBar, int ID, wxMenu *menu)
|
||||
{
|
||||
wxBitmap bmpPulldown(pulldown_xpm);
|
||||
wxSize pulldownSize;
|
||||
|
||||
#ifdef __WXMSW__
|
||||
pulldownSize.Set(12, 16);
|
||||
#else
|
||||
#ifdef __WXGTK__
|
||||
pulldownSize.Set(18, 16);
|
||||
#else
|
||||
pulldownSize.Set(10, 16);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
m_toolBar = toolBar;
|
||||
m_menu = menu;
|
||||
|
||||
((wxBitmapButton *)this)->Create(window, ID, bmpPulldown, wxDefaultPosition, pulldownSize, wxNO_BORDER);
|
||||
Connect(ID, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlMenuButton::DoProcessLeftClick) );
|
||||
}
|
||||
|
||||
|
||||
void ctlMenuButton::DoProcessLeftClick(wxMouseEvent &event)
|
||||
{
|
||||
wxPoint menu_pos;
|
||||
|
||||
if(m_toolBar)
|
||||
{
|
||||
wxSize tool_size = m_toolBar->GetToolSize();
|
||||
wxSize button_size = GetSize();
|
||||
|
||||
// ** Assume that pulldown is to the right of a standard toolbar button,
|
||||
// so, move the x position back one standard toolbar button's width
|
||||
menu_pos.x = - tool_size.GetWidth();
|
||||
menu_pos.y = button_size.GetHeight() / 2 + tool_size.GetHeight() / 2;
|
||||
|
||||
#ifdef __WXMAC__
|
||||
wxSize tbar_size = m_toolBar->GetSize();
|
||||
wxPoint button_pos = GetPosition();
|
||||
int iToolSep = m_toolBar->GetToolSeparation();
|
||||
if(iToolSep == 0) iToolSep = 5;
|
||||
|
||||
menu_pos.x += - iToolSep;
|
||||
menu_pos.y = tbar_size.GetHeight() - button_pos.y / 2;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
wxSize button_size;
|
||||
button_size = GetSize();
|
||||
menu_pos.x = 0;
|
||||
menu_pos.y = button_size.GetHeight();
|
||||
}
|
||||
|
||||
DoPopupMenu(m_menu, menu_pos.x, menu_pos.y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////
|
||||
// ctlMenuTool
|
||||
////////////////
|
||||
|
||||
ctlMenuTool::ctlMenuTool(wxToolBarToolBase *new_tool, int toolId)
|
||||
{
|
||||
m_tool = new_tool;
|
||||
m_menu = NULL;
|
||||
m_toolId = toolId;
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// ctlMenuToolbar
|
||||
////////////////////
|
||||
|
||||
ctlMenuToolbar::ctlMenuToolbar(wxFrame *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
|
||||
: wxToolBar(parent, id, pos, size, style, name)
|
||||
{
|
||||
m_frame = parent;
|
||||
m_menuTools = NULL;
|
||||
|
||||
Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ctlMenuToolbar::DoProcessLeftClick) );
|
||||
}
|
||||
|
||||
|
||||
ctlMenuToolbar::~ctlMenuToolbar()
|
||||
{
|
||||
if(m_menuTools)
|
||||
delete m_menuTools;
|
||||
}
|
||||
|
||||
ctlMenuButton *ctlMenuToolbar::AddMenuPulldownTool(int toolId, const wxString &label, const wxString &shortHelpString, wxMenu *popupmenu)
|
||||
{
|
||||
ctlMenuButton *menu_button = new ctlMenuButton(this, toolId, popupmenu);
|
||||
|
||||
AddControl(menu_button);
|
||||
|
||||
return menu_button;
|
||||
}
|
||||
|
||||
|
||||
void ctlMenuToolbar::DoProcessLeftClick(wxMouseEvent &event)
|
||||
{
|
||||
ctlMenuToolList::Node *node = NULL;
|
||||
ctlMenuTool *menu_tool = NULL;
|
||||
wxToolBarToolBase *clickTool = FindToolForPosition(event.m_x, event.m_y);
|
||||
|
||||
if(clickTool == NULL || m_menuTools == NULL)
|
||||
{
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// search for clickTool in the list of menu tools
|
||||
node = m_menuTools->GetFirst();
|
||||
for( ; node ; node = node->GetNext())
|
||||
{
|
||||
menu_tool = node->GetData();
|
||||
if(menu_tool->m_tool == clickTool)
|
||||
break;
|
||||
}
|
||||
|
||||
if(node == NULL)
|
||||
{
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
wxSize tbar_size = GetSize();
|
||||
wxPoint menu_pos;
|
||||
|
||||
menu_pos.x = event.m_x - 20;
|
||||
menu_pos.y = tbar_size.GetHeight() - 2;
|
||||
|
||||
PopupMenu(menu_tool->m_menu, menu_pos);
|
||||
}
|
||||
202
ctl/ctlProgressStatusBar.cpp
Normal file
202
ctl/ctlProgressStatusBar.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlProgressStatusBar.cpp - A status bar with progress indicator
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/gauge.h>
|
||||
|
||||
// App headers
|
||||
#include "ctl/ctlProgressStatusBar.h"
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlProgressStatusBar, wxStatusBar)
|
||||
EVT_SIZE(ctlProgressStatusBar::OnSize)
|
||||
EVT_TIMER(wxID_ANY, ctlProgressStatusBar::OnTimer)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
const unsigned short ctlProgressStatusBar::ms_increment = 100;
|
||||
const unsigned short ctlProgressStatusBar::ms_progressbar_width = 70;
|
||||
const unsigned short ctlProgressStatusBar::ms_progressstatus_width = 130;
|
||||
|
||||
ctlProgressStatusBar::ctlProgressStatusBar(wxWindow *parent, bool showProgressInitially, bool autoProgressive, int max)
|
||||
: wxStatusBar(parent, wxID_ANY), m_progressStopped(!showProgressInitially),
|
||||
m_autoProgressive(autoProgressive), m_autoValIncrementing(true),
|
||||
m_hr(0), m_min(0), m_sec(0), m_mil(0), m_val(0)
|
||||
{
|
||||
static int widths[] = { -1, ms_progressbar_width, ms_progressstatus_width};
|
||||
int fields = 0;
|
||||
|
||||
if (max <= 0)
|
||||
max = 100;
|
||||
|
||||
if (m_autoProgressive)
|
||||
fields = 3;
|
||||
else
|
||||
fields = 2;
|
||||
|
||||
m_progress = new wxGauge(this, -1, max);
|
||||
|
||||
wxStatusBar::SetFieldsCount(fields);
|
||||
wxStatusBar::SetStatusWidths(fields, widths);
|
||||
|
||||
m_timer.SetOwner(this->GetEventHandler());
|
||||
|
||||
if (!showProgressInitially)
|
||||
{
|
||||
m_progressStopped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progressStopped = false;
|
||||
if (m_autoProgressive)
|
||||
{
|
||||
m_timer.Start(ms_increment);
|
||||
|
||||
m_hr = 0;
|
||||
m_min = 0;
|
||||
m_sec = 0;
|
||||
m_mil = 0;
|
||||
}
|
||||
}
|
||||
// Dummy event to place the progressbar at right place
|
||||
wxSizeEvent ev;
|
||||
this->OnSize(ev);
|
||||
}
|
||||
|
||||
ctlProgressStatusBar::~ctlProgressStatusBar()
|
||||
{
|
||||
if (m_timer.IsRunning())
|
||||
{
|
||||
m_timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ctlProgressStatusBar::OnSize(wxSizeEvent &ev)
|
||||
{
|
||||
// We should have hide the progress bar
|
||||
if (GetFieldsCount() <= ProgressBar_field)
|
||||
return;
|
||||
|
||||
wxRect r;
|
||||
GetFieldRect(ProgressBar_field, r);
|
||||
|
||||
m_progress->SetSize(r.x + 1, r.y + 1, r.width - 2, r.height - 2);
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlProgressStatusBar::OnTimer(wxTimerEvent &ev)
|
||||
{
|
||||
if (m_progressStopped || !m_autoProgressive)
|
||||
{
|
||||
m_timer.Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
m_val = m_progress->GetValue();
|
||||
int max = m_progress->GetRange();
|
||||
|
||||
if (m_val == max)
|
||||
{
|
||||
m_val -= 1;
|
||||
m_autoValIncrementing = false;
|
||||
}
|
||||
else if (m_val == 0)
|
||||
{
|
||||
m_val = 1;
|
||||
m_autoValIncrementing = true;
|
||||
}
|
||||
else if (m_autoValIncrementing)
|
||||
{
|
||||
m_val += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_val -= 1;
|
||||
}
|
||||
|
||||
m_progress->SetValue(m_val);
|
||||
|
||||
m_mil += ms_increment;
|
||||
|
||||
if (m_mil >= 1000)
|
||||
{
|
||||
m_mil = m_mil - 1000;
|
||||
m_sec += 1;
|
||||
|
||||
if (m_sec == 60)
|
||||
{
|
||||
m_sec = 0;
|
||||
m_min += 1;
|
||||
|
||||
if (m_min == 60)
|
||||
{
|
||||
m_min = 0;
|
||||
m_hr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxStatusBar::SetStatusText(wxString::Format(_("%d:%02d:%02d.%03d"), m_hr, m_min, m_sec, m_mil), ProgressStatus_field);
|
||||
}
|
||||
|
||||
|
||||
void ctlProgressStatusBar::ShowProgress(bool restart)
|
||||
{
|
||||
if (m_progressStopped)
|
||||
{
|
||||
m_progressStopped = false;
|
||||
|
||||
if (!m_timer.IsRunning())
|
||||
{
|
||||
if (restart)
|
||||
{
|
||||
m_val = 0;
|
||||
m_hr = m_min = m_sec = m_mil = 0;
|
||||
}
|
||||
|
||||
m_progress->SetValue(m_val);
|
||||
m_timer.Start(ms_increment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ctlProgressStatusBar::StopProgress()
|
||||
{
|
||||
if (m_timer.IsRunning())
|
||||
m_timer.Stop();
|
||||
|
||||
m_progress->SetValue(0);
|
||||
m_progressStopped = true;
|
||||
}
|
||||
|
||||
void ctlProgressStatusBar::SetFieldsCount(int number, const int *widths)
|
||||
{
|
||||
m_progress->Show(number > ProgressBar_field);
|
||||
wxStatusBar::SetFieldsCount( number, widths);
|
||||
|
||||
// Dummy Size event (to reposition the progress bar)
|
||||
wxSizeEvent ev;
|
||||
this->OnSize(ev);
|
||||
}
|
||||
|
||||
void ctlProgressStatusBar::SetStatusWidths(int n, const int widths_field[])
|
||||
{
|
||||
wxStatusBar::SetStatusWidths( n, widths_field);
|
||||
|
||||
// Dummy Size event (to reposition the progress bar)
|
||||
wxSizeEvent ev;
|
||||
this->OnSize(ev);
|
||||
}
|
||||
1044
ctl/ctlSQLBox.cpp
Normal file
1044
ctl/ctlSQLBox.cpp
Normal file
File diff suppressed because it is too large
Load diff
554
ctl/ctlSQLGrid.cpp
Normal file
554
ctl/ctlSQLGrid.cpp
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlSQLGrid.cpp - SQL Query result window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
#include "db/pgConn.h"
|
||||
#include "ctl/ctlSQLGrid.h"
|
||||
#include "utils/sysSettings.h"
|
||||
#include "frm/frmExport.h"
|
||||
|
||||
|
||||
#define EXTRAEXTENT_HEIGHT 6
|
||||
#define EXTRAEXTENT_WIDTH 6
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlSQLGrid, wxGrid)
|
||||
EVT_MOUSEWHEEL(ctlSQLGrid::OnMouseWheel)
|
||||
EVT_GRID_COL_SIZE(ctlSQLGrid::OnGridColSize)
|
||||
EVT_GRID_LABEL_LEFT_CLICK(ctlSQLGrid::OnLabelClick)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlSQLGrid, wxGrid)
|
||||
|
||||
ctlSQLGrid::ctlSQLGrid()
|
||||
{
|
||||
}
|
||||
|
||||
ctlSQLGrid::ctlSQLGrid(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
|
||||
: wxGrid(parent, id, pos, size, wxWANTS_CHARS | wxVSCROLL | wxHSCROLL)
|
||||
{
|
||||
// Set cells font
|
||||
wxFont fntCells(settings->GetSQLFont());
|
||||
SetDefaultCellFont(fntCells);
|
||||
// Set labels font
|
||||
wxFont fntLabel(settings->GetSystemFont());
|
||||
fntLabel.SetWeight(wxBOLD);
|
||||
SetLabelFont(fntLabel);
|
||||
SetColLabelAlignment(wxALIGN_LEFT, wxALIGN_CENTER);
|
||||
SetRowLabelSize(50);
|
||||
SetDefaultRowSize(fntCells.GetPointSize() * 2 + 2);
|
||||
SetColLabelSize(fntLabel.GetPointSize() * 4);
|
||||
SetDefaultCellOverflow(false);
|
||||
|
||||
Connect(wxID_ANY, wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEventHandler(ctlSQLGrid::OnLabelDoubleClick));
|
||||
}
|
||||
|
||||
void ctlSQLGrid::OnGridColSize(wxGridSizeEvent &event)
|
||||
{
|
||||
// Save key="index:label", value=size
|
||||
int col = event.GetRowOrCol();
|
||||
colSizes[GetColKeyValue(col)] = GetColSize(col);
|
||||
}
|
||||
void ctlSQLGrid::OnCopy(wxCommandEvent &ev)
|
||||
{
|
||||
Copy();
|
||||
}
|
||||
|
||||
void ctlSQLGrid::OnMouseWheel(wxMouseEvent &event)
|
||||
{
|
||||
if (event.ControlDown() || event.CmdDown())
|
||||
{
|
||||
wxFont fontlabel = GetLabelFont();
|
||||
wxFont fontcells = GetDefaultCellFont();
|
||||
if (event.GetWheelRotation() > 0)
|
||||
{
|
||||
fontlabel.SetPointSize(fontlabel.GetPointSize() - 1);
|
||||
fontcells.SetPointSize(fontcells.GetPointSize() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fontlabel.SetPointSize(fontlabel.GetPointSize() + 1);
|
||||
fontcells.SetPointSize(fontcells.GetPointSize() + 1);
|
||||
}
|
||||
SetLabelFont(fontlabel);
|
||||
SetDefaultCellFont(fontcells);
|
||||
SetColLabelSize(fontlabel.GetPointSize() * 4);
|
||||
SetDefaultRowSize(fontcells.GetPointSize() * 2);
|
||||
for (int index = 0; index < GetNumberCols(); index++)
|
||||
SetColSize(index, -1);
|
||||
ForceRefresh();
|
||||
}
|
||||
else
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
wxString ctlSQLGrid::GetExportLine(int row)
|
||||
{
|
||||
return GetExportLine(row, 0, GetNumberCols() - 1);
|
||||
}
|
||||
|
||||
|
||||
wxString ctlSQLGrid::GetExportLine(int row, int col1, int col2)
|
||||
{
|
||||
wxArrayInt cols;
|
||||
wxString str;
|
||||
int i;
|
||||
|
||||
if (col2 < col1)
|
||||
return str;
|
||||
|
||||
cols.Alloc(col2 - col1 + 1);
|
||||
for (i = col1; i <= col2; i++)
|
||||
{
|
||||
cols.Add(i);
|
||||
}
|
||||
|
||||
return GetExportLine(row, cols);
|
||||
}
|
||||
|
||||
wxString ctlSQLGrid::GetExportLine(int row, wxArrayInt cols)
|
||||
{
|
||||
wxString str;
|
||||
unsigned int col;
|
||||
|
||||
if (GetNumberCols() == 0)
|
||||
return str;
|
||||
|
||||
for (col = 0 ; col < cols.Count() ; col++)
|
||||
{
|
||||
if (col > 0)
|
||||
str.Append(settings->GetCopyColSeparator());
|
||||
|
||||
wxString text = GetCellValue(row, cols[col]);
|
||||
|
||||
bool needQuote = false;
|
||||
if (settings->GetCopyQuoting() == 1)
|
||||
{
|
||||
needQuote = IsColText(cols[col]);
|
||||
}
|
||||
else if (settings->GetCopyQuoting() == 2)
|
||||
/* Quote everything */
|
||||
needQuote = true;
|
||||
|
||||
if (needQuote)
|
||||
str.Append(settings->GetCopyQuoteChar());
|
||||
str.Append(text);
|
||||
if (needQuote)
|
||||
str.Append(settings->GetCopyQuoteChar());
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
wxString ctlSQLGrid::GetColumnName(int colNum)
|
||||
{
|
||||
wxString columnName = GetColLabelValue(colNum);
|
||||
columnName = columnName.Left(columnName.find(wxT("\n")));
|
||||
return columnName;
|
||||
}
|
||||
|
||||
void ctlSQLGrid::AppendColumnHeader(wxString &str, int start, int end)
|
||||
{
|
||||
size_t i, arrsize;
|
||||
arrsize = (end - start + 1);
|
||||
wxArrayInt columns;
|
||||
|
||||
for(i = 0; i < arrsize; i++)
|
||||
{
|
||||
columns.Add(start + i);
|
||||
}
|
||||
|
||||
AppendColumnHeader(str, columns);
|
||||
}
|
||||
|
||||
void ctlSQLGrid::AppendColumnHeader(wxString &str, wxArrayInt columns)
|
||||
{
|
||||
if(settings->GetColumnNames())
|
||||
{
|
||||
bool CopyQuoting = (settings->GetCopyQuoting() == 1 || settings->GetCopyQuoting() == 2);
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < columns.Count() ; i++)
|
||||
{
|
||||
long columnPos = columns.Item(i);
|
||||
if(i > 0)
|
||||
str.Append(settings->GetCopyColSeparator());
|
||||
|
||||
if(CopyQuoting)
|
||||
str.Append(settings->GetCopyQuoteChar());
|
||||
str.Append(GetColumnName(columnPos));
|
||||
if(CopyQuoting)
|
||||
str.Append(settings->GetCopyQuoteChar());
|
||||
|
||||
}
|
||||
str.Append(END_OF_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
int ctlSQLGrid::Copy()
|
||||
{
|
||||
wxString str;
|
||||
int copied = 0;
|
||||
size_t i;
|
||||
|
||||
|
||||
|
||||
if (GetSelectedRows().GetCount())
|
||||
{
|
||||
AppendColumnHeader(str, 0, (GetNumberCols() - 1));
|
||||
|
||||
wxArrayInt rows = GetSelectedRows();
|
||||
|
||||
for (i = 0 ; i < rows.GetCount() ; i++)
|
||||
{
|
||||
str.Append(GetExportLine(rows.Item(i)));
|
||||
if (rows.GetCount() > 1)
|
||||
str.Append(END_OF_LINE);
|
||||
}
|
||||
|
||||
copied = rows.GetCount();
|
||||
}
|
||||
else if (GetSelectedCols().GetCount())
|
||||
{
|
||||
wxArrayInt cols = GetSelectedCols();
|
||||
size_t numRows = GetNumberRows();
|
||||
|
||||
AppendColumnHeader(str, cols);
|
||||
|
||||
for (i = 0 ; i < numRows ; i++)
|
||||
{
|
||||
str.Append(GetExportLine(i, cols));
|
||||
|
||||
if (numRows > 1)
|
||||
str.Append(END_OF_LINE);
|
||||
}
|
||||
|
||||
copied = numRows;
|
||||
}
|
||||
else if (GetSelectionBlockTopLeft().GetCount() > 0 &&
|
||||
GetSelectionBlockBottomRight().GetCount() > 0)
|
||||
{
|
||||
unsigned int x1, x2, y1, y2;
|
||||
|
||||
x1 = GetSelectionBlockTopLeft()[0].GetCol();
|
||||
x2 = GetSelectionBlockBottomRight()[0].GetCol();
|
||||
y1 = GetSelectionBlockTopLeft()[0].GetRow();
|
||||
y2 = GetSelectionBlockBottomRight()[0].GetRow();
|
||||
|
||||
AppendColumnHeader(str, x1, x2);
|
||||
|
||||
for (i = y1; i <= y2; i++)
|
||||
{
|
||||
str.Append(GetExportLine(i, x1, x2));
|
||||
|
||||
if (y2 > y1)
|
||||
str.Append(END_OF_LINE);
|
||||
}
|
||||
|
||||
copied = y2 - y1 + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int row, col;
|
||||
|
||||
row = GetGridCursorRow();
|
||||
col = GetGridCursorCol();
|
||||
|
||||
AppendColumnHeader(str, col, col);
|
||||
|
||||
str.Append(GetExportLine(row, col, col));
|
||||
copied = 1;
|
||||
}
|
||||
|
||||
if (copied && wxTheClipboard->Open())
|
||||
{
|
||||
wxTheClipboard->SetData(new wxTextDataObject(str));
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
copied = 0;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
void ctlSQLGrid::OnLabelDoubleClick(wxGridEvent &event)
|
||||
{
|
||||
int maxHeight, maxWidth;
|
||||
GetClientSize(&maxWidth, &maxHeight);
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
|
||||
int extent, extentWant = 0;
|
||||
|
||||
if (row >= 0)
|
||||
{
|
||||
for (col = 0 ; col < GetNumberCols() ; col++)
|
||||
{
|
||||
extent = GetBestSize(row, col).GetHeight();
|
||||
if (extent > extentWant)
|
||||
extentWant = extent;
|
||||
}
|
||||
|
||||
extentWant += EXTRAEXTENT_HEIGHT;
|
||||
extentWant = wxMax(extentWant, GetRowMinimalAcceptableHeight());
|
||||
extentWant = wxMin(extentWant, maxHeight * 3 / 4);
|
||||
int currentHeight = GetRowHeight(row);
|
||||
|
||||
if (currentHeight >= maxHeight * 3 / 4 || currentHeight == extentWant)
|
||||
extentWant = GetRowMinimalAcceptableHeight();
|
||||
else if (currentHeight < maxHeight / 4)
|
||||
extentWant = wxMin(maxHeight / 4, extentWant);
|
||||
else if (currentHeight < maxHeight / 2)
|
||||
extentWant = wxMin(maxHeight / 2, extentWant);
|
||||
else if (currentHeight < maxHeight * 3 / 4)
|
||||
extentWant = wxMin(maxHeight * 3 / 4, extentWant);
|
||||
|
||||
if (extentWant != currentHeight)
|
||||
{
|
||||
BeginBatch();
|
||||
if(IsCellEditControlShown())
|
||||
{
|
||||
HideCellEditControl();
|
||||
SaveEditControlValue();
|
||||
}
|
||||
|
||||
SetRowHeight(row, extentWant);
|
||||
EndBatch();
|
||||
}
|
||||
}
|
||||
else if (col >= 0)
|
||||
{
|
||||
// Holding Ctrl or Meta switches back to automatic column's sizing
|
||||
if (event.ControlDown() || event.CmdDown())
|
||||
{
|
||||
colSizes.erase(GetColKeyValue(col));
|
||||
BeginBatch();
|
||||
if(IsCellEditControlShown())
|
||||
{
|
||||
HideCellEditControl();
|
||||
SaveEditControlValue();
|
||||
}
|
||||
AutoSizeColumn(col, false);
|
||||
EndBatch();
|
||||
}
|
||||
else // toggle between some predefined sizes
|
||||
{
|
||||
|
||||
if (col < (int)colMaxSizes.GetCount() && colMaxSizes[col] >= 0)
|
||||
extentWant = colMaxSizes[col];
|
||||
else
|
||||
{
|
||||
for (row = 0 ; row < GetNumberRows() ; row++)
|
||||
{
|
||||
if (CheckRowPresent(row))
|
||||
{
|
||||
extent = GetBestSize(row, col).GetWidth();
|
||||
if (extent > extentWant)
|
||||
extentWant = extent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extentWant += EXTRAEXTENT_WIDTH;
|
||||
extentWant = wxMax(extentWant, GetColMinimalAcceptableWidth());
|
||||
extentWant = wxMin(extentWant, maxWidth * 3 / 4);
|
||||
int currentWidth = GetColumnWidth(col);
|
||||
|
||||
if (currentWidth >= maxWidth * 3 / 4 || currentWidth == extentWant)
|
||||
extentWant = GetColMinimalAcceptableWidth();
|
||||
else if (currentWidth < maxWidth / 4)
|
||||
extentWant = wxMin(maxWidth / 4, extentWant);
|
||||
else if (currentWidth < maxWidth / 2)
|
||||
extentWant = wxMin(maxWidth / 2, extentWant);
|
||||
else if (currentWidth < maxWidth * 3 / 4)
|
||||
extentWant = wxMin(maxWidth * 3 / 4, extentWant);
|
||||
|
||||
if (extentWant != currentWidth)
|
||||
{
|
||||
BeginBatch();
|
||||
if(IsCellEditControlShown())
|
||||
{
|
||||
HideCellEditControl();
|
||||
SaveEditControlValue();
|
||||
}
|
||||
SetColumnWidth(col, extentWant);
|
||||
EndBatch();
|
||||
colSizes[GetColKeyValue(col)] = extentWant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ctlSQLGrid::OnLabelClick(wxGridEvent &event)
|
||||
{
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
|
||||
// add support for (de)selecting multiple rows and cols with Control pressed
|
||||
if ( row >= 0 && (event.ControlDown() || event.CmdDown()) )
|
||||
{
|
||||
if (GetSelectedRows().Index(row) == wxNOT_FOUND)
|
||||
SelectRow(row, true);
|
||||
else
|
||||
DeselectRow(row);
|
||||
}
|
||||
else if ( col >= 0 && (event.ControlDown() || event.CmdDown()) )
|
||||
{
|
||||
if (GetSelectedCols().Index(col) == wxNOT_FOUND)
|
||||
SelectCol(col, true);
|
||||
else
|
||||
DeselectCol(col);
|
||||
}
|
||||
else
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void ctlSQLGrid::AutoSizeColumn(int col, bool setAsMin, bool doLimit)
|
||||
{
|
||||
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
|
||||
if (it != colSizes.end()) // Restore user-specified size
|
||||
SetColSize(col, it->second);
|
||||
else
|
||||
wxGrid::AutoSizeColumn(col, setAsMin);
|
||||
|
||||
if (doLimit)
|
||||
{
|
||||
int newSize, oldSize;
|
||||
int maxSize, totalSize = 0, availSize;
|
||||
|
||||
oldSize = GetColSize(col);
|
||||
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
|
||||
maxSize = availSize / 2;
|
||||
for (int i = 0 ; i < GetNumberCols() ; i++)
|
||||
totalSize += GetColSize(i);
|
||||
|
||||
if (oldSize > maxSize && totalSize > availSize)
|
||||
{
|
||||
totalSize -= oldSize;
|
||||
/* Shrink wide column to maxSize.
|
||||
* If the rest of the columns are short, make sure to use all the remaining space,
|
||||
* but no more than oldSize (which is enough according to AutoSizeColumns())
|
||||
*/
|
||||
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
|
||||
SetColSize(col, newSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ctlSQLGrid::AutoSizeColumns(bool setAsMin)
|
||||
{
|
||||
wxCoord newSize, oldSize;
|
||||
wxCoord maxSize, totalSize = 0, availSize;
|
||||
int col, nCols = GetNumberCols();
|
||||
int row, nRows = GetNumberRows();
|
||||
colMaxSizes.Empty();
|
||||
|
||||
/* We need to check each cell's width to choose best. wxGrid::AutoSizeColumns()
|
||||
* is good, but looping through long result sets gives a noticeable slowdown.
|
||||
* Thus we'll check every first 500 cells for each column.
|
||||
*/
|
||||
|
||||
// First pass: auto-size columns
|
||||
for (col = 0 ; col < nCols; col++)
|
||||
{
|
||||
ColKeySizeHashMap::iterator it = colSizes.find(GetColKeyValue(col));
|
||||
if (it != colSizes.end()) // Restore user-specified size
|
||||
{
|
||||
newSize = it->second;
|
||||
colMaxSizes.Add(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxClientDC dc(GetGridWindow());
|
||||
newSize = 0;
|
||||
// get cells's width
|
||||
for (row = 0 ; row < wxMin(nRows, 500) ; row++)
|
||||
{
|
||||
wxSize size = GetBestSize(row, col);
|
||||
if ( size.x > newSize )
|
||||
newSize = size.x;
|
||||
}
|
||||
// get column's label width
|
||||
wxCoord w, h;
|
||||
dc.SetFont( GetLabelFont() );
|
||||
dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h );
|
||||
if ( GetColLabelTextOrientation() == wxVERTICAL )
|
||||
w = h;
|
||||
|
||||
if ( w > newSize )
|
||||
newSize = w;
|
||||
|
||||
if (!newSize)
|
||||
newSize = GetRowLabelSize();
|
||||
else
|
||||
// leave some space around text
|
||||
newSize += 6;
|
||||
|
||||
colMaxSizes.Add(newSize);
|
||||
}
|
||||
SetColSize(col, newSize);
|
||||
totalSize += newSize;
|
||||
}
|
||||
|
||||
availSize = GetClientSize().GetWidth() - GetRowLabelSize();
|
||||
|
||||
// Second pass: shrink wide columns if exceeded available width
|
||||
if (totalSize > availSize)
|
||||
{
|
||||
// A wide column shouldn't take up more than 50% of the visible space
|
||||
maxSize = availSize / 2;
|
||||
for (col = 0 ; col < nCols ; col++)
|
||||
{
|
||||
oldSize = GetColSize(col);
|
||||
// Is too wide and no user-specified size
|
||||
if (oldSize > maxSize && !(col < (int)colMaxSizes.GetCount() && colMaxSizes[col] == -1))
|
||||
{
|
||||
totalSize -= oldSize;
|
||||
/* Shrink wide column to maxSize.
|
||||
* If the rest of the columns are short, make sure to use all the remaining space,
|
||||
* but no more than oldSize (which is enough according to first pass)
|
||||
*/
|
||||
newSize = wxMin(oldSize, wxMax(maxSize, availSize - totalSize));
|
||||
SetColSize(col, newSize);
|
||||
totalSize += newSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxString ctlSQLGrid::GetColKeyValue(int col)
|
||||
{
|
||||
wxString colKey = wxString::Format(wxT("%d:"), col) + GetColLabelValue(col);
|
||||
return colKey;
|
||||
}
|
||||
|
||||
wxSize ctlSQLGrid::GetBestSize(int row, int col)
|
||||
{
|
||||
wxSize size;
|
||||
|
||||
wxGridCellAttr *attr = GetCellAttr(row, col);
|
||||
wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
|
||||
if ( renderer )
|
||||
{
|
||||
wxClientDC dc(GetGridWindow());
|
||||
size = renderer->GetBestSize(*this, *attr, dc, row, col);
|
||||
renderer->DecRef();
|
||||
}
|
||||
|
||||
attr->DecRef();
|
||||
|
||||
return size;
|
||||
}
|
||||
480
ctl/ctlSQLResult.cpp
Normal file
480
ctl/ctlSQLResult.cpp
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlSQLResult.cpp - SQL Query result window
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgQueryThread.h"
|
||||
#include "ctl/ctlSQLResult.h"
|
||||
#include "utils/sysSettings.h"
|
||||
#include "frm/frmExport.h"
|
||||
|
||||
|
||||
|
||||
ctlSQLResult::ctlSQLResult(wxWindow *parent, pgConn *_conn, wxWindowID id, const wxPoint &pos, const wxSize &size)
|
||||
: ctlSQLGrid(parent, id, pos, size)
|
||||
{
|
||||
conn = _conn;
|
||||
thread = NULL;
|
||||
|
||||
SetTable(new sqlResultTable(), true);
|
||||
|
||||
EnableEditing(false);
|
||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||
|
||||
Connect(wxID_ANY, wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler(ctlSQLResult::OnGridSelect));
|
||||
}
|
||||
|
||||
|
||||
|
||||
ctlSQLResult::~ctlSQLResult()
|
||||
{
|
||||
Abort();
|
||||
|
||||
if (thread)
|
||||
{
|
||||
if (thread->IsRunning())
|
||||
{
|
||||
thread->CancelExecution();
|
||||
thread->Wait();
|
||||
}
|
||||
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ctlSQLResult::SetConnection(pgConn *_conn)
|
||||
{
|
||||
conn = _conn;
|
||||
}
|
||||
|
||||
|
||||
bool ctlSQLResult::Export()
|
||||
{
|
||||
if (NumRows() > 0)
|
||||
{
|
||||
frmExport dlg(this);
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
return dlg.Export(NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ctlSQLResult::ToFile()
|
||||
{
|
||||
if (NumRows() > 0)
|
||||
{
|
||||
frmExport dlg(this);
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
return dlg.Export(thread->DataSet());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ctlSQLResult::ToFile(frmExport *frm)
|
||||
{
|
||||
if (NumRows() > 0)
|
||||
{
|
||||
return frm->Export(thread->DataSet());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ctlSQLResult::IsColText(int col)
|
||||
{
|
||||
switch (colTypClasses.Item(col))
|
||||
{
|
||||
case PGTYPCLASS_NUMERIC:
|
||||
case PGTYPCLASS_BOOL:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ctlSQLResult::Execute(const wxString &query, int resultToRetrieve, wxWindow *caller, long eventId, void *data)
|
||||
{
|
||||
wxGridTableMessage *msg;
|
||||
sqlResultTable *table = (sqlResultTable *)GetTable();
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, GetNumberRows());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_DELETED, 0, GetNumberCols());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
|
||||
Abort();
|
||||
|
||||
colNames.Empty();
|
||||
colTypes.Empty();
|
||||
colTypClasses.Empty();
|
||||
|
||||
thread = new pgQueryThread(conn, query, resultToRetrieve, caller, eventId, data);
|
||||
|
||||
if (thread->Create() != wxTHREAD_NO_ERROR)
|
||||
{
|
||||
Abort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
((sqlResultTable *)GetTable())->SetThread(thread);
|
||||
sqlquerytext= wxString(query);
|
||||
thread->Run();
|
||||
return RunStatus();
|
||||
}
|
||||
|
||||
|
||||
int ctlSQLResult::Abort()
|
||||
{
|
||||
if (thread)
|
||||
{
|
||||
((sqlResultTable *)GetTable())->SetThread(0);
|
||||
|
||||
if (thread->IsRunning())
|
||||
{
|
||||
thread->CancelExecution();
|
||||
thread->Wait();
|
||||
}
|
||||
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ctlSQLResult::DisplayData(bool single)
|
||||
{
|
||||
if (!thread || !thread->DataValid())
|
||||
return;
|
||||
|
||||
if (thread->ReturnCode() != PGRES_TUPLES_OK)
|
||||
return;
|
||||
|
||||
rowcountSuppressed = single;
|
||||
Freeze();
|
||||
|
||||
/*
|
||||
* Resize and repopulate by informing it to delete all the rows and
|
||||
* columns, then append the correct number of them. Probably is a
|
||||
* better way to do this.
|
||||
*/
|
||||
wxGridTableMessage *msg;
|
||||
sqlResultTable *table = (sqlResultTable *)GetTable();
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, GetNumberRows());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_DELETED, 0, GetNumberCols());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, NumRows());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
msg = new wxGridTableMessage(table, wxGRIDTABLE_NOTIFY_COLS_APPENDED, thread->DataSet()->NumCols());
|
||||
ProcessTableMessage(*msg);
|
||||
delete msg;
|
||||
if (NumRows()<1000) {
|
||||
wxGridCellAttr* pAttr = new wxGridCellAttr;
|
||||
pAttr->SetBackgroundColour(wxColour(224,255,224));
|
||||
for(int row = 0; row < NumRows(); ++row) {
|
||||
if (row%2==0) SetRowAttr(row,pAttr);
|
||||
}
|
||||
}
|
||||
if (single)
|
||||
{
|
||||
colNames.Add(thread->DataSet()->ColName(0));
|
||||
colTypes.Add(wxT(""));
|
||||
colTypClasses.Add(0L);
|
||||
|
||||
AutoSizeColumn(0, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
long col, nCols = thread->DataSet()->NumCols();
|
||||
|
||||
AutoSizeColumns(false);
|
||||
|
||||
for (col = 0 ; col < nCols ; col++)
|
||||
{
|
||||
colNames.Add(thread->DataSet()->ColName(col));
|
||||
colTypes.Add(thread->DataSet()->ColFullType(col));
|
||||
colTypClasses.Add(thread->DataSet()->ColTypClass(col));
|
||||
|
||||
if (thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC)
|
||||
{
|
||||
/*
|
||||
* For numeric columns, set alignment to right.
|
||||
*/
|
||||
wxGridCellAttr *attr = new wxGridCellAttr();
|
||||
attr->SetAlignment(wxALIGN_RIGHT, wxALIGN_TOP);
|
||||
SetColAttr(col, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Thaw();
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxString ctlSQLResult::GetMessagesAndClear()
|
||||
{
|
||||
if (thread)
|
||||
return thread->GetMessagesAndClear();
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
|
||||
wxString ctlSQLResult::GetErrorMessage()
|
||||
{
|
||||
return conn->GetLastError();
|
||||
}
|
||||
|
||||
pgError ctlSQLResult::GetResultError()
|
||||
{
|
||||
if (thread)
|
||||
return thread->GetResultError();
|
||||
|
||||
pgError dummy;
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
return dummy;
|
||||
}
|
||||
|
||||
long ctlSQLResult::NumRows() const
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
return thread->DataSet()->NumRows();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long ctlSQLResult::InsertedCount() const
|
||||
{
|
||||
if (thread)
|
||||
return thread->RowsInserted();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
OID ctlSQLResult::InsertedOid() const
|
||||
{
|
||||
if (thread)
|
||||
return thread->InsertedOid();
|
||||
return (OID) - 1;
|
||||
}
|
||||
|
||||
|
||||
int ctlSQLResult::RunStatus()
|
||||
{
|
||||
if (!thread)
|
||||
return -1;
|
||||
|
||||
if (thread->IsRunning())
|
||||
return CTLSQL_RUNNING;
|
||||
|
||||
return thread->ReturnCode();
|
||||
}
|
||||
|
||||
|
||||
wxString ctlSQLResult::OnGetItemText(long item, long col) const
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
{
|
||||
if (!rowcountSuppressed)
|
||||
{
|
||||
if (col)
|
||||
col--;
|
||||
else
|
||||
return NumToStr(item + 1L);
|
||||
}
|
||||
if (item >= 0)
|
||||
{
|
||||
thread->DataSet()->Locate(item + 1);
|
||||
return thread->DataSet()->GetVal(col);
|
||||
}
|
||||
else
|
||||
return thread->DataSet()->ColName(col);
|
||||
}
|
||||
return wxEmptyString;
|
||||
}
|
||||
wxString ctlSQLResult::SummaryColumn()
|
||||
{
|
||||
//ce=cells.Item(0);
|
||||
double sum=0;
|
||||
size_t i;
|
||||
if (GetSelectedRows().GetCount())
|
||||
{
|
||||
//AppendColumnHeader(str, 0, (GetNumberCols() - 1));
|
||||
|
||||
wxArrayInt rows = GetSelectedRows();
|
||||
|
||||
for (i = 0 ; i < rows.GetCount() ; i++)
|
||||
{
|
||||
//str.Append(GetExportLine(rows.Item(i)));
|
||||
//if (rows.GetCount() > 1)
|
||||
// str.Append(END_OF_LINE);
|
||||
}
|
||||
|
||||
//copied = rows.GetCount();
|
||||
}
|
||||
else if (GetSelectedCols().GetCount())
|
||||
{
|
||||
wxArrayInt cols = GetSelectedCols();
|
||||
size_t numRows = GetNumberRows();
|
||||
|
||||
//AppendColumnHeader(str, cols);
|
||||
for (i = 0 ; i < numRows ; i++)
|
||||
{
|
||||
//str.Append(GetExportLine(i, cols));
|
||||
for (size_t col = 0 ; col < cols.Count() ; col++)
|
||||
{
|
||||
wxString text = GetCellValue(i, cols[col]);
|
||||
sum=sum+wxAtof(text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//copied = numRows;
|
||||
}
|
||||
else if (GetSelectionBlockTopLeft().GetCount() > 0 &&
|
||||
GetSelectionBlockBottomRight().GetCount() > 0)
|
||||
{
|
||||
unsigned int x1, x2, y1, y2;
|
||||
|
||||
x1 = GetSelectionBlockTopLeft()[0].GetCol();
|
||||
x2 = GetSelectionBlockBottomRight()[0].GetCol();
|
||||
y1 = GetSelectionBlockTopLeft()[0].GetRow();
|
||||
y2 = GetSelectionBlockBottomRight()[0].GetRow();
|
||||
|
||||
//AppendColumnHeader(str, x1, x2);
|
||||
|
||||
for (i = y1; i <= y2; i++)
|
||||
{
|
||||
// str.Append(GetExportLine(i, x1, x2));
|
||||
wxString text = GetCellValue(i, x1);
|
||||
sum=sum+wxAtof(text);
|
||||
|
||||
//copied = y2 - y1 + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int row, col;
|
||||
|
||||
row = GetGridCursorRow();
|
||||
col = GetGridCursorCol();
|
||||
|
||||
//AppendColumnHeader(str, col, col);
|
||||
|
||||
// str.Append(GetExportLine(row, col, col));
|
||||
//copied = 1;
|
||||
}
|
||||
|
||||
|
||||
wxString result;
|
||||
result.Printf(wxT("%f"), sum);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ctlSQLResult::OnGridSelect(wxGridRangeSelectEvent &event)
|
||||
{
|
||||
SetFocus();
|
||||
}
|
||||
|
||||
wxString sqlResultTable::GetValue(int row, int col)
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
{
|
||||
if (col >= 0)
|
||||
{
|
||||
thread->DataSet()->Locate(row + 1);
|
||||
if (settings->GetIndicateNull() && thread->DataSet()->IsNull(col))
|
||||
return wxT("<NULL>");
|
||||
else
|
||||
{
|
||||
|
||||
wxString decimalMark = wxT(".");
|
||||
wxString s = thread->DataSet()->GetVal(col);
|
||||
|
||||
if(thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC &&
|
||||
settings->GetDecimalMark().Length() > 0)
|
||||
{
|
||||
decimalMark = settings->GetDecimalMark();
|
||||
s.Replace(wxT("."), decimalMark);
|
||||
|
||||
}
|
||||
if (thread->DataSet()->ColTypClass(col) == PGTYPCLASS_NUMERIC &&
|
||||
settings->GetThousandsSeparator().Length() > 0)
|
||||
{
|
||||
/* Add thousands separator */
|
||||
size_t pos = s.find(decimalMark);
|
||||
if (pos == wxString::npos)
|
||||
pos = s.length();
|
||||
while (pos > 3)
|
||||
{
|
||||
pos -= 3;
|
||||
if (pos > 1 || !s.StartsWith(wxT("-")))
|
||||
s.insert(pos, settings->GetThousandsSeparator());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString data = thread->DataSet()->GetVal(col);
|
||||
|
||||
if (data.Length() > (size_t)settings->GetMaxColSize())
|
||||
return thread->DataSet()->GetVal(col).Left(settings->GetMaxColSize()) + wxT(" (...)");
|
||||
else
|
||||
return thread->DataSet()->GetVal(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return thread->DataSet()->ColName(col);
|
||||
}
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
sqlResultTable::sqlResultTable()
|
||||
{
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
int sqlResultTable::GetNumberRows()
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
return thread->DataSet()->NumRows();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
wxString sqlResultTable::GetColLabelValue(int col)
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
return thread->DataSet()->ColName(col) + wxT("\n") +
|
||||
thread->DataSet()->ColFullType(col);
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
int sqlResultTable::GetNumberCols()
|
||||
{
|
||||
if (thread && thread->DataValid())
|
||||
return thread->DataSet()->NumCols();
|
||||
return 0;
|
||||
}
|
||||
|
||||
291
ctl/ctlSeclabelPanel.cpp
Normal file
291
ctl/ctlSeclabelPanel.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlSeclabelPanel.cpp - Panel with security label information
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/misc.h"
|
||||
#include "utils/sysLogger.h"
|
||||
#include "ctl/ctlSeclabelPanel.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "utils/pgDefs.h"
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlSeclabelPanel, wxPanel)
|
||||
EVT_LIST_ITEM_SELECTED(CTL_LBSECLABEL, ctlSeclabelPanel::OnSeclabelSelChange)
|
||||
EVT_BUTTON(CTL_ADDSECLABEL, ctlSeclabelPanel::OnAddSeclabel)
|
||||
EVT_BUTTON(CTL_DELSECLABEL, ctlSeclabelPanel::OnDelSeclabel)
|
||||
EVT_TEXT(CTL_PROVIDER, ctlSeclabelPanel::OnChange)
|
||||
EVT_TEXT(CTL_SECLABEL, ctlSeclabelPanel::OnChange)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
DEFINE_LOCAL_EVENT_TYPE(EVT_SECLABELPANEL_CHANGE)
|
||||
|
||||
|
||||
ctlSeclabelPanel::ctlSeclabelPanel(wxNotebook *nb)
|
||||
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
wxStaticText *label;
|
||||
|
||||
nbNotebook = nb;
|
||||
nbNotebook->AddPage(this, _("Security Labels"));
|
||||
|
||||
connection = NULL;
|
||||
|
||||
// root sizer
|
||||
wxFlexGridSizer *sizer0 = new wxFlexGridSizer(4, 1, 5, 5);
|
||||
sizer0->AddGrowableCol(0);
|
||||
sizer0->AddGrowableRow(0);
|
||||
|
||||
// grid sizer
|
||||
wxFlexGridSizer *sizer1 = new wxFlexGridSizer(1, 1, 5, 5);
|
||||
sizer1->AddGrowableCol(0);
|
||||
sizer1->AddGrowableRow(0);
|
||||
lbSeclabels = new ctlListView(this, CTL_LBSECLABEL, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
|
||||
lbSeclabels->AddColumn(_("Provider"), 70, wxLIST_FORMAT_LEFT);
|
||||
lbSeclabels->AddColumn(_("Security label"), 70, wxLIST_FORMAT_LEFT);
|
||||
sizer1->Add(lbSeclabels, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
sizer0->Add(sizer1, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// buttons sizer
|
||||
wxBoxSizer *sizer2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnAddSeclabel = new wxButton(this, CTL_ADDSECLABEL, _("Add/Change"));
|
||||
sizer2->Add(btnAddSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
btnDelSeclabel = new wxButton(this, CTL_DELSECLABEL, _("Remove"));
|
||||
sizer2->Add(btnDelSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
sizer0->Add(sizer2, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
|
||||
// textboxes sizer
|
||||
wxFlexGridSizer *sizer3 = new wxFlexGridSizer(2, 2, 5, 5);
|
||||
sizer3->AddGrowableCol(1);
|
||||
label = new wxStaticText(this, 0, _("Provider"));
|
||||
sizer3->Add(label, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
txtProvider = new wxTextCtrl(this, CTL_PROVIDER);
|
||||
sizer3->Add(txtProvider, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
label = new wxStaticText(this, 0, _("Security label"));
|
||||
sizer3->Add(label, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
txtSeclabel = new wxTextCtrl(this, CTL_SECLABEL);
|
||||
sizer3->Add(txtSeclabel, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
sizer0->Add(sizer3, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
// compute sizes
|
||||
this->SetSizer(sizer0);
|
||||
sizer0->Fit(this);
|
||||
|
||||
btnAddSeclabel->Enable(false);
|
||||
}
|
||||
|
||||
|
||||
ctlSeclabelPanel::~ctlSeclabelPanel()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ctlSeclabelPanel::Disable()
|
||||
{
|
||||
lbSeclabels->Disable();
|
||||
btnAddSeclabel->Disable();
|
||||
btnDelSeclabel->Disable();
|
||||
txtProvider->Disable();
|
||||
txtSeclabel->Disable();
|
||||
}
|
||||
|
||||
|
||||
void ctlSeclabelPanel::SetConnection(pgConn *conn)
|
||||
{
|
||||
connection = conn;
|
||||
}
|
||||
|
||||
|
||||
void ctlSeclabelPanel::SetObject(pgObject *obj)
|
||||
{
|
||||
object = obj;
|
||||
|
||||
if (object && !object->GetLabels().IsEmpty())
|
||||
{
|
||||
wxArrayString seclabels = object->GetProviderLabelArray();
|
||||
if (seclabels.GetCount() > 0)
|
||||
{
|
||||
for (unsigned int index = 0 ; index < seclabels.GetCount() - 1 ; index += 2)
|
||||
{
|
||||
lbSeclabels->AppendItem(seclabels.Item(index),
|
||||
seclabels.Item(index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ctlSeclabelPanel::OnDelSeclabel(wxCommandEvent &ev)
|
||||
{
|
||||
if (lbSeclabels->GetFirstSelected() == -1)
|
||||
return;
|
||||
|
||||
lbSeclabels->DeleteCurrentItem();
|
||||
|
||||
wxCommandEvent event( EVT_SECLABELPANEL_CHANGE, GetId() );
|
||||
event.SetEventObject( this );
|
||||
GetEventHandler()->ProcessEvent( event );
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlSeclabelPanel::OnAddSeclabel(wxCommandEvent &ev)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
for (int indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
|
||||
{
|
||||
if (lbSeclabels->GetText(indexList) == txtProvider->GetValue())
|
||||
{
|
||||
found = true;
|
||||
lbSeclabels->SetItem(indexList, 1, txtSeclabel->GetValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
int pos = lbSeclabels->GetItemCount();
|
||||
lbSeclabels->InsertItem(pos, txtProvider->GetValue());
|
||||
lbSeclabels->SetItem(pos, 1, txtSeclabel->GetValue());
|
||||
}
|
||||
|
||||
wxCommandEvent event( EVT_SECLABELPANEL_CHANGE, GetId() );
|
||||
event.SetEventObject( this );
|
||||
GetEventHandler()->ProcessEvent( event );
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
void ctlSeclabelPanel::OnChange(wxCommandEvent &event)
|
||||
{
|
||||
wxString provider = txtProvider->GetValue().Trim(true).Trim(false);
|
||||
wxString label = txtSeclabel->GetValue().Trim(true).Trim(false);
|
||||
btnAddSeclabel->Enable(!provider.IsEmpty() && !label.IsEmpty());
|
||||
}
|
||||
|
||||
void ctlSeclabelPanel::OnSeclabelSelChange(wxListEvent &ev)
|
||||
{
|
||||
if (lbSeclabels->GetFirstSelected() == -1)
|
||||
return;
|
||||
|
||||
txtProvider->SetValue(lbSeclabels->GetText(lbSeclabels->GetSelection()));
|
||||
txtSeclabel->SetValue(lbSeclabels->GetText(lbSeclabels->GetSelection(), 1));
|
||||
}
|
||||
|
||||
void ctlSeclabelPanel::GetCurrentProviderLabelArray(wxArrayString &secLabels)
|
||||
{
|
||||
for(int indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
|
||||
{
|
||||
secLabels.Add(lbSeclabels->GetText(indexList));
|
||||
secLabels.Add(lbSeclabels->GetText(indexList, 1));
|
||||
}
|
||||
}
|
||||
|
||||
wxString ctlSeclabelPanel::GetSqlForSecLabels(wxString objecttype, wxString objectname)
|
||||
{
|
||||
wxASSERT(connection != NULL);
|
||||
|
||||
wxString sql;
|
||||
wxArrayString seclabels;
|
||||
int indexList;
|
||||
unsigned int indexArray;
|
||||
wxString oldprovider, newprovider, oldlabel, newlabel;
|
||||
bool found;
|
||||
|
||||
if (object)
|
||||
{
|
||||
seclabels = object->GetProviderLabelArray();
|
||||
|
||||
// find new or changed seclabels
|
||||
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
|
||||
{
|
||||
found = false;
|
||||
newprovider = lbSeclabels->GetText(indexList);
|
||||
newlabel = lbSeclabels->GetText(indexList, 1);
|
||||
|
||||
// try to find the provider in the current providers list
|
||||
if (object && seclabels.GetCount() > 0)
|
||||
{
|
||||
for (indexArray = 0 ; indexArray < seclabels.GetCount() - 1 ; indexArray += 2)
|
||||
{
|
||||
oldprovider = seclabels.Item(indexArray);
|
||||
oldlabel = seclabels.Item(indexArray + 1);
|
||||
|
||||
if (oldprovider == newprovider)
|
||||
{
|
||||
found = true;
|
||||
// provider is available on the old and new list
|
||||
// we should check is the label has changed
|
||||
if (oldlabel != newlabel)
|
||||
sql += wxT("SECURITY LABEL FOR ") + newprovider
|
||||
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
|
||||
+ wxT("\n IS ") + object->qtDbString(newlabel) + wxT(";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
sql += wxT("SECURITY LABEL FOR ") + newprovider
|
||||
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
|
||||
+ wxT("\n IS ") + object->qtDbString(newlabel) + wxT(";\n");
|
||||
}
|
||||
|
||||
// find old seclabels
|
||||
if (seclabels.GetCount() > 0)
|
||||
{
|
||||
for (indexArray = 0 ; indexArray < seclabels.GetCount() - 1 ; indexArray += 2)
|
||||
{
|
||||
found = false;
|
||||
oldprovider = seclabels.Item(indexArray);
|
||||
|
||||
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
|
||||
{
|
||||
newprovider = lbSeclabels->GetText(indexList);
|
||||
|
||||
if (oldprovider == newprovider)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
sql += wxT("SECURITY LABEL FOR ") + oldprovider
|
||||
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
|
||||
+ wxT("\n IS NULL;\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// find new or changed seclabels
|
||||
for (indexList = 0; indexList < lbSeclabels->GetItemCount(); indexList++)
|
||||
{
|
||||
newprovider = lbSeclabels->GetText(indexList);
|
||||
newlabel = lbSeclabels->GetText(indexList, 1);
|
||||
|
||||
sql += wxT("SECURITY LABEL FOR ") + newprovider
|
||||
+ wxT("\n ON ") + objecttype + wxT(" ") + objectname
|
||||
+ wxT("\n IS ") + connection->qtDbString(newlabel) + wxT(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
445
ctl/ctlSecurityPanel.cpp
Normal file
445
ctl/ctlSecurityPanel.cpp
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlSecurityPanel.cpp - Panel with security information
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/sysLogger.h"
|
||||
#include "ctl/ctlSecurityPanel.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgGroup.h"
|
||||
#include "schema/pgUser.h"
|
||||
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlSecurityPanel, wxPanel)
|
||||
EVT_LIST_ITEM_SELECTED(CTL_LBPRIV, ctlSecurityPanel::OnPrivSelChange)
|
||||
EVT_BUTTON(CTL_ADDPRIV, ctlSecurityPanel::OnAddPriv)
|
||||
EVT_BUTTON(CTL_DELPRIV, ctlSecurityPanel::OnDelPriv)
|
||||
EVT_TEXT(CTL_CBGROUP, ctlSecurityPanel::OnGroupChange)
|
||||
EVT_COMBOBOX(CTL_CBGROUP, ctlSecurityPanel::OnGroupChange)
|
||||
EVT_CHECKBOX(CTL_ALLPRIV, ctlSecurityPanel::OnPrivCheckAll)
|
||||
EVT_CHECKBOX(CTL_ALLPRIVGRANT, ctlSecurityPanel::OnPrivCheckAllGrant)
|
||||
EVT_CHECKBOX(CTL_PRIVCB, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 2, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 4, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 6, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 8, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 10, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 12, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 14, ctlSecurityPanel::OnPrivCheck)
|
||||
EVT_CHECKBOX(CTL_PRIVCB + 16, ctlSecurityPanel::OnPrivCheck)
|
||||
END_EVENT_TABLE();
|
||||
|
||||
DEFINE_LOCAL_EVENT_TYPE(EVT_SECURITYPANEL_CHANGE)
|
||||
|
||||
ctlSecurityPanel::ctlSecurityPanel(wxNotebook *nb, const wxString &privList, const char *privChars, wxImageList *imgList)
|
||||
: wxPanel(nb, -1, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
nbNotebook = nb;
|
||||
nbNotebook->AddPage(this, _("Privileges"));
|
||||
|
||||
connection = 0;
|
||||
privilegeChars = privChars;
|
||||
allPrivileges = 0;
|
||||
|
||||
privCheckboxes = 0;
|
||||
|
||||
wxStringTokenizer privileges(privList, wxT(","));
|
||||
privilegeCount = privileges.CountTokens();
|
||||
|
||||
wxFlexGridSizer *item0 = new wxFlexGridSizer(3, 1, 5, 5);
|
||||
item0->AddGrowableCol(0);
|
||||
item0->AddGrowableRow(0);
|
||||
|
||||
if (privilegeCount)
|
||||
{
|
||||
bool needAll = (privilegeCount > 1);
|
||||
privCheckboxes = new wxCheckBox*[privilegeCount * 2];
|
||||
int i = 0;
|
||||
|
||||
wxFlexGridSizer *itemSizer1 = new wxFlexGridSizer(1, 1, 5, 5);
|
||||
itemSizer1->AddGrowableCol(0);
|
||||
itemSizer1->AddGrowableRow(0);
|
||||
lbPrivileges = new ctlListView(this, CTL_LBPRIV, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxLC_REPORT);
|
||||
lbPrivileges->SetImageList(imgList, wxIMAGE_LIST_SMALL);
|
||||
lbPrivileges->AddColumn(_("User/Group"), 70, wxLIST_FORMAT_LEFT);
|
||||
lbPrivileges->AddColumn(_("Privileges"), 70, wxLIST_FORMAT_LEFT);
|
||||
itemSizer1->Add(lbPrivileges, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
item0->Add(itemSizer1, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxBoxSizer *itemSizer2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
btnAddPriv = new wxButton(this, CTL_ADDPRIV, _("Add/Change"));
|
||||
itemSizer2->Add(btnAddPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
btnDelPriv = new wxButton(this, CTL_DELPRIV, _("Remove"));
|
||||
itemSizer2->Add(btnDelPriv, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
item0->Add(itemSizer2, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
wxStaticBox *sb = new wxStaticBox(this, -1, _("Privileges"));
|
||||
wxBoxSizer *itemSizer3 = new wxStaticBoxSizer( sb, wxVERTICAL );
|
||||
item0->Add(itemSizer3, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
wxBoxSizer *itemSizer4 = new wxBoxSizer(wxHORIZONTAL);
|
||||
stGroup = new wxStaticText(this, CTL_STATICGROUP, _("Group"));
|
||||
#ifdef __WXMSW__
|
||||
stGroup->SetMinSize(wxSize(30, 15));
|
||||
#endif // __WXMSW__
|
||||
itemSizer4->Add(stGroup, 0, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT, 4);
|
||||
cbGroups = new ctlComboBox(this, CTL_CBGROUP, wxDefaultPosition, wxDefaultSize);
|
||||
cbGroups->Append(wxT("public"));
|
||||
cbGroups->SetSelection(0);
|
||||
itemSizer4->Add(cbGroups, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
itemSizer3->Add(itemSizer4, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
/* border size depends on the plateform */
|
||||
#ifdef __WXMSW__
|
||||
int bordersize = 4;
|
||||
#endif
|
||||
#ifdef __WXMAC__
|
||||
int bordersize = 3;
|
||||
#endif
|
||||
#ifdef __WXGTK__
|
||||
int bordersize = 0;
|
||||
#endif
|
||||
|
||||
if (needAll)
|
||||
{
|
||||
wxBoxSizer *itemSizer5 = new wxBoxSizer(wxHORIZONTAL);
|
||||
allPrivileges = new wxCheckBox(this, CTL_ALLPRIV, wxT("ALL"));
|
||||
itemSizer5->Add(allPrivileges, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
allPrivilegesGrant = new wxCheckBox(this, CTL_ALLPRIVGRANT, wxT("WITH GRANT OPTION"));
|
||||
itemSizer5->Add(allPrivilegesGrant, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
allPrivilegesGrant->Disable();
|
||||
itemSizer3->Add(itemSizer5, 0, wxALL, bordersize);
|
||||
}
|
||||
|
||||
while (privileges.HasMoreTokens())
|
||||
{
|
||||
wxString priv = privileges.GetNextToken();
|
||||
wxCheckBox *cb;
|
||||
wxBoxSizer *itemSizer6 = new wxBoxSizer(wxHORIZONTAL);
|
||||
cb = new wxCheckBox(this, CTL_PRIVCB + i, priv);
|
||||
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
privCheckboxes[i++] = cb;
|
||||
cb = new wxCheckBox(this, CTL_PRIVCB + i, wxT("WITH GRANT OPTION"));
|
||||
itemSizer6->Add(cb, wxEXPAND | wxALIGN_CENTRE_VERTICAL | wxTOP | wxLEFT | wxRIGHT);
|
||||
cb->Disable();
|
||||
privCheckboxes[i++] = cb;
|
||||
itemSizer3->Add(itemSizer6, 0, wxALL, bordersize);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetSizer(item0);
|
||||
item0->Fit(this);
|
||||
}
|
||||
|
||||
|
||||
ctlSecurityPanel::~ctlSecurityPanel()
|
||||
{
|
||||
if (privCheckboxes)
|
||||
delete[] privCheckboxes;
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::SetConnection(pgConn *conn)
|
||||
{
|
||||
connection = conn;
|
||||
if (connection && stGroup && connection->BackendMinimumVersion(8, 1))
|
||||
stGroup->SetLabel(_("Role"));
|
||||
}
|
||||
|
||||
wxString ctlSecurityPanel::GetUserPrivileges()
|
||||
{
|
||||
wxString strPrivilages = wxEmptyString;
|
||||
int cnt = lbPrivileges->GetItemCount();
|
||||
int pos;
|
||||
if(cnt > 0)
|
||||
{
|
||||
strPrivilages += wxT("{");
|
||||
for (pos = 0 ; pos < cnt ; pos++)
|
||||
{
|
||||
if (pos != 0)
|
||||
strPrivilages += wxT(",");
|
||||
|
||||
strPrivilages +=
|
||||
lbPrivileges->GetText(pos) + wxT("=") + lbPrivileges->GetText(pos, 1) + wxT("/") + connection->GetUser();
|
||||
}
|
||||
strPrivilages += wxT("}");
|
||||
}
|
||||
return strPrivilages;
|
||||
}
|
||||
|
||||
wxString ctlSecurityPanel::GetGrant(const wxString &allPattern, const wxString &grantObject, wxArrayString *currentAcl, wxString column)
|
||||
{
|
||||
wxArrayString tmpAcl;
|
||||
if (currentAcl)
|
||||
tmpAcl = *currentAcl;
|
||||
|
||||
wxString sql;
|
||||
int cnt = lbPrivileges->GetItemCount();
|
||||
int pos;
|
||||
unsigned int i;
|
||||
|
||||
for (pos = 0 ; pos < cnt ; pos++)
|
||||
{
|
||||
wxString name = lbPrivileges->GetText(pos);
|
||||
wxString value = lbPrivileges->GetText(pos, 1);
|
||||
|
||||
int nameLen = name.Length();
|
||||
|
||||
bool privWasAssigned = false;
|
||||
bool privPartiallyAssigned = false;
|
||||
for (i = 0 ; i < tmpAcl.GetCount() ; i++)
|
||||
{
|
||||
if (tmpAcl.Item(i).Left(nameLen + 1) == name + wxT("="))
|
||||
{
|
||||
privPartiallyAssigned = true;
|
||||
if (tmpAcl.Item(i).Mid(nameLen + 1) == value)
|
||||
privWasAssigned = true;
|
||||
tmpAcl.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name.Left(6).IsSameAs(wxT("group "), false))
|
||||
name = wxT("GROUP ") + qtIdent(name.Mid(6));
|
||||
else
|
||||
name = qtIdent(name);
|
||||
|
||||
if (!privWasAssigned)
|
||||
{
|
||||
if (privPartiallyAssigned)
|
||||
sql += pgObject::GetPrivileges(allPattern, wxT(""), grantObject, name, column);
|
||||
sql += pgObject::GetPrivileges(allPattern, value, grantObject, name, column);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0 ; i < tmpAcl.GetCount() ; i++)
|
||||
{
|
||||
wxString name = tmpAcl.Item(i).BeforeLast('=');
|
||||
|
||||
if (name.Left(6).IsSameAs(wxT("group "), false))
|
||||
name = wxT("GROUP ") + qtIdent(name.Mid(6));
|
||||
else
|
||||
name = qtIdent(name);
|
||||
sql += pgObject::GetPrivileges(allPattern, wxT(""), grantObject, name, column);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnGroupChange(wxCommandEvent &ev)
|
||||
{
|
||||
cbGroups->GuessSelection(ev);
|
||||
wxString name = cbGroups->GetGuessedStringSelection();
|
||||
|
||||
btnAddPriv->Enable(!name.Strip(wxString::both).IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnPrivCheckAll(wxCommandEvent &ev)
|
||||
{
|
||||
bool all = allPrivileges->GetValue();
|
||||
int i;
|
||||
for (i = 0 ; i < privilegeCount ; i++)
|
||||
{
|
||||
if (all)
|
||||
{
|
||||
// We use the Name property of the checkboxes as a flag to note if that
|
||||
// box should remain disabled (eg. CONNECT for a DB on PG < 8.2
|
||||
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
|
||||
{
|
||||
privCheckboxes[i * 2]->SetValue(true);
|
||||
privCheckboxes[i * 2]->Disable();
|
||||
privCheckboxes[i * 2 + 1]->Disable();
|
||||
allPrivilegesGrant->Enable(GrantAllowed());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
|
||||
{
|
||||
allPrivilegesGrant->Disable();
|
||||
allPrivilegesGrant->SetValue(false);
|
||||
privCheckboxes[i * 2]->Enable();
|
||||
CheckGrantOpt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnPrivCheckAllGrant(wxCommandEvent &ev)
|
||||
{
|
||||
bool grant = allPrivilegesGrant->GetValue();
|
||||
int i;
|
||||
for (i = 0 ; i < privilegeCount ; i++)
|
||||
privCheckboxes[i * 2 + 1]->SetValue(grant);
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnPrivCheck(wxCommandEvent &ev)
|
||||
{
|
||||
int id = (ev.GetId() - CTL_PRIVCB) / 2;
|
||||
CheckGrantOpt(id);
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::CheckGrantOpt(int id)
|
||||
{
|
||||
bool canGrant = (GrantAllowed() && privCheckboxes[id * 2]->GetValue());
|
||||
if (canGrant)
|
||||
privCheckboxes[id * 2 + 1]->Enable();
|
||||
else
|
||||
{
|
||||
privCheckboxes[id * 2 + 1]->SetValue(false);
|
||||
privCheckboxes[id * 2 + 1]->Disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnDelPriv(wxCommandEvent &ev)
|
||||
{
|
||||
if (lbPrivileges->GetFirstSelected() == -1)
|
||||
return;
|
||||
|
||||
lbPrivileges->DeleteCurrentItem();
|
||||
|
||||
wxCommandEvent event( EVT_SECURITYPANEL_CHANGE, GetId() );
|
||||
event.SetEventObject( this );
|
||||
GetEventHandler()->ProcessEvent( event );
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnAddPriv(wxCommandEvent &ev)
|
||||
{
|
||||
wxString name = cbGroups->GetGuessedStringSelection();
|
||||
|
||||
long pos = lbPrivileges->FindItem(-1, name);
|
||||
if (pos < 0)
|
||||
{
|
||||
pos = lbPrivileges->GetItemCount();
|
||||
int icon = userFactory.GetIconId();
|
||||
|
||||
if (name.Left(6).IsSameAs(wxT("group "), false))
|
||||
icon = groupFactory.GetIconId();
|
||||
else if (name.IsSameAs(wxT("public"), false))
|
||||
icon = PGICON_PUBLIC;
|
||||
|
||||
lbPrivileges->InsertItem(pos, name, icon);
|
||||
}
|
||||
wxString value;
|
||||
int i;
|
||||
for (i = 0 ; i < privilegeCount ; i++)
|
||||
{
|
||||
if (privCheckboxes[i * 2]->GetValue())
|
||||
{
|
||||
value += privilegeChars[i];
|
||||
if (privCheckboxes[i * 2 + 1]->GetValue())
|
||||
value += '*';
|
||||
}
|
||||
}
|
||||
lbPrivileges->SetItem(pos, 1, value);
|
||||
|
||||
wxCommandEvent event( EVT_SECURITYPANEL_CHANGE, GetId() );
|
||||
event.SetEventObject( this );
|
||||
GetEventHandler()->ProcessEvent( event );
|
||||
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void ctlSecurityPanel::OnPrivSelChange(wxListEvent &ev)
|
||||
{
|
||||
if (!cbGroups)
|
||||
return;
|
||||
if (allPrivileges)
|
||||
{
|
||||
allPrivileges->SetValue(false);
|
||||
allPrivilegesGrant->SetValue(false);
|
||||
allPrivilegesGrant->Disable();
|
||||
}
|
||||
long pos = lbPrivileges->GetSelection();
|
||||
if (pos >= 0)
|
||||
{
|
||||
wxString name = lbPrivileges->GetText(pos);
|
||||
wxString value = lbPrivileges->GetText(pos, 1);
|
||||
|
||||
pos = cbGroups->FindString(name);
|
||||
if (pos < 0)
|
||||
{
|
||||
cbGroups->Append(name);
|
||||
pos = cbGroups->GetCount() - 1;
|
||||
}
|
||||
cbGroups->SetSelection(pos);
|
||||
|
||||
int i;
|
||||
for (i = 0 ; i < privilegeCount ; i++)
|
||||
{
|
||||
if (privCheckboxes[i * 2]->GetName() != wxT("DISABLE"))
|
||||
{
|
||||
privCheckboxes[i * 2]->Enable();
|
||||
int index = value.Find(privilegeChars[i]);
|
||||
if (index >= 0)
|
||||
{
|
||||
privCheckboxes[i * 2]->SetValue(true);
|
||||
privCheckboxes[i * 2 + 1]->SetValue(value.Mid(index + 1, 1) == wxT("*"));
|
||||
}
|
||||
else
|
||||
privCheckboxes[i * 2]->SetValue(false);
|
||||
}
|
||||
CheckGrantOpt(i);
|
||||
}
|
||||
}
|
||||
btnAddPriv->Enable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ctlSecurityPanel::GrantAllowed() const
|
||||
{
|
||||
if (!connection->BackendMinimumVersion(7, 4))
|
||||
return false;
|
||||
|
||||
wxString user = cbGroups->GetValue();
|
||||
if (user.IsSameAs(wxT("public"), false))
|
||||
return false;
|
||||
|
||||
if (!connection->BackendMinimumVersion(8, 1) &&
|
||||
user.Left(6).IsSameAs(wxT("group "), false))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ctlSecurityPanel::DisablePrivilege(const wxString &priv)
|
||||
{
|
||||
for (int i = 0; i < privilegeCount; i++)
|
||||
{
|
||||
if (privCheckboxes[i * 2]->GetLabel() == priv)
|
||||
{
|
||||
// Use the Name property as a flag so we don't accidently reenable the control later
|
||||
privCheckboxes[i * 2]->SetName(wxT("DISABLE"));
|
||||
privCheckboxes[i * 2]->Disable();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
398
ctl/ctlTree.cpp
Normal file
398
ctl/ctlTree.cpp
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlTree.cpp - wxTreeCtrl containing pgObjects
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "ctl/ctlTree.h"
|
||||
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgCollection.h"
|
||||
#include "schema/pgServer.h"
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlTree, wxTreeCtrl)
|
||||
EVT_CHAR(ctlTree::OnChar)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
wxTreeItemId ctlTree::FindItem(const wxTreeItemId &idParent, const wxString &prefixOrig)
|
||||
{
|
||||
// match is case insensitive as this is more convenient to the user: having
|
||||
// to press Shift-letter to go to the item starting with a capital letter
|
||||
// would be too bothersome
|
||||
wxString prefix = prefixOrig.Lower();
|
||||
|
||||
// determine the starting point: we shouldn't take the current item (this
|
||||
// allows to switch between two items starting with the same letter just by
|
||||
// pressing it) but we shouldn't jump to the next one if the user is
|
||||
// continuing to type as otherwise he might easily skip the item he wanted
|
||||
wxTreeItemId id = idParent;
|
||||
|
||||
if ( prefix.length() == 1 )
|
||||
{
|
||||
wxCookieType cookie;
|
||||
if (HasChildren(id))
|
||||
id = GetFirstChild(id, cookie);
|
||||
else
|
||||
{
|
||||
// Try a sibling of this or ancestor instead
|
||||
wxTreeItemId p = id;
|
||||
wxTreeItemId toFind;
|
||||
do
|
||||
{
|
||||
toFind = GetNextSibling(p);
|
||||
p = GetItemParent(p);
|
||||
}
|
||||
while (p.IsOk() && !toFind.IsOk());
|
||||
id = toFind;
|
||||
}
|
||||
}
|
||||
|
||||
// look for the item starting with the given prefix after it
|
||||
while ( id.IsOk() &&
|
||||
( ( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
|
||||
!GetItemText(id).Lower().StartsWith(prefix) ))
|
||||
{
|
||||
wxCookieType cookie;
|
||||
if ( HasChildren(id) )
|
||||
id = GetFirstChild(id, cookie);
|
||||
else
|
||||
{
|
||||
// Try a sibling of this or ancestor instead
|
||||
wxTreeItemId p = id;
|
||||
wxTreeItemId toFind;
|
||||
do
|
||||
{
|
||||
toFind = GetNextSibling(p);
|
||||
p = GetItemParent(p);
|
||||
}
|
||||
while (p.IsOk() && !toFind.IsOk());
|
||||
id = toFind;
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't found anything...
|
||||
if ( !id.IsOk() )
|
||||
{
|
||||
// ... wrap to the beginning
|
||||
id = GetRootItem();
|
||||
if ( HasFlag(wxTR_HIDE_ROOT) )
|
||||
{
|
||||
wxCookieType cookie;
|
||||
// can't select virtual root
|
||||
if ( HasChildren(id) )
|
||||
id = GetFirstChild(id, cookie);
|
||||
else
|
||||
{
|
||||
// Try a sibling of this or ancestor instead
|
||||
wxTreeItemId p = id;
|
||||
wxTreeItemId toFind;
|
||||
do
|
||||
{
|
||||
toFind = GetNextSibling(p);
|
||||
p = GetItemParent(p);
|
||||
}
|
||||
while (p.IsOk() && !toFind.IsOk());
|
||||
id = toFind;
|
||||
}
|
||||
}
|
||||
|
||||
// and try all the items (stop when we get to the one we started from)
|
||||
while ( id.IsOk() && id != idParent &&
|
||||
(( GetItemText(id) == wxT("Dummy") && !GetItemData(id) ) ||
|
||||
!GetItemText(id).Lower().StartsWith(prefix) ))
|
||||
{
|
||||
wxCookieType cookie;
|
||||
if ( HasChildren(id) )
|
||||
id = GetFirstChild(id, cookie);
|
||||
else
|
||||
{
|
||||
// Try a sibling of this or ancestor instead
|
||||
wxTreeItemId p = id;
|
||||
wxTreeItemId toFind;
|
||||
do
|
||||
{
|
||||
toFind = GetNextSibling(p);
|
||||
p = GetItemParent(p);
|
||||
}
|
||||
while (p.IsOk() && !toFind.IsOk());
|
||||
id = toFind;
|
||||
}
|
||||
}
|
||||
// If we haven't found the item, id.IsOk() will be false, as per
|
||||
// documentation
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ctlTree::OnChar(wxKeyEvent &event)
|
||||
{
|
||||
int keyCode = event.GetKeyCode();
|
||||
if ( !event.HasModifiers() &&
|
||||
((keyCode >= '0' && keyCode <= '9') ||
|
||||
(keyCode >= 'a' && keyCode <= 'z') ||
|
||||
(keyCode >= 'A' && keyCode <= 'Z')))
|
||||
{
|
||||
wxTreeItemId currItem = GetSelection();
|
||||
|
||||
if (!currItem.IsOk())
|
||||
return;
|
||||
|
||||
wxTreeItemId matchItem = FindItem(currItem, m_findPrefix + (wxChar)keyCode);
|
||||
|
||||
if ( matchItem.IsOk() )
|
||||
{
|
||||
EnsureVisible(matchItem);
|
||||
SelectItem(matchItem);
|
||||
|
||||
m_findPrefix += (wxChar)keyCode;
|
||||
|
||||
// also start the timer to reset the current prefix if the user
|
||||
// doesn't press any more alnum keys soon -- we wouldn't want
|
||||
// to use this prefix for a new item search
|
||||
if ( !m_findTimer )
|
||||
{
|
||||
m_findTimer = new ctlTreeFindTimer(this);
|
||||
}
|
||||
|
||||
m_findTimer->Start(ctlTreeFindTimer::CTLTREE_DELAY, wxTIMER_ONE_SHOT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event.Skip(true);
|
||||
}
|
||||
}
|
||||
|
||||
ctlTree::ctlTree(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
|
||||
: wxTreeCtrl(parent, id, pos, size, style), m_findTimer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ctlTree::~ctlTree()
|
||||
{
|
||||
if ( m_findTimer )
|
||||
delete m_findTimer;
|
||||
m_findTimer = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ctlTree::RemoveDummyChild(pgObject *obj)
|
||||
{
|
||||
wxCookieType cookie;
|
||||
wxTreeItemId childItem = GetFirstChild(obj->GetId(), cookie);
|
||||
if (childItem && !GetItemData(childItem))
|
||||
{
|
||||
// The child was a dummy item, which will be replaced by the following ShowTreeDetail by true items
|
||||
Delete(childItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pgObject *ctlTree::GetObject(wxTreeItemId id)
|
||||
{
|
||||
if (id)
|
||||
return (pgObject *)GetItemData(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
pgCollection *ctlTree::GetParentCollection(wxTreeItemId id)
|
||||
{
|
||||
pgCollection *coll = (pgCollection *)GetParentObject(id);
|
||||
if (coll && coll->IsCollection())
|
||||
return coll;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ctlTree::SetItemImage(const wxTreeItemId &item, int image, wxTreeItemIcon which)
|
||||
{
|
||||
wxTreeCtrl::SetItemImage(item, image, which);
|
||||
|
||||
wxTreeItemData *data = GetItemData(item);
|
||||
|
||||
// Set the item colour
|
||||
if (data)
|
||||
{
|
||||
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
|
||||
{
|
||||
if (!((pgServer *)data)->GetColour().IsEmpty())
|
||||
SetItemBackgroundColour(item, wxColour(((pgServer *)data)->GetColour()));
|
||||
}
|
||||
else if (((pgObject *)data)->GetServer())
|
||||
{
|
||||
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
|
||||
SetItemBackgroundColour(item, wxColour(((pgObject *)data)->GetServer()->GetColour()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxTreeItemId ctlTree::AppendItem(const wxTreeItemId &parent, const wxString &text, int image, int selImage, wxTreeItemData *data)
|
||||
{
|
||||
wxTreeItemId itm = wxTreeCtrl::AppendItem(parent, text, image, selImage, data);
|
||||
|
||||
// Set the item colour
|
||||
if (data)
|
||||
{
|
||||
if (((pgObject *)data)->GetMetaType() == PGM_SERVER)
|
||||
{
|
||||
if (!((pgServer *)data)->GetColour().IsEmpty())
|
||||
SetItemBackgroundColour(itm, wxColour(((pgServer *)data)->GetColour()));
|
||||
}
|
||||
else if (((pgObject *)data)->GetServer())
|
||||
{
|
||||
if (!((pgObject *)data)->GetServer()->GetColour().IsEmpty())
|
||||
SetItemBackgroundColour(itm, wxColour(((pgObject *)data)->GetServer()->GetColour()));
|
||||
}
|
||||
}
|
||||
|
||||
return itm;
|
||||
}
|
||||
|
||||
wxTreeItemId ctlTree::AppendObject(pgObject *parent, pgObject *object)
|
||||
{
|
||||
wxString label;
|
||||
wxTreeItemId item;
|
||||
|
||||
if (object->IsCollection())
|
||||
label = object->GetTypeName();
|
||||
else
|
||||
label = object->GetDisplayName();
|
||||
item = AppendItem(parent->GetId(), label, object->GetIconId(), -1, object);
|
||||
if (object->IsCollection())
|
||||
object->ShowTreeDetail(this);
|
||||
else if (object->WantDummyChild())
|
||||
AppendItem(object->GetId(), wxT("Dummy"));
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
pgCollection *ctlTree::AppendCollection(pgObject *parent, pgaFactory &factory)
|
||||
{
|
||||
pgCollection *collection = factory.CreateCollection(parent);
|
||||
AppendObject(parent, collection);
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
pgObject *ctlTree::FindObject(pgaFactory &factory, wxTreeItemId parent)
|
||||
{
|
||||
wxCookieType cookie;
|
||||
wxTreeItemId item = GetFirstChild(parent, cookie);
|
||||
while (item)
|
||||
{
|
||||
pgObject *obj = (pgObject *)GetItemData(item);
|
||||
if (obj && obj->IsCreatedBy(factory))
|
||||
return obj;
|
||||
item = GetNextChild(parent, cookie);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
pgCollection *ctlTree::FindCollection(pgaFactory &factory, wxTreeItemId parent)
|
||||
{
|
||||
pgaFactory *cf = factory.GetCollectionFactory();
|
||||
if (!cf)
|
||||
return 0;
|
||||
|
||||
pgCollection *collection = (pgCollection *)FindObject(*cf, parent);
|
||||
|
||||
if (!collection || !collection->IsCollection())
|
||||
return 0;
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
void ctlTree::NavigateTree(int keyCode)
|
||||
{
|
||||
switch(keyCode)
|
||||
{
|
||||
case WXK_LEFT:
|
||||
{
|
||||
//If tree item has children and is expanded, collapse it, otherwise select it's parent if has one
|
||||
wxTreeItemId currItem = GetSelection();
|
||||
|
||||
if (ItemHasChildren(currItem) && IsExpanded(currItem))
|
||||
{
|
||||
Collapse(currItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxTreeItemId parent = GetItemParent(currItem);
|
||||
if (parent.IsOk())
|
||||
{
|
||||
SelectItem(currItem, false);
|
||||
SelectItem(parent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WXK_RIGHT:
|
||||
{
|
||||
//If tree item do not have any children ignore it,
|
||||
//otherwise expand it if not expanded, and select first child if already expanded
|
||||
wxTreeItemId currItem = GetSelection();
|
||||
|
||||
if(ItemHasChildren(currItem))
|
||||
{
|
||||
if (!IsExpanded(currItem))
|
||||
{
|
||||
Expand(currItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxCookieType cookie;
|
||||
wxTreeItemId firstChild = GetFirstChild(currItem, cookie);
|
||||
SelectItem(currItem, false);
|
||||
SelectItem(firstChild, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
wxASSERT_MSG(false, _("Currently handles only right and left arrow key, other keys are working"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
treeObjectIterator::treeObjectIterator(ctlTree *brow, pgObject *obj)
|
||||
{
|
||||
browser = brow;
|
||||
object = obj;
|
||||
}
|
||||
|
||||
|
||||
pgObject *treeObjectIterator::GetNextObject()
|
||||
{
|
||||
if (!object || !browser)
|
||||
return 0;
|
||||
|
||||
if (!lastItem)
|
||||
lastItem = browser->GetFirstChild(object->GetId(), cookie);
|
||||
else
|
||||
lastItem = browser->GetNextChild(object->GetId(), cookie);
|
||||
|
||||
if (lastItem)
|
||||
return browser->GetObject(lastItem);
|
||||
else
|
||||
object = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
651
ctl/explainCanvas.cpp
Normal file
651
ctl/explainCanvas.cpp
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// explainCanvas.cpp - Explain Canvas
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "ctl/explainCanvas.h"
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ExplainCanvas, wxShapeCanvas)
|
||||
EVT_MOTION(ExplainCanvas::OnMouseMotion)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
ExplainCanvas::ExplainCanvas(wxWindow *parent)
|
||||
: wxShapeCanvas(parent), rootShape(NULL)
|
||||
{
|
||||
SetDiagram(new wxDiagram);
|
||||
GetDiagram()->SetCanvas(this);
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
popup = NULL;
|
||||
}
|
||||
|
||||
|
||||
ExplainCanvas::~ExplainCanvas()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ExplainCanvas::Clear()
|
||||
{
|
||||
GetDiagram()->DeleteAllShapes();
|
||||
rootShape = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ExplainCanvas::SetExplainString(const wxString &str)
|
||||
{
|
||||
// filtred block never executed
|
||||
wxString rez=wxT("");
|
||||
wxString flt=wxT("");
|
||||
wxStringTokenizer tmpstr(str, wxT("\n"));
|
||||
int p=-1;
|
||||
while (tmpstr.HasMoreTokens())
|
||||
{
|
||||
wxString tmp = tmpstr.GetNextToken();
|
||||
if (p>-1)
|
||||
{
|
||||
// áëîê never executed
|
||||
const wxChar *cp = tmp.c_str();
|
||||
int pp=0;
|
||||
while (pp<=p)
|
||||
{
|
||||
if (*cp != ' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
cp++;
|
||||
pp++;
|
||||
}
|
||||
if (pp<=p) p=-1;
|
||||
}
|
||||
if (p==-1)
|
||||
{
|
||||
p=tmp.Find(wxT("->"));
|
||||
if (p>-1) {
|
||||
int p2=tmp.Find(wxT("(never executed)"));
|
||||
if (p2==-1) {
|
||||
p=-1;
|
||||
} else
|
||||
{
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
flt=flt+tmp+wxT("\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Clear();
|
||||
|
||||
// We can have multiple plans in a single explain string
|
||||
// Add a empty root shape, which will never get drawn, but it will help us
|
||||
// to keep track of all these plans
|
||||
rootShape = ExplainShape::Create(0, NULL, wxEmptyString);
|
||||
AddShape(rootShape);
|
||||
|
||||
ExplainShape *last = rootShape;
|
||||
int maxLevel = 0;
|
||||
|
||||
wxStringTokenizer lines(flt, wxT("\n"));
|
||||
|
||||
while (lines.HasMoreTokens())
|
||||
{
|
||||
wxString tmp = lines.GetNextToken();
|
||||
wxString line = tmp.Strip(wxString::both);
|
||||
int braceCount = 0;
|
||||
do
|
||||
{
|
||||
const wxChar *cp = line.c_str();
|
||||
while (*cp)
|
||||
{
|
||||
if (*cp == '(')
|
||||
braceCount++;
|
||||
else if (*cp == ')')
|
||||
braceCount--;
|
||||
cp++;
|
||||
}
|
||||
if (braceCount > 0)
|
||||
{
|
||||
wxString tmp = lines.GetNextToken();
|
||||
line += wxT(" ") + tmp.Strip(wxString::both);
|
||||
braceCount = 0;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
while (lines.HasMoreTokens());
|
||||
|
||||
long level = ((tmp.Length() - line.Length() + 4) / 6) + 1;
|
||||
|
||||
if (last)
|
||||
{
|
||||
if (level != 1)
|
||||
{
|
||||
if (line.Left(4) == wxT("-> "))
|
||||
line = line.Mid(4);
|
||||
else
|
||||
{
|
||||
last->SetCondition(line);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
while (last != rootShape && level <= last->GetLevel())
|
||||
last = last->GetUpper();
|
||||
}
|
||||
|
||||
|
||||
ExplainShape *s = ExplainShape::Create(level, last, line);
|
||||
if (!s)
|
||||
continue;
|
||||
s->SetCanvas(this);
|
||||
InsertShape(s);
|
||||
s->Show(true);
|
||||
|
||||
if (level > maxLevel)
|
||||
maxLevel = level;
|
||||
|
||||
last = s;
|
||||
}
|
||||
|
||||
|
||||
int x0 = (int)(rootShape->GetWidth() * 3);
|
||||
int y0 = (int)(rootShape->GetHeight() * 3 / 2);
|
||||
int xoffs = (int)(rootShape->GetWidth() * 3);
|
||||
int yoffs = (int)(rootShape->GetHeight() * 5 / 4);
|
||||
|
||||
wxNode *current = GetDiagram()->GetShapeList()->GetFirst();
|
||||
while (current)
|
||||
{
|
||||
ExplainShape *s = (ExplainShape *)current->GetData();
|
||||
|
||||
if (!s->totalShapes)
|
||||
s->totalShapes = 1;
|
||||
if (s->GetUpper())
|
||||
s->GetUpper()->totalShapes += s->totalShapes;
|
||||
current = current->GetNext();
|
||||
}
|
||||
|
||||
current = GetDiagram()->GetShapeList()->GetLast();
|
||||
while (current)
|
||||
{
|
||||
ExplainShape *s = (ExplainShape *)current->GetData();
|
||||
|
||||
s->SetX(y0 + (maxLevel - s->GetLevel()) * xoffs);
|
||||
ExplainShape *upper = s->GetUpper();
|
||||
|
||||
if (upper)
|
||||
{
|
||||
s->SetY(upper->GetY() + upper->usedShapes * yoffs);
|
||||
upper->usedShapes += s->totalShapes;
|
||||
|
||||
// We don't require to draw a line from the root shape to its
|
||||
// childrens
|
||||
if (upper != rootShape)
|
||||
{
|
||||
wxLineShape *l = new ExplainLine(s, upper);
|
||||
l->Show(true);
|
||||
AddShape(l);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s->SetY(y0);
|
||||
}
|
||||
|
||||
current = current->GetPrevious();
|
||||
}
|
||||
|
||||
#define PIXPERUNIT 20
|
||||
int w = (maxLevel * xoffs + x0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
|
||||
int h = (rootShape->totalShapes * yoffs + y0 * 2 + PIXPERUNIT - 1) / PIXPERUNIT;
|
||||
|
||||
SetScrollbars(PIXPERUNIT, PIXPERUNIT, w, h);
|
||||
}
|
||||
|
||||
|
||||
void ExplainCanvas::OnMouseMotion(wxMouseEvent &ev)
|
||||
{
|
||||
ev.Skip(true);
|
||||
|
||||
if (ev.Dragging())
|
||||
return;
|
||||
|
||||
wxClientDC dc(this);
|
||||
PrepareDC(dc);
|
||||
|
||||
wxPoint logPos(ev.GetLogicalPosition(dc));
|
||||
|
||||
double x, y;
|
||||
x = (double) logPos.x;
|
||||
y = (double) logPos.y;
|
||||
|
||||
// Find the nearest object
|
||||
int attachment = 0;
|
||||
ExplainShape *nearestObj = dynamic_cast<ExplainShape *>(FindShape(x, y, &attachment));
|
||||
|
||||
if (nearestObj)
|
||||
{
|
||||
ShowPopup(nearestObj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExplainCanvas::ShowPopup(ExplainShape *s)
|
||||
{
|
||||
if (popup || s == NULL)
|
||||
return;
|
||||
|
||||
popup = new ExplainPopup(this, s, &popup);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ExplainCanvas::SaveAsImage(const wxString &fileName, wxBitmapType imageType)
|
||||
{
|
||||
if (GetDiagram()->GetCount() == 0)
|
||||
{
|
||||
wxMessageBox(_("Nothing to be saved!"), _("Save As an image"), wxOK | wxICON_INFORMATION);
|
||||
return;
|
||||
}
|
||||
|
||||
int width = 0, height = 0;
|
||||
GetVirtualSize(&width, &height);
|
||||
|
||||
/*
|
||||
* Create the bitmap from the Explain window
|
||||
*/
|
||||
wxMemoryDC memDC;
|
||||
wxBitmap tempBitmap(width, height);
|
||||
|
||||
memDC.SelectObject(tempBitmap);
|
||||
memDC.Clear();
|
||||
|
||||
// Draw the diagram on the bitmap (Memory Device Context)
|
||||
GetDiagram()->Redraw(memDC);
|
||||
|
||||
memDC.SelectObject(wxNullBitmap);
|
||||
|
||||
if (!tempBitmap.SaveFile(fileName, imageType))
|
||||
{
|
||||
wxLogError(_("Could not write file \"%s\": error code %d."), fileName.c_str(), wxSysErrorCode());
|
||||
}
|
||||
}
|
||||
|
||||
class ExplainText : public wxWindow
|
||||
{
|
||||
public:
|
||||
ExplainText(ExplainPopup *parent, ExplainShape *s);
|
||||
|
||||
protected:
|
||||
void OnMouseMove(wxMouseEvent &ev);
|
||||
#ifdef wxUSE_POPUPWIN
|
||||
void OnMouseLost(wxMouseCaptureLostEvent &ev);
|
||||
#endif
|
||||
|
||||
private:
|
||||
ExplainPopup *popup;
|
||||
void OnPaint(wxPaintEvent &ev);
|
||||
|
||||
wxString m_desc, m_detail, m_condition, m_cost, m_actual;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(ExplainText, wxWindow)
|
||||
EVT_PAINT(ExplainText::OnPaint)
|
||||
EVT_MOTION(ExplainText::OnMouseMove)
|
||||
#ifdef wxUSE_POPUPWIN
|
||||
EVT_MOUSE_CAPTURE_LOST(ExplainText::OnMouseLost)
|
||||
#endif
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ExplainText::ExplainText(ExplainPopup *parent, ExplainShape *s) : wxWindow(parent, -1)
|
||||
{
|
||||
SetBackgroundColour(wxColour(255, 255, 224));
|
||||
|
||||
popup = parent;
|
||||
|
||||
wxWindowDC dc(this);
|
||||
dc.SetFont(settings->GetSystemFont());
|
||||
|
||||
m_desc = s->description;
|
||||
m_detail = s->detail;
|
||||
m_condition = s->condition;
|
||||
m_cost = s->cost;
|
||||
m_actual = s->actual;
|
||||
|
||||
int w1, w2, h;
|
||||
dc.GetTextExtent(m_desc, &w1, &h);
|
||||
|
||||
dc.GetTextExtent(m_detail, &w2, &h);
|
||||
if (w1 < w2) w1 = w2;
|
||||
dc.GetTextExtent(m_condition, &w2, &h);
|
||||
if (w1 < w2) w1 = w2;
|
||||
dc.GetTextExtent(m_cost, &w2, &h);
|
||||
if (w1 < w2) w1 = w2;
|
||||
dc.GetTextExtent(m_actual, &w2, &h);
|
||||
if (w1 < w2) w1 = w2;
|
||||
|
||||
int n = 2;
|
||||
if (!m_detail.IsEmpty())
|
||||
n++;
|
||||
if (!m_condition.IsEmpty())
|
||||
n++;
|
||||
if (!m_cost.IsEmpty())
|
||||
n++;
|
||||
if (!m_actual.IsEmpty())
|
||||
n++;
|
||||
|
||||
if (!h)
|
||||
h = GetCharHeight();
|
||||
|
||||
SetSize(GetCharHeight() + w1, GetCharHeight() + h * n + h / 3);
|
||||
}
|
||||
|
||||
void ExplainText::OnMouseMove(wxMouseEvent &ev)
|
||||
{
|
||||
popup->OnMouseMove(ev);
|
||||
}
|
||||
|
||||
#ifdef wxUSE_POPUPWIN
|
||||
void ExplainText::OnMouseLost(wxMouseCaptureLostEvent &ev)
|
||||
{
|
||||
/*
|
||||
* We will not do anything here.
|
||||
* But - in order to resolve a weird bug on window, when using
|
||||
* wxPopupTransientWindow, related to loosing the mouse control,
|
||||
* was not taken care properly, we have to introduce this function
|
||||
* to avoid the crash.
|
||||
*
|
||||
* Please refer:
|
||||
* http://article.gmane.org/gmane.comp.lib.wxwidgets.devel/82376
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
void ExplainText::OnPaint(wxPaintEvent &ev)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
|
||||
wxFont stdFont = settings->GetSystemFont();
|
||||
wxFont boldFont = stdFont;
|
||||
boldFont.SetWeight(wxBOLD);
|
||||
|
||||
int x = GetCharHeight() / 2;
|
||||
int y = GetCharHeight() / 2;
|
||||
int w, yoffs;
|
||||
dc.GetTextExtent(wxT("Dummy"), &w, &yoffs);
|
||||
|
||||
dc.SetFont(boldFont);
|
||||
dc.DrawText(m_desc, x, y);
|
||||
|
||||
dc.SetFont(stdFont);
|
||||
|
||||
if (!m_detail.IsEmpty())
|
||||
{
|
||||
y += yoffs;
|
||||
dc.DrawText(m_detail, x, y);
|
||||
|
||||
}
|
||||
y += yoffs / 3;
|
||||
|
||||
if (!m_condition.IsEmpty())
|
||||
{
|
||||
y += yoffs;
|
||||
dc.DrawText(m_condition, x, y);
|
||||
}
|
||||
if (!m_cost.IsEmpty())
|
||||
{
|
||||
y += yoffs;
|
||||
dc.DrawText(m_cost, x, y);
|
||||
}
|
||||
if (!m_actual.IsEmpty())
|
||||
{
|
||||
y += yoffs;
|
||||
dc.DrawText(m_actual, x, y);
|
||||
}
|
||||
|
||||
#if wxUSE_POPUPWIN
|
||||
|
||||
wxPen pen1 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)),
|
||||
pen2 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW)),
|
||||
pen3 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHIGHLIGHT)),
|
||||
pen4 = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
|
||||
|
||||
wxRect rect(wxPoint(0, 0), GetSize());
|
||||
|
||||
// draw the rectangle
|
||||
dc.SetPen(pen1);
|
||||
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
|
||||
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
|
||||
dc.SetPen(pen2);
|
||||
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
|
||||
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
|
||||
|
||||
// adjust the rect
|
||||
rect.Inflate(-1);
|
||||
|
||||
// draw the rectangle
|
||||
dc.SetPen(pen3);
|
||||
dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
|
||||
dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(), rect.GetRight(), rect.GetTop());
|
||||
dc.SetPen(pen4);
|
||||
dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
|
||||
dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight() + 1, rect.GetBottom());
|
||||
|
||||
// adjust the rect
|
||||
rect.Inflate(-1);
|
||||
|
||||
dc.SetPen(pen1);
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRectangle(rect);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ExplainPopup, pgTipWindowBase)
|
||||
EVT_MOTION(ExplainPopup::OnMouseMove)
|
||||
EVT_LEFT_DOWN(ExplainPopup::OnMouseClick)
|
||||
EVT_RIGHT_DOWN(ExplainPopup::OnMouseClick)
|
||||
EVT_MIDDLE_DOWN(ExplainPopup::OnMouseClick)
|
||||
#if wxUSE_POPUPWIN
|
||||
EVT_MOUSE_CAPTURE_LOST(ExplainPopup::OnMouseLost)
|
||||
#else
|
||||
EVT_KILL_FOCUS(ExplainPopup::OnKillFocus)
|
||||
EVT_ACTIVATE(ExplainPopup::OnActivate)
|
||||
#endif // !wxUSE_POPUPWIN
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ExplainPopup::ExplainPopup(ExplainCanvas *parent, ExplainShape *shape, ExplainPopup **popup)
|
||||
#if wxUSE_POPUPWIN
|
||||
: wxPopupTransientWindow(parent, wxNO_BORDER)
|
||||
#else
|
||||
: wxFrame(parent, wxID_ANY, wxEmptyString,
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxNO_BORDER | wxFRAME_NO_TASKBAR )
|
||||
#endif
|
||||
{
|
||||
wxASSERT(parent != NULL);
|
||||
wxASSERT(shape != NULL);
|
||||
|
||||
if (popup)
|
||||
m_ptr = popup;
|
||||
else
|
||||
m_ptr = NULL;
|
||||
|
||||
m_explainText = new ExplainText(this, shape);
|
||||
#if !wxUSE_POPUPWIN
|
||||
m_creationTime = wxGetLocalTime();
|
||||
#endif
|
||||
double width = 0.0, height = 0.0;
|
||||
|
||||
shape->GetBoundingBoxMin(&width, &height);
|
||||
if (fabs(width) < 4.0) width = 4.0;
|
||||
if (fabs(height) < 4.0) height = 4.0;
|
||||
|
||||
width += (double)4.0;
|
||||
height += (double)4.0; // Allowance for inaccurate mousing
|
||||
|
||||
int x = (int)(shape->GetX() - (width / 2.0));
|
||||
int y = (int)(shape->GetY() - (height / 2.0));
|
||||
|
||||
int sx, sy;
|
||||
parent->CalcScrolledPosition(x, y, &sx, &sy);
|
||||
parent->ClientToScreen(&sx, &sy);
|
||||
|
||||
m_rectBound.x = sx;
|
||||
m_rectBound.y = sy;
|
||||
m_rectBound.width = WXROUND(width);
|
||||
m_rectBound.height = WXROUND(height);
|
||||
|
||||
wxSize popupSize;
|
||||
popupSize = m_explainText->GetSize();
|
||||
popupSize.DecTo(wxGetDisplaySize());
|
||||
SetSize(popupSize);
|
||||
|
||||
if (sy > GetClientSize().y * 2 / 3)
|
||||
sy -= GetSize().y;
|
||||
sx -= GetSize().x / 2;
|
||||
|
||||
if (sx < 5) sx = 5;
|
||||
|
||||
if (sx < 0) x = 0;
|
||||
|
||||
#if wxUSE_POPUPWIN
|
||||
Position(wxPoint(sx, sy), wxSize(0, 0));
|
||||
Popup(m_explainText);
|
||||
m_explainText->SetFocus();
|
||||
#ifdef __WXGTK__
|
||||
m_explainText->CaptureMouse();
|
||||
#endif
|
||||
#else
|
||||
Move(sx, sy);
|
||||
Show(true);
|
||||
|
||||
m_explainText->SetFocus();
|
||||
m_explainText->CaptureMouse();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ExplainPopup::OnMouseMove(wxMouseEvent &ev)
|
||||
{
|
||||
if ( m_rectBound.width &&
|
||||
!m_rectBound.Contains(ClientToScreen(ev.GetPosition())) )
|
||||
{
|
||||
// mouse left the bounding rect, disappear
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
ev.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExplainPopup::OnMouseClick(wxMouseEvent &ev)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
ExplainPopup::~ExplainPopup()
|
||||
{
|
||||
if (m_ptr)
|
||||
{
|
||||
*m_ptr = NULL;
|
||||
}
|
||||
#ifdef wxUSE_POPUPWIN
|
||||
#ifdef __WXGTK__
|
||||
if (m_explainText->HasCapture() )
|
||||
m_explainText->ReleaseMouse();
|
||||
#endif
|
||||
#endif
|
||||
if(HasCapture())
|
||||
ReleaseMouse();
|
||||
}
|
||||
|
||||
void ExplainPopup::Close()
|
||||
{
|
||||
if (m_ptr)
|
||||
{
|
||||
*m_ptr = NULL;
|
||||
m_ptr = NULL;
|
||||
}
|
||||
|
||||
if (m_explainText->HasCapture())
|
||||
m_explainText->ReleaseMouse();
|
||||
|
||||
if(HasCapture())
|
||||
ReleaseMouse();
|
||||
|
||||
#if wxUSE_POPUPWIN
|
||||
Show(false);
|
||||
#ifdef __WXGTK__
|
||||
#endif
|
||||
Destroy();
|
||||
#else
|
||||
wxFrame::Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if wxUSE_POPUPWIN
|
||||
void ExplainPopup::OnDismiss()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void ExplainPopup::OnMouseLost(wxMouseCaptureLostEvent &ev)
|
||||
{
|
||||
/* Do Nothing */
|
||||
}
|
||||
#else
|
||||
void ExplainPopup::OnKillFocus(wxFocusEvent &event)
|
||||
{
|
||||
// Workaround the kill focus event happening just after creation in wxGTK
|
||||
if (wxGetLocalTime() > m_creationTime + 1 &&
|
||||
m_rectBound.width &&
|
||||
!m_rectBound.Contains(::wxGetMousePosition()))
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
m_explainText->SetFocus();
|
||||
m_explainText->CaptureMouse();
|
||||
}
|
||||
|
||||
|
||||
void ExplainPopup::OnActivate(wxActivateEvent &event)
|
||||
{
|
||||
if (!event.GetActive())
|
||||
{
|
||||
if ( m_rectBound.width &&
|
||||
!m_rectBound.Contains(::wxGetMousePosition()) )
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
m_explainText->SetFocus();
|
||||
m_explainText->CaptureMouse();
|
||||
}
|
||||
}
|
||||
#endif // !wxUSE_POPUPWIN
|
||||
581
ctl/explainShape.cpp
Normal file
581
ctl/explainShape.cpp
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// explainShape.cpp - Explain Shapes
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "ctl/explainCanvas.h"
|
||||
|
||||
#include <wx/docview.h>
|
||||
|
||||
#include "images/ex_aggregate.pngc"
|
||||
#include "images/ex_append.pngc"
|
||||
#include "images/ex_bmp_and.pngc"
|
||||
#include "images/ex_bmp_heap.pngc"
|
||||
#include "images/ex_bmp_index.pngc"
|
||||
#include "images/ex_bmp_or.pngc"
|
||||
#include "images/ex_cte_scan.pngc"
|
||||
#include "images/ex_delete.pngc"
|
||||
#include "images/ex_foreign_scan.pngc"
|
||||
#include "images/ex_group.pngc"
|
||||
#include "images/ex_hash.pngc"
|
||||
#include "images/ex_hash_anti_join.pngc"
|
||||
#include "images/ex_hash_semi_join.pngc"
|
||||
#include "images/ex_hash_setop_except.pngc"
|
||||
#include "images/ex_hash_setop_except_all.pngc"
|
||||
#include "images/ex_hash_setop_intersect.pngc"
|
||||
#include "images/ex_hash_setop_intersect_all.pngc"
|
||||
#include "images/ex_hash_setop_unknown.pngc"
|
||||
#include "images/ex_index_only_scan.pngc"
|
||||
#include "images/ex_index_scan.pngc"
|
||||
#include "images/ex_insert.pngc"
|
||||
#include "images/ex_lock_rows.pngc"
|
||||
#include "images/ex_join.pngc"
|
||||
#include "images/ex_limit.pngc"
|
||||
#include "images/ex_materialize.pngc"
|
||||
#include "images/ex_merge.pngc"
|
||||
#include "images/ex_merge_anti_join.pngc"
|
||||
#include "images/ex_merge_append.pngc"
|
||||
#include "images/ex_merge_semi_join.pngc"
|
||||
#include "images/ex_gatcher.pngc"
|
||||
#include "images/ex_nested.pngc"
|
||||
#include "images/ex_nested_loop_anti_join.pngc"
|
||||
#include "images/ex_nested_loop_semi_join.pngc"
|
||||
#include "images/ex_recursive_union.pngc"
|
||||
#include "images/ex_result.pngc"
|
||||
#include "images/ex_scan.pngc"
|
||||
#include "images/ex_pscan.pngc"
|
||||
#include "images/ex_seek.pngc"
|
||||
#include "images/ex_setop.pngc"
|
||||
#include "images/ex_sort.pngc"
|
||||
#include "images/ex_subplan.pngc"
|
||||
#include "images/ex_tid_scan.pngc"
|
||||
#include "images/ex_unique.pngc"
|
||||
#include "images/ex_unknown.pngc"
|
||||
#include "images/ex_update.pngc"
|
||||
#include "images/ex_values_scan.pngc"
|
||||
#include "images/ex_window_aggregate.pngc"
|
||||
#include "images/ex_worktable_scan.pngc"
|
||||
|
||||
// Greenplum images
|
||||
#include "images/ex_broadcast_motion.pngc"
|
||||
#include "images/ex_redistribute_motion.pngc"
|
||||
#include "images/ex_gather_motion.pngc"
|
||||
|
||||
#define BMP_BORDER 3
|
||||
|
||||
ExplainShape::ExplainShape(const wxImage &bmp, const wxString &description, long tokenNo, long detailNo)
|
||||
{
|
||||
SetBitmap(wxBitmap(bmp));
|
||||
SetLabel(description, tokenNo, detailNo);
|
||||
kidCount = 0;
|
||||
totalShapes = 0;
|
||||
usedShapes = 0;
|
||||
m_rootShape = false;
|
||||
}
|
||||
|
||||
|
||||
void ExplainShape::SetLabel(const wxString &str, int tokenNo, int detailNo)
|
||||
{
|
||||
if (tokenNo < 0)
|
||||
{
|
||||
description = str;
|
||||
label = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxStringTokenizer tokens(str, wxT(" "));
|
||||
|
||||
while (tokenNo-- >= 0)
|
||||
{
|
||||
label = tokens.GetNextToken();
|
||||
|
||||
if (detailNo <= 0)
|
||||
{
|
||||
if (!description.IsEmpty())
|
||||
description.Append(wxT(" "));
|
||||
|
||||
description.Append(label);
|
||||
}
|
||||
}
|
||||
if (detailNo > 0)
|
||||
{
|
||||
tokens.SetString(str, wxT(" "));
|
||||
|
||||
while (detailNo--)
|
||||
{
|
||||
if (!description.IsEmpty())
|
||||
description.Append(wxT(" "));
|
||||
|
||||
description.Append(tokens.GetNextToken());
|
||||
}
|
||||
detail = tokens.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExplainShape::OnDraw(wxDC &dc)
|
||||
{
|
||||
wxBitmap &bmp = GetBitmap();
|
||||
if (!bmp.Ok())
|
||||
return;
|
||||
|
||||
// We do not draw the root shape
|
||||
if (m_rootShape)
|
||||
return;
|
||||
|
||||
int x, y;
|
||||
x = WXROUND(m_xpos - bmp.GetWidth() / 2.0);
|
||||
y = WXROUND(m_ypos - GetHeight() / 2.0);
|
||||
|
||||
dc.DrawBitmap(bmp, x, y, true);
|
||||
|
||||
int w, h;
|
||||
dc.SetFont(GetCanvas()->GetFont());
|
||||
dc.GetTextExtent(label, &w, &h);
|
||||
|
||||
x = WXROUND(m_xpos - w / 2.0);
|
||||
y += bmp.GetHeight() + BMP_BORDER;
|
||||
|
||||
dc.DrawText(label, x, y);
|
||||
}
|
||||
|
||||
|
||||
void ExplainShape::OnLeftClick(double x, double y, int keys, int attachment)
|
||||
{
|
||||
((ExplainCanvas *)GetCanvas())->ShowPopup(this);
|
||||
}
|
||||
|
||||
|
||||
#define ARROWMARGIN 5
|
||||
wxRealPoint ExplainShape::GetStartPoint()
|
||||
{
|
||||
wxRealPoint rp(GetX() + GetBitmap().GetWidth() / 2.0 + ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2.);
|
||||
return rp;
|
||||
}
|
||||
|
||||
|
||||
wxRealPoint ExplainShape::GetEndPoint(int kidNo)
|
||||
{
|
||||
wxRealPoint rp(GetX() - GetBitmap().GetWidth() / 2.0 - ARROWMARGIN, GetY() - (GetHeight() - GetBitmap().GetHeight()) / 2. + (kidCount > 1 ? GetBitmap().GetHeight() * 2. / 3. * kidNo / (2 * kidCount - 2) : 0 ));
|
||||
return rp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExplainShape *ExplainShape::Create(long level, ExplainShape *last, const wxString &str)
|
||||
{
|
||||
ExplainShape *s = 0;
|
||||
|
||||
int costPos = str.Find(wxT("(cost="));
|
||||
int actPos = str.Find(wxT("(actual"));
|
||||
bool parallel=false;
|
||||
int inc=0;
|
||||
wxStringTokenizer tokens(str, wxT(" "));
|
||||
wxString token = tokens.GetNextToken();
|
||||
if (token == wxT("Parallel"))
|
||||
{
|
||||
token = tokens.GetNextToken();
|
||||
parallel=true;
|
||||
inc=1;
|
||||
}
|
||||
|
||||
wxString token2 = tokens.GetNextToken();
|
||||
wxString token3 = tokens.GetNextToken();
|
||||
wxString token4;
|
||||
if (tokens.HasMoreTokens())
|
||||
token4 = tokens.GetNextToken();
|
||||
wxString descr;
|
||||
if (costPos > 0)
|
||||
descr = str.Left(costPos);
|
||||
else if (actPos > 0)
|
||||
descr = str.Left(actPos);
|
||||
else
|
||||
descr = str;
|
||||
|
||||
// Requested an empty shape, which can be treated as a root shape
|
||||
if (level == 0)
|
||||
{
|
||||
s = new ExplainShape(*ex_unknown_png_img, wxEmptyString);
|
||||
s->SetDraggable(false);
|
||||
s->m_rootShape = true;
|
||||
s->level = level;
|
||||
int w = 50, h = 20;
|
||||
|
||||
wxBitmap &bmp = s->GetBitmap();
|
||||
if (w < bmp.GetWidth())
|
||||
w = bmp.GetWidth();
|
||||
|
||||
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
|
||||
s->SetWidth(w);
|
||||
|
||||
s->upperShape = NULL;
|
||||
s->kidNo = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
// possible keywords can be found in postgresql/src/backend/commands/explain.c
|
||||
|
||||
if (token == wxT("Total")) return 0;
|
||||
else if (token == wxT("Trigger")) return 0;
|
||||
else if (token == wxT("Settings:")) return 0; /* Greenplum */
|
||||
else if (token == wxT("Slice")) return 0; /* Greenplum */
|
||||
else if (token.Mid(0, 6) == wxT("(slice")) return 0; /* Greenplum */
|
||||
else if (token == wxT("Result")) s = new ExplainShape(*ex_result_png_img, descr);
|
||||
else if (token == wxT("Append")) s = new ExplainShape(*ex_append_png_img, descr);
|
||||
else if (token == wxT("Gather")) s = new ExplainShape(*ex_gatcher_png_img, descr);
|
||||
else if (token == wxT("Nested"))
|
||||
{
|
||||
if (token2 == wxT("Loop") && token4 == wxT("Join"))
|
||||
{
|
||||
// Nested Loop Anti Join
|
||||
if (token3 == wxT("Anti"))
|
||||
{
|
||||
s = new ExplainShape(*ex_nested_loop_anti_join_png_img, descr);
|
||||
}
|
||||
// Nested Loop Semi Join
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_nested_loop_semi_join_png_img, descr);
|
||||
}
|
||||
}
|
||||
if (!s)
|
||||
s = new ExplainShape(*ex_nested_png_img, descr);
|
||||
}
|
||||
else if (token == wxT("Merge"))
|
||||
{
|
||||
if (token3 == wxT("Join"))
|
||||
{
|
||||
// Merge Anti Join
|
||||
if (token2 == wxT("Anti"))
|
||||
{
|
||||
s = new ExplainShape(*ex_merge_anti_join_png_img, descr);
|
||||
}
|
||||
// Merge Semi Join
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_merge_semi_join_png_img, descr);
|
||||
}
|
||||
}
|
||||
// Merge Append
|
||||
else if (token2 == wxT("Append"))
|
||||
{
|
||||
s = new ExplainShape(*ex_merge_append_png_img, descr);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_merge_png_img, descr);
|
||||
}
|
||||
}
|
||||
else if (token == wxT("Hash"))
|
||||
{
|
||||
if (token2 == wxT("Join"))
|
||||
{
|
||||
s = new ExplainShape(*ex_join_png_img, descr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (token3 == wxT("Join"))
|
||||
{
|
||||
// Hash Anti Join
|
||||
if (token2 == wxT("Anti"))
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_anti_join_png_img, descr);
|
||||
}
|
||||
// Hash Semi Join
|
||||
else if (token2 == wxT("Semi"))
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_semi_join_png_img, descr);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_png_img, descr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_png_img, descr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token == wxT("HashSetOp"))
|
||||
{
|
||||
if (token2 == wxT("Except"))
|
||||
{
|
||||
// HashSetOp Except ALL
|
||||
if (token3 == wxT("ALL"))
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_setop_except_all_png_img, descr);
|
||||
}
|
||||
// HashSetOp Except
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_setop_except_png_img, descr);
|
||||
}
|
||||
}
|
||||
else if (token2 == wxT("Intersect"))
|
||||
{
|
||||
// HashSetOp Intersect ALL
|
||||
if (token3 == wxT("ALL"))
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_setop_intersect_all_png_img, descr);
|
||||
}
|
||||
// HashSetOp Intersect
|
||||
else
|
||||
{
|
||||
s = new ExplainShape(*ex_hash_setop_intersect_png_img, descr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// HashSetOp ???
|
||||
s = new ExplainShape(*ex_hash_setop_unknown_png_img, descr);
|
||||
}
|
||||
}
|
||||
else if (token == wxT("Subquery")) s = new ExplainShape(*ex_subplan_png_img, descr, 0, 2);
|
||||
else if (token == wxT("Function")) s = new ExplainShape(*ex_result_png_img, descr, 0, 2);
|
||||
else if (token == wxT("Materialize")) s = new ExplainShape(*ex_materialize_png_img, descr);
|
||||
else if (token == wxT("Sort")) s = new ExplainShape(*ex_sort_png_img, descr);
|
||||
else if (token == wxT("Group")) s = new ExplainShape(*ex_group_png_img, descr);
|
||||
else if (token == wxT("Aggregate") || token == wxT("GroupAggregate") || token == wxT("HashAggregate"))
|
||||
s = new ExplainShape(*ex_aggregate_png_img, descr);
|
||||
else if (token == wxT("Unique")) s = new ExplainShape(*ex_unique_png_img, descr);
|
||||
else if (token == wxT("SetOp")) s = new ExplainShape(*ex_setop_png_img, descr);
|
||||
else if (token == wxT("Limit")) s = new ExplainShape(*ex_limit_png_img, descr);
|
||||
else if (token == wxT("LockRows")) s = new ExplainShape(*ex_lock_rows_png_img, descr);
|
||||
else if (token == wxT("Bitmap"))
|
||||
{
|
||||
if (token2 == wxT("Index")) s = new ExplainShape(*ex_bmp_index_png_img, descr, 4+inc, 3+inc);
|
||||
else s = new ExplainShape(*ex_bmp_heap_png_img, descr, 4+inc, 3+inc);
|
||||
}
|
||||
else if (token == wxT("BitmapAnd")) s = new ExplainShape(*ex_bmp_and_png_img, descr);
|
||||
else if (token == wxT("BitmapOr")) s = new ExplainShape(*ex_bmp_or_png_img, descr);
|
||||
else if (token2 == wxT("Scan"))
|
||||
{
|
||||
if (token == wxT("Index"))
|
||||
// Scan Index Backward
|
||||
if (token3 == wxT("Backward"))
|
||||
s = new ExplainShape(*ex_index_scan_png_img, descr, 4+inc, 3+inc);
|
||||
else
|
||||
s = new ExplainShape(*ex_index_scan_png_img, descr, 3+inc, 2+inc);
|
||||
// Tid Scan
|
||||
else if (token == wxT("Tid"))
|
||||
s = new ExplainShape(*ex_tid_scan_png_img, descr, 3+inc, 2+inc);
|
||||
// WorkTable Scan
|
||||
else if (token == wxT("WorkTable"))
|
||||
s = new ExplainShape(*ex_worktable_scan_png_img, descr, 3+inc, 2+inc);
|
||||
// CTE Scan
|
||||
else if (token == wxT("CTE"))
|
||||
s = new ExplainShape(*ex_cte_scan_png_img, descr, 3, 2);
|
||||
// Foreign Scan
|
||||
else if (token == wxT("Foreign"))
|
||||
s = new ExplainShape(*ex_foreign_scan_png_img, descr, 3, 2);
|
||||
// Values Scan
|
||||
else if (token == wxT("Values"))
|
||||
s = new ExplainShape(*ex_values_scan_png_img, descr, 3, 2);
|
||||
else if (parallel) s = new ExplainShape(*ex_pscan_png_img, descr, 4, 3);
|
||||
else
|
||||
s = new ExplainShape(*ex_scan_png_img, descr, 3+inc, 2+inc);
|
||||
}
|
||||
else if (token == wxT("Index"))
|
||||
{
|
||||
// Index Only Scan
|
||||
if (token2 == wxT("Only") && token3 == wxT("Scan"))
|
||||
{
|
||||
s = new ExplainShape(*ex_index_only_scan_png_img, descr, 4+inc, 3+inc);
|
||||
}
|
||||
}
|
||||
else if (token2 == wxT("Seek")) s = new ExplainShape(*ex_seek_png_img, descr, 3, 2);
|
||||
// Recursive Union
|
||||
else if (token == wxT("Recursive") && token2 == wxT("Union"))
|
||||
s = new ExplainShape(*ex_recursive_union_png_img, descr);
|
||||
else if (token == wxT("WindowAgg"))
|
||||
s = new ExplainShape(*ex_window_aggregate_png_img, descr);
|
||||
|
||||
// DML
|
||||
else if (token == wxT("Insert"))
|
||||
s = new ExplainShape(*ex_insert_png_img, descr, 2, 1);
|
||||
else if (token == wxT("Update"))
|
||||
s = new ExplainShape(*ex_update_png_img, descr, 2, 1);
|
||||
else if (token == wxT("Delete"))
|
||||
s = new ExplainShape(*ex_delete_png_img, descr, 2, 1);
|
||||
|
||||
// Greenplum additions
|
||||
else if (token == wxT("Gather") && token2 == wxT("Motion"))
|
||||
s = new ExplainShape(*ex_gather_motion_png_img, descr);
|
||||
else if (token == wxT("Broadcast") && token2 == wxT("Motion"))
|
||||
s = new ExplainShape(*ex_broadcast_motion_png_img, descr);
|
||||
else if (token == wxT("Redistribute") && token2 == wxT("Motion"))
|
||||
s = new ExplainShape(*ex_redistribute_motion_png_img, descr);
|
||||
|
||||
if (!s)
|
||||
s = new ExplainShape(*ex_unknown_png_img, descr);
|
||||
|
||||
s->SetDraggable(false);
|
||||
|
||||
s->level = level;
|
||||
|
||||
if (costPos > 0)
|
||||
{
|
||||
if (actPos > 0)
|
||||
{
|
||||
s->actual = str.Mid(actPos);
|
||||
s->cost = str.Mid(costPos, actPos - costPos);
|
||||
}
|
||||
else
|
||||
s->cost = str.Mid(costPos);
|
||||
}
|
||||
else if (actPos > 0)
|
||||
s->actual = str.Mid(actPos);
|
||||
|
||||
int w = 50, h = 20;
|
||||
|
||||
wxBitmap &bmp = s->GetBitmap();
|
||||
if (w < bmp.GetWidth())
|
||||
w = bmp.GetWidth();
|
||||
|
||||
s->SetHeight(bmp.GetHeight() + BMP_BORDER + h);
|
||||
s->SetWidth(w);
|
||||
|
||||
s->upperShape = last;
|
||||
if (last)
|
||||
{
|
||||
s->kidNo = last->kidCount;
|
||||
last->kidCount++;
|
||||
}
|
||||
else
|
||||
s->kidNo = 0;
|
||||
|
||||
if (costPos > 0)
|
||||
{
|
||||
wxChar *cl = const_cast<wxChar *>((const wxChar *)str + costPos + 6);
|
||||
wxChar *ch = wxStrstr(cl, wxT(".."));
|
||||
if (ch)
|
||||
{
|
||||
*ch = 0;
|
||||
ch += 2;
|
||||
}
|
||||
s->costLow = StrToDouble(cl);
|
||||
if (ch)
|
||||
{
|
||||
wxChar *r = wxStrstr(ch, wxT(" rows="));
|
||||
if (r)
|
||||
{
|
||||
*r = 0;
|
||||
r += 6;
|
||||
}
|
||||
s->costHigh = StrToDouble(ch);
|
||||
if (r)
|
||||
{
|
||||
wxChar *w = wxStrstr(r, wxT(" width="));
|
||||
if (w)
|
||||
{
|
||||
*w = 0;
|
||||
w += 7;
|
||||
}
|
||||
s->rows = StrToLong(r);
|
||||
if (w)
|
||||
s->width = StrToLong(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
ExplainLine::ExplainLine(ExplainShape *from, ExplainShape *to, double weight)
|
||||
{
|
||||
SetCanvas(from->GetCanvas());
|
||||
from->AddLine(this, to);
|
||||
MakeLineControlPoints(4);
|
||||
|
||||
width = (int) log(from->GetAverageCost());
|
||||
if (width > 10)
|
||||
width = 10;
|
||||
|
||||
// If we got an average cost of 0, width is probably negative
|
||||
// which will look pretty darn ugly as an arrow width!
|
||||
// This may happen on Greenplum.
|
||||
if (width < 1)
|
||||
width = 1;
|
||||
|
||||
wxNode *first = GetLineControlPoints()->GetFirst();
|
||||
wxNode *last = GetLineControlPoints()->GetLast();
|
||||
*(wxRealPoint *)first->GetData() = from->GetStartPoint();
|
||||
*(wxRealPoint *)last->GetData() = to->GetEndPoint(from->GetKidno());
|
||||
|
||||
wxRealPoint *p1 = (wxRealPoint *)first->GetNext()->GetData();
|
||||
wxRealPoint *p2 = (wxRealPoint *)last->GetPrevious()->GetData();
|
||||
*p1 = from->GetStartPoint();
|
||||
*p2 = to->GetEndPoint(from->GetKidno());
|
||||
p1->x -= (p1->x - p2->x) / 3. + 8;
|
||||
p2->x += (p1->x - p2->x) / 3. - 8;
|
||||
|
||||
Initialise();
|
||||
}
|
||||
|
||||
|
||||
#define ARROWWIDTH 4
|
||||
|
||||
|
||||
void ExplainLine::OnDraw(wxDC &dc)
|
||||
{
|
||||
if (m_lineControlPoints)
|
||||
{
|
||||
dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxSOLID));
|
||||
dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(*wxLIGHT_GREY, wxSOLID));
|
||||
|
||||
wxPoint *points = new wxPoint[11];
|
||||
wxRealPoint *point0 = (wxRealPoint *) m_lineControlPoints->Item(0)->GetData();
|
||||
wxRealPoint *point1 = (wxRealPoint *) m_lineControlPoints->Item(1)->GetData();
|
||||
wxRealPoint *point2 = (wxRealPoint *) m_lineControlPoints->Item(2)->GetData();
|
||||
wxRealPoint *point3 = (wxRealPoint *) m_lineControlPoints->Item(3)->GetData();
|
||||
|
||||
double phi = atan2(point2->y - point1->y, point2->x - point1->x);
|
||||
double offs = width * tan(phi / 2);
|
||||
|
||||
points[0].x = WXROUND(point0->x);
|
||||
points[0].y = WXROUND(point0->y) - width;
|
||||
points[10].x = WXROUND(point0->x);
|
||||
points[10].y = WXROUND(point0->y) + width;
|
||||
|
||||
points[1].x = WXROUND(point1->x + offs);
|
||||
points[1].y = WXROUND(point1->y) - width;
|
||||
points[9].x = WXROUND(point1->x - offs);
|
||||
points[9].y = WXROUND(point1->y) + width;
|
||||
|
||||
points[2].x = WXROUND(point2->x + offs);
|
||||
points[2].y = WXROUND(point2->y) - width;
|
||||
points[8].x = WXROUND(point2->x - offs);
|
||||
points[8].y = WXROUND(point2->y) + width;
|
||||
|
||||
points[3].x = WXROUND(point3->x) - width - ARROWWIDTH;
|
||||
points[3].y = WXROUND(point3->y) - width;
|
||||
points[7].x = WXROUND(point3->x) - width - ARROWWIDTH;
|
||||
points[7].y = WXROUND(point3->y) + width;
|
||||
|
||||
points[4].x = points[3].x;
|
||||
points[4].y = points[3].y - ARROWWIDTH;
|
||||
points[6].x = points[7].x;
|
||||
points[6].y = points[7].y + ARROWWIDTH;
|
||||
|
||||
points[5].x = WXROUND(point3->x);
|
||||
points[5].y = WXROUND(point3->y);
|
||||
|
||||
dc.DrawPolygon(11, points, 0, 0);
|
||||
}
|
||||
}
|
||||
42
ctl/module.mk
Normal file
42
ctl/module.mk
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/ctl/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
ctl/calbox.cpp \
|
||||
ctl/ctlAuiNotebook.cpp \
|
||||
ctl/ctlCheckTreeView.cpp \
|
||||
ctl/ctlColourPicker.cpp \
|
||||
ctl/ctlComboBox.cpp \
|
||||
ctl/ctlListView.cpp \
|
||||
ctl/ctlMenuToolbar.cpp \
|
||||
ctl/ctlSQLBox.cpp \
|
||||
ctl/ctlSQLGrid.cpp \
|
||||
ctl/ctlSQLResult.cpp \
|
||||
ctl/ctlDefaultSecurityPanel.cpp \
|
||||
ctl/ctlSeclabelPanel.cpp \
|
||||
ctl/ctlSecurityPanel.cpp \
|
||||
ctl/ctlTree.cpp \
|
||||
ctl/ctlProgressStatusBar.cpp \
|
||||
ctl/explainCanvas.cpp \
|
||||
ctl/explainShape.cpp \
|
||||
ctl/timespin.cpp \
|
||||
ctl/xh_calb.cpp \
|
||||
ctl/xh_ctlcolourpicker.cpp \
|
||||
ctl/xh_ctlcombo.cpp \
|
||||
ctl/xh_ctlchecktreeview.cpp \
|
||||
ctl/xh_ctltree.cpp \
|
||||
ctl/xh_sqlbox.cpp \
|
||||
ctl/xh_timespin.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
ctl/module.mk
|
||||
|
||||
|
||||
461
ctl/timespin.cpp
Normal file
461
ctl/timespin.cpp
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// timespin.cpp - timeSpin SpinCtrl
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "ctl/timespin.h"
|
||||
|
||||
|
||||
#define CTRLID_TXT 101
|
||||
#define CTRLID_SPN 102
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(wxTimeSpinCtrl, wxControl)
|
||||
EVT_TEXT(CTRLID_TXT, wxTimeSpinCtrl::OnText)
|
||||
EVT_NAVIGATION_KEY(wxTimeSpinCtrl::OnNavigate)
|
||||
EVT_SPIN_UP(CTRLID_SPN, wxTimeSpinCtrl::OnSpinUp)
|
||||
EVT_SPIN_DOWN(CTRLID_SPN, wxTimeSpinCtrl::OnSpinDown)
|
||||
EVT_SPIN(CTRLID_SPN, wxTimeSpinCtrl::OnSpin)
|
||||
EVT_SET_FOCUS(wxTimeSpinCtrl::OnSetFocus)
|
||||
EVT_SIZE(wxTimeSpinCtrl::OnSize)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxTimeSpinCtrl, wxControl)
|
||||
|
||||
|
||||
wxTimeSpinCtrl::wxTimeSpinCtrl(wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxPoint &pos,
|
||||
const wxSize &size,
|
||||
long style,
|
||||
const wxString &name)
|
||||
{
|
||||
Init();
|
||||
Create(parent, id, pos, size, style, name);
|
||||
}
|
||||
|
||||
|
||||
bool wxTimeSpinCtrl::Create(wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxPoint &pos,
|
||||
const wxSize &size,
|
||||
long style,
|
||||
const wxString &name)
|
||||
{
|
||||
wxControl::Create(parent, id, pos, size, style & ~(wxSP_WRAP | wxSP_ARROW_KEYS), wxDefaultValidator, name);
|
||||
SetFont(parent->GetFont());
|
||||
|
||||
m_spn = new wxSpinButton(this, CTRLID_SPN, wxDefaultPosition, wxDefaultSize, wxSP_WRAP | wxSP_VERTICAL | (style & wxSP_ARROW_KEYS));
|
||||
|
||||
wxSize cs = GetClientSize();
|
||||
wxSize ss = m_spn->GetBestSize();
|
||||
|
||||
m_txt = new wxTextCtrl(this, CTRLID_TXT, wxEmptyString, wxPoint(0, 0), wxSize(cs.x - ss.x, cs.y), wxWANTS_CHARS);
|
||||
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(wxTimeSpinCtrl::OnEditKey), 0, this);
|
||||
m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS, wxFocusEventHandler(wxTimeSpinCtrl::OnKillFocus), 0, this);
|
||||
|
||||
wxArrayString valArray;
|
||||
wxChar c;
|
||||
for (c = '0'; c <= '9'; c++)
|
||||
valArray.Add(wxString(c, 1));
|
||||
valArray.Add(wxT(":"));
|
||||
wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
|
||||
tv.SetIncludes(valArray);
|
||||
m_txt->SetValidator(tv);
|
||||
|
||||
m_spn->SetSize(ss.x, cs.y);
|
||||
m_spn->SetPosition(wxPoint(cs.x - ss.x, 0));
|
||||
|
||||
canWrap = (style & wxSP_WRAP) != 0;
|
||||
SetMax(24 * 60 * 60 - 1);
|
||||
|
||||
SetInitialSize(size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TXTPOSX 0
|
||||
#define TXTPOSY 0
|
||||
void wxTimeSpinCtrl::OnSize(wxSizeEvent &event)
|
||||
{
|
||||
if ( m_spn )
|
||||
{
|
||||
wxSize sz = GetClientSize();
|
||||
|
||||
wxSize ss = m_spn->GetSize();
|
||||
int eh = m_txt->GetBestSize().y;
|
||||
|
||||
m_txt->SetSize(TXTPOSX, TXTPOSY, sz.x - ss.x - TXTPOSX, sz.y > eh ? eh - TXTPOSY : sz.y - TXTPOSY);
|
||||
m_spn->SetSize(sz.x - ss.x, 0, ss.x, sz.y);
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
bool wxTimeSpinCtrl::Destroy()
|
||||
{
|
||||
if (m_txt)
|
||||
{
|
||||
m_txt->Destroy();
|
||||
m_txt = NULL;
|
||||
}
|
||||
if (m_spn)
|
||||
{
|
||||
m_spn->Destroy();
|
||||
m_spn = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::Init()
|
||||
{
|
||||
m_txt = NULL;
|
||||
m_spn = NULL;
|
||||
spinValue = 0;
|
||||
}
|
||||
|
||||
|
||||
wxSize wxTimeSpinCtrl::DoGetBestSize() const
|
||||
{
|
||||
#ifdef __WXGTK__
|
||||
return wxSize(100, m_spn->GetBestSize().y);
|
||||
#else
|
||||
return wxSize(100, m_txt->GetBestSize().y);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool wxTimeSpinCtrl::Enable(bool enable)
|
||||
{
|
||||
m_txt->Enable(enable);
|
||||
m_spn->Enable(enable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wxTimeSpinCtrl::SetMax(long seconds, bool useDay)
|
||||
{
|
||||
hasDay = useDay;
|
||||
if (hasDay)
|
||||
m_format = wxT("%D:%H:%M:%S");
|
||||
else
|
||||
m_format = wxT("%H:%M:%S");
|
||||
maxSpinValue = seconds + 1;
|
||||
if (maxSpinValue < 2)
|
||||
maxSpinValue = 2;
|
||||
m_spn->SetRange(0, 32767);
|
||||
}
|
||||
|
||||
|
||||
bool wxTimeSpinCtrl::SetTime(const wxDateTime &time)
|
||||
{
|
||||
if (time.IsValid())
|
||||
{
|
||||
wxDateTime tt(time);
|
||||
tt.ResetTime();
|
||||
return SetValue(time - tt);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_txt->SetValue(wxEmptyString);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool wxTimeSpinCtrl::SetValue(const wxTimeSpan &span)
|
||||
{
|
||||
m_txt->SetValue(span.Format(m_format));
|
||||
spinValue = span.GetSeconds().GetLo();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
wxTimeSpan wxTimeSpinCtrl::GetValue()
|
||||
{
|
||||
return wxTimeSpan(0, 0, spinValue);
|
||||
}
|
||||
|
||||
|
||||
int wxTimeSpinCtrl::GetTimePart()
|
||||
{
|
||||
wxString strAfter = m_txt->GetRange(m_txt->GetInsertionPoint(), 9999);
|
||||
int cnt = 0;
|
||||
const wxChar *p = (const wxChar *)strAfter;
|
||||
while (*p)
|
||||
{
|
||||
if (*p++ == ':')
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::Highlight(int tp)
|
||||
{
|
||||
wxString txt = m_txt->GetValue();
|
||||
long from = 0, to;
|
||||
int i;
|
||||
|
||||
for (i = 3 - tp - (hasDay ? 0 : 1) ; i > 0 ; i--)
|
||||
from += txt.Mid(from).Find(':') + 1;
|
||||
|
||||
to = txt.Mid(from).Find(':') + from;
|
||||
if (to < from)
|
||||
to = 999;
|
||||
|
||||
m_txt->SetSelection(from, to);
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnSpinUp(wxSpinEvent &ev)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
m_txt->SetFocus();
|
||||
#endif
|
||||
DoSpin(1);
|
||||
}
|
||||
|
||||
void wxTimeSpinCtrl::OnSpinDown(wxSpinEvent &ev)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
m_txt->SetFocus();
|
||||
#endif
|
||||
DoSpin(-1);
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnSpin(wxSpinEvent &ev)
|
||||
{
|
||||
wxCommandEvent parentSpinEvt(ev.GetEventType(), this->GetId());
|
||||
this->GetEventHandler()->AddPendingEvent(parentSpinEvt);
|
||||
}
|
||||
|
||||
void wxTimeSpinCtrl::DoSpin(int diff)
|
||||
{
|
||||
int tp = GetTimePart();
|
||||
long oldValue = 0, maxValue = 0, mult = 0;
|
||||
|
||||
switch (tp)
|
||||
{
|
||||
case 0:
|
||||
oldValue = spinValue;
|
||||
maxValue = 60;
|
||||
mult = 1;
|
||||
break;
|
||||
case 1:
|
||||
oldValue = spinValue / 60;
|
||||
maxValue = 60;
|
||||
mult = 60;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
oldValue = spinValue / 60 / 60;
|
||||
if (hasDay)
|
||||
maxValue = 24;
|
||||
else
|
||||
maxValue = maxSpinValue / 60 / 60;
|
||||
mult = 60 * 60;
|
||||
break;
|
||||
case 3:
|
||||
oldValue = spinValue / 60 / 60 / 24;
|
||||
maxValue = maxSpinValue / 60 / 60 / 24;
|
||||
mult = 60 * 60 * 24;
|
||||
break;
|
||||
default:
|
||||
// can't happen
|
||||
break;
|
||||
}
|
||||
oldValue %= maxValue;
|
||||
int newValue = oldValue + diff;
|
||||
|
||||
if (!canWrap)
|
||||
{
|
||||
if (newValue < 0)
|
||||
diff += maxValue;
|
||||
else if (newValue >= maxValue)
|
||||
diff -= maxValue;
|
||||
}
|
||||
|
||||
long newSpinValue = spinValue + diff * mult;
|
||||
if (newSpinValue < 0)
|
||||
newSpinValue = maxSpinValue - 1;
|
||||
else if (newSpinValue > maxSpinValue)
|
||||
newSpinValue = 0;
|
||||
|
||||
if (spinValue != newSpinValue)
|
||||
{
|
||||
spinValue = newSpinValue;
|
||||
wxTimeSpan span(0, 0, spinValue);
|
||||
wxString txt = span.Format(m_format);
|
||||
m_txt->SetValue(txt);
|
||||
Highlight(tp);
|
||||
|
||||
wxSpinEvent ev;
|
||||
ev.SetEventObject(this);
|
||||
ev.SetId(GetId());
|
||||
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
GetParent()->GetEventHandler()->ProcessEvent(ev);
|
||||
#else
|
||||
GetParent()->ProcessEvent(ev);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnText(wxCommandEvent &ev)
|
||||
{
|
||||
long time = GetTextTime();
|
||||
if (time >= 0)
|
||||
{
|
||||
spinValue = time;
|
||||
ev.SetEventObject(this);
|
||||
ev.SetId(GetId());
|
||||
#if wxCHECK_VERSION(2, 9, 0)
|
||||
GetParent()->GetEventHandler()->ProcessEvent(ev);
|
||||
#else
|
||||
GetParent()->ProcessEvent(ev);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnNavigate(wxNavigationKeyEvent &ev)
|
||||
{
|
||||
if (wxWindow::FindFocus() == m_txt)
|
||||
{
|
||||
int tp = GetTimePart();
|
||||
if (ev.GetDirection())
|
||||
tp++;
|
||||
else
|
||||
tp--;
|
||||
if ((tp >= 0 && tp < 3) || (hasDay && tp == 3))
|
||||
{
|
||||
Highlight(tp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnEditKey(wxKeyEvent &ev)
|
||||
{
|
||||
if (!ev.HasModifiers())
|
||||
{
|
||||
switch(ev.GetKeyCode())
|
||||
{
|
||||
case WXK_LEFT:
|
||||
{
|
||||
if (!ev.ShiftDown())
|
||||
{
|
||||
int tp = GetTimePart() + 1;
|
||||
if (tp < 3 || (hasDay && tp == 3))
|
||||
{
|
||||
Highlight(tp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WXK_RIGHT:
|
||||
{
|
||||
if (!ev.ShiftDown())
|
||||
{
|
||||
int tp = GetTimePart() - 1;
|
||||
if (tp >= 0)
|
||||
{
|
||||
Highlight(tp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WXK_UP:
|
||||
DoSpin(1);
|
||||
return;
|
||||
break;
|
||||
case WXK_DOWN:
|
||||
DoSpin(-1);
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ev.Skip();
|
||||
}
|
||||
|
||||
|
||||
long wxTimeSpinCtrl::GetTextTime()
|
||||
{
|
||||
int t1, t2, t3, t4;
|
||||
int scanned = wxSscanf(m_txt->GetValue(), wxT("%d:%d:%d:%d"), &t1, &t2, &t3, &t4);
|
||||
|
||||
switch (scanned)
|
||||
{
|
||||
case 1:
|
||||
if (t1 >= 0)
|
||||
return t1;
|
||||
break;
|
||||
case 2:
|
||||
if (t1 >= 0 && t2 >= 0 && t2 < 60)
|
||||
return t1 * 60 + t2;
|
||||
break;
|
||||
case 3:
|
||||
if (t1 >= 0 && t2 >= 0 && t2 < 60 && t3 >= 0 && t3 < 60)
|
||||
return (t1 * 60 + t2) * 60 + t3;
|
||||
break;
|
||||
case 4:
|
||||
if (t1 >= 0 && t2 >= 0 && t2 < 24 && t3 >= 0 && t3 < 60 && t4 >= 0 && t4 < 60)
|
||||
return ((t1 * 24 + t2) * 60 + t3) * 60 + t4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnKillFocus(wxFocusEvent &ev)
|
||||
{
|
||||
#ifdef __WXGTK__
|
||||
ev.Skip();
|
||||
#endif
|
||||
|
||||
long time = GetTextTime();
|
||||
|
||||
if (time < 0)
|
||||
{
|
||||
m_txt->SetValue(wxEmptyString);
|
||||
spinValue = 0;
|
||||
m_spn->SetValue(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int tp = GetTimePart();
|
||||
SetValue(wxTimeSpan(0, 0, time));
|
||||
Highlight(tp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void wxTimeSpinCtrl::OnSetFocus(wxFocusEvent &ev)
|
||||
{
|
||||
m_txt->SetFocus();
|
||||
Highlight(hasDay ? 3 : 2);
|
||||
}
|
||||
77
ctl/xh_calb.cpp
Normal file
77
ctl/xh_calb.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_calb.cpp - wxCalendarBox handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/xh_calb.h"
|
||||
#include "ctl/calbox.h"
|
||||
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxCalendarBoxXmlHandler, wxXmlResourceHandler)
|
||||
|
||||
wxCalendarBoxXmlHandler::wxCalendarBoxXmlHandler()
|
||||
: wxXmlResourceHandler()
|
||||
{
|
||||
/*
|
||||
* Only available with the wxDatePickerCtrl
|
||||
*/
|
||||
XRC_ADD_STYLE(wxDP_DEFAULT);
|
||||
XRC_ADD_STYLE(wxDP_SPIN);
|
||||
XRC_ADD_STYLE(wxDP_DROPDOWN);
|
||||
XRC_ADD_STYLE(wxDP_ALLOWNONE);
|
||||
XRC_ADD_STYLE(wxDP_SHOWCENTURY);
|
||||
|
||||
AddWindowStyles();
|
||||
}
|
||||
|
||||
|
||||
wxObject *wxCalendarBoxXmlHandler::DoCreateResource()
|
||||
{
|
||||
XRC_MAKE_INSTANCE(calendar, wxCalendarBox);
|
||||
|
||||
#if pgUSE_WX_CAL
|
||||
calendar->Create(m_parentAsWindow,
|
||||
GetID(),
|
||||
wxDefaultDateTime,
|
||||
GetPosition(), GetSize(),
|
||||
wxDP_DEFAULT | wxDP_SHOWCENTURY | wxDP_ALLOWNONE,
|
||||
wxDefaultValidator,
|
||||
GetName());
|
||||
|
||||
#else // pgUSE_WX_CAL
|
||||
#if !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
|
||||
calendar->Create(m_parentAsWindow,
|
||||
GetID(),
|
||||
wxDefaultDateTime,
|
||||
GetPosition(), GetSize(),
|
||||
GetStyle(),
|
||||
GetName());
|
||||
#else // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
|
||||
calendar->Create(m_parentAsWindow,
|
||||
(wxWindowID)GetID(),
|
||||
wxDefaultDateTime,
|
||||
GetPosition(), GetSize(),
|
||||
(long int)GetStyle(),
|
||||
wxDefaultValidator,
|
||||
GetName());
|
||||
#endif // !defined(wxUSE_DATEPICKCTRL) || !wxUSE_DATEPICKCTRL
|
||||
#endif // pgUSE_WX_CAL
|
||||
|
||||
SetupWindow(calendar);
|
||||
|
||||
return calendar;
|
||||
}
|
||||
|
||||
bool wxCalendarBoxXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("wxCalendarBox"));
|
||||
}
|
||||
33
ctl/xh_ctlchecktreeview.cpp
Normal file
33
ctl/xh_ctlchecktreeview.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_ctlchecktreeview.cpp - ctlCheckTreeView handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/ctlCheckTreeView.h"
|
||||
#include "ctl/xh_ctlchecktreeview.h"
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlCheckTreeViewXmlHandler, wxTreeCtrlXmlHandler)
|
||||
|
||||
|
||||
wxObject *ctlCheckTreeViewXmlHandler::DoCreateResource()
|
||||
{
|
||||
ctlCheckTreeView *ctl = new ctlCheckTreeView(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
|
||||
|
||||
SetupWindow(ctl);
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
bool ctlCheckTreeViewXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("ctlCheckTreeView"));
|
||||
}
|
||||
33
ctl/xh_ctlcolourpicker.cpp
Normal file
33
ctl/xh_ctlcolourpicker.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the BSD Licence
|
||||
//
|
||||
// xh_ctlcolourpicker.cpp - ctlColourPicker handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/ctlColourPicker.h"
|
||||
#include "ctl/xh_ctlcolourpicker.h"
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlColourPickerXmlHandler, wxBitmapButtonXmlHandler)
|
||||
|
||||
|
||||
wxObject *ctlColourPickerXmlHandler::DoCreateResource()
|
||||
{
|
||||
ctlColourPicker *ctl = new ctlColourPicker(m_parentAsWindow, GetID(), GetPosition(), GetSize());
|
||||
|
||||
SetupWindow(ctl);
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
bool ctlColourPickerXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("ctlColourPicker"));
|
||||
}
|
||||
33
ctl/xh_ctlcombo.cpp
Normal file
33
ctl/xh_ctlcombo.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_ctlcombo.cpp - ctlComboBox handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/ctlComboBox.h"
|
||||
#include "ctl/xh_ctlcombo.h"
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlComboBoxXmlHandler, wxComboBoxXmlHandler)
|
||||
|
||||
|
||||
wxObject *ctlComboBoxXmlHandler::DoCreateResource()
|
||||
{
|
||||
ctlComboBox *ctl = new ctlComboBox(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
|
||||
|
||||
SetupWindow(ctl);
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
bool ctlComboBoxXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("ctlComboBox"));
|
||||
}
|
||||
35
ctl/xh_ctltree.cpp
Normal file
35
ctl/xh_ctltree.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_ctltree.cpp - ctlTree handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
|
||||
#include "utils/misc.h"
|
||||
#include "ctl/ctlTree.h"
|
||||
#include "ctl/xh_ctltree.h"
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlTreeXmlHandler, wxTreeCtrlXmlHandler)
|
||||
|
||||
|
||||
wxObject *ctlTreeXmlHandler::DoCreateResource()
|
||||
{
|
||||
ctlTree *ctl = new ctlTree(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
|
||||
|
||||
SetupWindow(ctl);
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
bool ctlTreeXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("ctlTree"));
|
||||
}
|
||||
45
ctl/xh_sqlbox.cpp
Normal file
45
ctl/xh_sqlbox.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_sqlbox.cpp - ctlSQLBox handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/xh_sqlbox.h"
|
||||
#include "ctl/ctlSQLBox.h"
|
||||
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(ctlSQLBoxXmlHandler, wxXmlResourceHandler)
|
||||
|
||||
ctlSQLBoxXmlHandler::ctlSQLBoxXmlHandler()
|
||||
: wxXmlResourceHandler()
|
||||
{
|
||||
XRC_ADD_STYLE(wxTE_MULTILINE);
|
||||
XRC_ADD_STYLE(wxSIMPLE_BORDER);
|
||||
XRC_ADD_STYLE(wxSUNKEN_BORDER);
|
||||
XRC_ADD_STYLE(wxTE_RICH2);
|
||||
|
||||
AddWindowStyles();
|
||||
}
|
||||
|
||||
|
||||
wxObject *ctlSQLBoxXmlHandler::DoCreateResource()
|
||||
{
|
||||
ctlSQLBox *sqlbox = new ctlSQLBox(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle());
|
||||
|
||||
SetupWindow(sqlbox);
|
||||
|
||||
return sqlbox;
|
||||
}
|
||||
|
||||
bool ctlSQLBoxXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("ctlSQLBox"));
|
||||
}
|
||||
49
ctl/xh_timespin.cpp
Normal file
49
ctl/xh_timespin.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// xh_timespin.cpp - wxTimeSpinCtrl handler
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
#include "wx/wx.h"
|
||||
#include "ctl/timespin.h"
|
||||
#include "ctl/xh_timespin.h"
|
||||
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxTimeSpinXmlHandler, wxXmlResourceHandler)
|
||||
|
||||
wxTimeSpinXmlHandler::wxTimeSpinXmlHandler()
|
||||
: wxXmlResourceHandler()
|
||||
{
|
||||
XRC_ADD_STYLE(wxSP_WRAP);
|
||||
XRC_ADD_STYLE(wxSP_ARROW_KEYS);
|
||||
|
||||
AddWindowStyles();
|
||||
}
|
||||
|
||||
|
||||
wxObject *wxTimeSpinXmlHandler::DoCreateResource()
|
||||
{
|
||||
XRC_MAKE_INSTANCE(timespin, wxTimeSpinCtrl);
|
||||
|
||||
timespin->Create(m_parentAsWindow,
|
||||
GetID(),
|
||||
GetPosition(), GetSize(),
|
||||
GetStyle(),
|
||||
GetName());
|
||||
|
||||
SetupWindow(timespin);
|
||||
|
||||
return timespin;
|
||||
}
|
||||
|
||||
bool wxTimeSpinXmlHandler::CanHandle(wxXmlNode *node)
|
||||
{
|
||||
return IsOfClass(node, wxT("wxTimeSpinCtrl"));
|
||||
}
|
||||
152
db/keywords.c
Normal file
152
db/keywords.c
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* keywords.c
|
||||
* lexical token lookup for reserved words in PostgreSQL
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.177 2006/10/07 21:51:02 petere Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin note: This file is based on src/backend/parser/keywords.c and
|
||||
// src/backend/parser/kwlookup.c from PostgreSQL, but extended
|
||||
// to support EntepriseDB and Greenplum.
|
||||
//
|
||||
// This file is under the BSD licence, per PostgreSQL.
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "postgres.h"
|
||||
#include "parser/keywords.h"
|
||||
|
||||
/*
|
||||
* List of (keyword-name, keyword-token-value) pairs.
|
||||
*/
|
||||
#define PG_KEYWORD(a,b,c) {a,c},
|
||||
const ScanKeyword ScanKeywords[] = {
|
||||
#include <parser/kwlist.h>
|
||||
};
|
||||
const int NumScanKeywords = lengthof(ScanKeywords);
|
||||
|
||||
/*
|
||||
* Additional pairs here. They need to live in a separate array since
|
||||
* the ScanKeywords array needs to be sorted!
|
||||
*
|
||||
* !!WARNING!!: This list must be sorted, because binary
|
||||
* search is used to locate entries.
|
||||
*/
|
||||
#define PG_KEYWORD2(a,b) {a,b},
|
||||
const ScanKeyword ScanKeywordsExtra[] = {
|
||||
PG_KEYWORD2("connect", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("convert", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("distributed", UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD2("exec", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("log", UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD2("long", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("minus", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("nocache", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("number", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("package", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("pls_integer", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("raw", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("return", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("smalldatetime", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("smallfloat", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("smallmoney", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("sysdate", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("systimestap", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("tinyint", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("tinytext", RESERVED_KEYWORD)
|
||||
PG_KEYWORD2("varchar2", RESERVED_KEYWORD)
|
||||
};
|
||||
const int NumScanKeywordsExtra = lengthof(ScanKeywordsExtra);
|
||||
|
||||
/*
|
||||
* ScanKeywordLookup - see if a given word is a keyword
|
||||
*
|
||||
* Returns a pointer to the ScanKeyword table entry, or NULL if no match.
|
||||
*
|
||||
* The match is done case-insensitively. Note that we deliberately use a
|
||||
* dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z',
|
||||
* even if we are in a locale where tolower() would produce more or different
|
||||
* translations. This is to conform to the SQL99 spec, which says that
|
||||
* keywords are to be matched in this way even though non-keyword identifiers
|
||||
* receive a different case-normalization mapping.
|
||||
*/
|
||||
const ScanKeyword *
|
||||
ScanKeywordLookup(const char *text)
|
||||
{
|
||||
int len,
|
||||
i;
|
||||
char word[NAMEDATALEN];
|
||||
const ScanKeyword *low;
|
||||
const ScanKeyword *high;
|
||||
|
||||
len = strlen(text);
|
||||
/* We assume all keywords are shorter than NAMEDATALEN. */
|
||||
if (len >= NAMEDATALEN)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Apply an ASCII-only downcasing. We must not use tolower() since it may
|
||||
* produce the wrong translation in some locales (eg, Turkish).
|
||||
*/
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
char ch = text[i];
|
||||
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
ch += 'a' - 'A';
|
||||
word[i] = ch;
|
||||
}
|
||||
word[len] = '\0';
|
||||
|
||||
/*
|
||||
* Now do a binary search using plain strcmp() comparison.
|
||||
*/
|
||||
low = &ScanKeywords[0];
|
||||
high = endof(ScanKeywords) - 1;
|
||||
while (low <= high)
|
||||
{
|
||||
const ScanKeyword *middle;
|
||||
int difference;
|
||||
|
||||
middle = low + (high - low) / 2;
|
||||
difference = strcmp(middle->name, word);
|
||||
if (difference == 0)
|
||||
return middle;
|
||||
else if (difference < 0)
|
||||
low = middle + 1;
|
||||
else
|
||||
high = middle - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If not found, also do a binary search in the list of extra
|
||||
* keywords.
|
||||
*/
|
||||
low = &ScanKeywordsExtra[0];
|
||||
high = endof(ScanKeywordsExtra) - 1;
|
||||
while (low <= high)
|
||||
{
|
||||
const ScanKeyword *middle;
|
||||
int difference;
|
||||
|
||||
middle = low + (high - low) / 2;
|
||||
difference = strcmp(middle->name, word);
|
||||
if (difference == 0)
|
||||
return middle;
|
||||
else if (difference < 0)
|
||||
low = middle + 1;
|
||||
else
|
||||
high = middle - 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
20
db/module.mk
Normal file
20
db/module.mk
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/db/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
db/keywords.c \
|
||||
db/pgConn.cpp \
|
||||
db/pgSet.cpp \
|
||||
db/pgQueryThread.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
db/module.mk
|
||||
|
||||
1334
db/pgConn.cpp
Normal file
1334
db/pgConn.cpp
Normal file
File diff suppressed because it is too large
Load diff
901
db/pgQueryThread.cpp
Normal file
901
db/pgQueryThread.cpp
Normal file
|
|
@ -0,0 +1,901 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// pgQueryThread.cpp - PostgreSQL threaded query class
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// PostgreSQL headers
|
||||
#include <libpq-fe.h>
|
||||
|
||||
// App headers
|
||||
#include "db/pgSet.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgQueryThread.h"
|
||||
#include "db/pgQueryResultEvent.h"
|
||||
#include "utils/pgDefs.h"
|
||||
#include "utils/sysLogger.h"
|
||||
|
||||
const wxEventType PGQueryResultEvent = wxNewEventType();
|
||||
|
||||
// default notice processor for the pgQueryThread
|
||||
// we do assume that the argument passed will be always the
|
||||
// object of pgQueryThread
|
||||
void pgNoticeProcessor(void *_arg, const char *_message)
|
||||
{
|
||||
wxString str(_message, wxConvUTF8);
|
||||
|
||||
wxLogNotice(wxT("%s"), str.Trim().c_str());
|
||||
if (_arg)
|
||||
{
|
||||
((pgQueryThread *)_arg)->AppendMessage(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// support for multiple queries support
|
||||
pgQueryThread::pgQueryThread(pgConn *_conn, wxEvtHandler *_caller,
|
||||
PQnoticeProcessor _processor, void *_noticeHandler) :
|
||||
wxThread(wxTHREAD_JOINABLE), m_currIndex(-1), m_conn(_conn),
|
||||
m_cancelled(false), m_multiQueries(true), m_useCallable(false),
|
||||
m_caller(_caller), m_processor(pgNoticeProcessor), m_noticeHandler(NULL),
|
||||
m_eventOnCancellation(true)
|
||||
{
|
||||
// check if we can really use the enterprisedb callable statement and
|
||||
// required
|
||||
#ifdef __WXMSW__
|
||||
if (PQiGetOutResult && PQiPrepareOut && PQiSendQueryPreparedOut)
|
||||
{
|
||||
// we do not need all of pqi stuff as90 onwards
|
||||
if (m_conn && m_conn->conn && m_conn->GetIsEdb()
|
||||
&& !m_conn->EdbMinimumVersion(9, 0))
|
||||
{
|
||||
m_useCallable = true;
|
||||
}
|
||||
}
|
||||
#else // __WXMSW__
|
||||
#ifdef EDB_LIBPQ
|
||||
// use callable statement only with enterprisedb advanced servers
|
||||
// we do not need all of pqi stuff as90 onwards
|
||||
if (m_conn && m_conn->conn && m_conn->GetIsEdb()
|
||||
&& !m_conn->EdbMinimumVersion(9, 0))
|
||||
{
|
||||
m_useCallable = true;
|
||||
}
|
||||
#endif // EDB_LIBPQ
|
||||
#endif // __WXMSW__
|
||||
|
||||
if (m_conn && m_conn->conn)
|
||||
{
|
||||
PQsetnonblocking(m_conn->conn, 1);
|
||||
}
|
||||
|
||||
if (_processor != NULL)
|
||||
{
|
||||
m_processor = _processor;
|
||||
if (_noticeHandler)
|
||||
m_noticeHandler = _noticeHandler;
|
||||
else
|
||||
m_noticeHandler = (void *)this;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_noticeHandler = (void *)this;
|
||||
}
|
||||
}
|
||||
|
||||
pgQueryThread::pgQueryThread(pgConn *_conn, const wxString &_qry,
|
||||
int _resultToRetrieve, wxWindow *_caller, long _eventId, void *_data)
|
||||
: wxThread(wxTHREAD_JOINABLE), m_currIndex(-1), m_conn(_conn),
|
||||
m_cancelled(false), m_multiQueries(false), m_useCallable(false),
|
||||
m_caller(NULL), m_processor(pgNoticeProcessor), m_noticeHandler(NULL),
|
||||
m_eventOnCancellation(true)
|
||||
{
|
||||
if (m_conn && m_conn->conn)
|
||||
{
|
||||
PQsetnonblocking(m_conn->conn, 1);
|
||||
}
|
||||
m_queries.Add(
|
||||
new pgBatchQuery(_qry, (pgParamsArray *)NULL, _eventId, _data, false,
|
||||
_resultToRetrieve));
|
||||
|
||||
wxLogInfo(wxT("queueing : %s"), _qry.c_str());
|
||||
m_noticeHandler = (void *)this;
|
||||
|
||||
if (_caller)
|
||||
{
|
||||
m_caller = _caller->GetEventHandler();
|
||||
}
|
||||
}
|
||||
|
||||
void pgQueryThread::SetEventOnCancellation(bool eventOnCancelled)
|
||||
{
|
||||
m_eventOnCancellation = eventOnCancelled;
|
||||
}
|
||||
|
||||
void pgQueryThread::AddQuery(const wxString &_qry, pgParamsArray *_params,
|
||||
long _eventId, void *_data, bool _useCallable, int _resultToRetrieve)
|
||||
{
|
||||
m_queries.Add(
|
||||
new pgBatchQuery(_qry, _params, _eventId, _data,
|
||||
// use callable statement only if supported
|
||||
m_useCallable && _useCallable, _resultToRetrieve));
|
||||
|
||||
wxLogInfo(wxT("queueing (%ld): %s"), GetId(), _qry.c_str());
|
||||
}
|
||||
|
||||
|
||||
pgQueryThread::~pgQueryThread()
|
||||
{
|
||||
m_conn->RegisterNoticeProcessor(0, 0);
|
||||
WX_CLEAR_ARRAY(m_queries);
|
||||
}
|
||||
|
||||
|
||||
wxString pgQueryThread::GetMessagesAndClear(int idx)
|
||||
{
|
||||
wxString msg;
|
||||
|
||||
if (idx == -1)
|
||||
idx = m_currIndex;
|
||||
|
||||
if (idx >= 0 && idx <= m_currIndex)
|
||||
{
|
||||
wxCriticalSectionLocker lock(m_criticalSection);
|
||||
msg = m_queries[idx]->m_message;
|
||||
m_queries[idx]->m_message = wxT("");
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
void pgQueryThread::AppendMessage(const wxString &str)
|
||||
{
|
||||
if (m_queries[m_currIndex]->m_message.IsEmpty())
|
||||
{
|
||||
wxCriticalSectionLocker lock(m_criticalSection);
|
||||
if (str != wxT("\n"))
|
||||
m_queries[m_currIndex]->m_message.Append(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxCriticalSectionLocker lock(m_criticalSection);
|
||||
m_queries[m_currIndex]->m_message.Append(wxT("\n")).Append(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int pgQueryThread::Execute()
|
||||
{
|
||||
wxMutexLocker lock(m_queriesLock);
|
||||
|
||||
PGresult *result = NULL;
|
||||
wxMBConv &conv = *(m_conn->conv);
|
||||
|
||||
wxString &query = m_queries[m_currIndex]->m_query;
|
||||
int &resultToRetrieve = m_queries[m_currIndex]->m_resToRetrieve;
|
||||
long &rowsInserted = m_queries[m_currIndex]->m_rowsInserted;
|
||||
Oid &insertedOid = m_queries[m_currIndex]->m_insertedOid;
|
||||
// using the alias for the pointer here, in order to save the result back
|
||||
// in the pgBatchQuery object
|
||||
pgSet *&dataSet = m_queries[m_currIndex]->m_resultSet;
|
||||
int &rc = m_queries[m_currIndex]->m_returnCode;
|
||||
pgParamsArray *params = m_queries[m_currIndex]->m_params;
|
||||
bool useCallable = m_queries[m_currIndex]->m_useCallable;
|
||||
pgError &err = m_queries[m_currIndex]->m_err;
|
||||
|
||||
wxCharBuffer queryBuf = query.mb_str(conv);
|
||||
|
||||
if (PQstatus(m_conn->conn) != CONNECTION_OK)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_CONN_LOST;
|
||||
err.msg_primary = _("Connection to the database server lost");
|
||||
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
|
||||
if (!queryBuf && !query.IsEmpty())
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_STRING_INVALID;
|
||||
m_conn->SetLastResultError(NULL, _("the query could not be converted to the required encoding."));
|
||||
err.msg_primary = _("Query string is empty");
|
||||
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
|
||||
// Honour the parameters (if any)
|
||||
if (params && params->GetCount() > 0)
|
||||
{
|
||||
int pCount = params->GetCount();
|
||||
int ret = 0,
|
||||
idx = 0;
|
||||
|
||||
Oid *pOids = (Oid *)malloc(pCount * sizeof(Oid));
|
||||
const char **pParams = (const char **)malloc(pCount * sizeof(const char *));
|
||||
int *pLens = (int *)malloc(pCount * sizeof(int));
|
||||
int *pFormats = (int *)malloc(pCount * sizeof(int));
|
||||
// modes are used only by enterprisedb callable statement
|
||||
#if defined (__WXMSW__) || (EDB_LIBPQ)
|
||||
int *pModes = (int *)malloc(pCount * sizeof(int));
|
||||
#endif
|
||||
|
||||
for (; idx < pCount; idx++)
|
||||
{
|
||||
pgParam *param = (*params)[idx];
|
||||
|
||||
pOids[idx] = param->m_type;
|
||||
pParams[idx] = (const char *)param->m_val;
|
||||
pLens[idx] = param->m_len;
|
||||
pFormats[idx] = param->GetFormat();
|
||||
#if defined (__WXMSW__) || (EDB_LIBPQ)
|
||||
pModes[idx] = param->m_mode;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (useCallable)
|
||||
{
|
||||
#if defined (__WXMSW__) || (EDB_LIBPQ)
|
||||
wxLogInfo(wxString::Format(
|
||||
_("using an enterprisedb callable statement (queryid:%ld, threadid:%ld)"),
|
||||
(long)m_currIndex, (long)GetId()));
|
||||
wxString stmt = wxString::Format(wxT("pgQueryThread-%ld-%ld"), this->GetId(), m_currIndex);
|
||||
PGresult *res = PQiPrepareOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
|
||||
queryBuf, pCount, pOids, pModes);
|
||||
|
||||
if( PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE;
|
||||
err.SetError(res, &conv);
|
||||
|
||||
PQclear(res);
|
||||
|
||||
goto return_with_error;
|
||||
}
|
||||
|
||||
ret = PQiSendQueryPreparedOut(m_conn->conn, stmt.mb_str(wxConvUTF8),
|
||||
pCount, pParams, pLens, pFormats, 1);
|
||||
|
||||
if (ret != 1)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE;
|
||||
|
||||
m_conn->SetLastResultError(NULL, _("Failed to run PQsendQuery in pgQueryThread"));
|
||||
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
|
||||
|
||||
PQclear(res);
|
||||
res = NULL;
|
||||
|
||||
goto return_with_error;
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
res = NULL;
|
||||
#else
|
||||
rc = -1;
|
||||
wxASSERT_MSG(false,
|
||||
_("the program execution flow must not reach to this point in pgQueryThread"));
|
||||
|
||||
goto return_with_error;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// assumptions: we will need the results in text format only
|
||||
ret = PQsendQueryParams(m_conn->conn, queryBuf, pCount, pOids, pParams, pLens, pFormats, 0);
|
||||
|
||||
if (ret != 1)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;
|
||||
|
||||
m_conn->SetLastResultError(NULL,
|
||||
_("Failed to run PQsendQueryParams in pgQueryThread"));
|
||||
|
||||
err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
|
||||
wxString(PQerrorMessage(m_conn->conn), conv);
|
||||
|
||||
goto return_with_error;
|
||||
}
|
||||
}
|
||||
goto continue_without_error;
|
||||
|
||||
return_with_error:
|
||||
{
|
||||
free(pOids);
|
||||
free(pParams);
|
||||
free(pLens);
|
||||
free(pFormats);
|
||||
#if defined (__WXMSW__) || (EDB_LIBPQ)
|
||||
free(pModes);
|
||||
#endif
|
||||
return (RaiseEvent(rc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the PQsendQuery api in case, we don't have any parameters to
|
||||
// pass to the server
|
||||
if (!PQsendQuery(m_conn->conn, queryBuf))
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_SEND_QUERY;
|
||||
|
||||
err.msg_primary = _("Failed to run PQsendQueryParams in pgQueryThread.\n") +
|
||||
wxString(PQerrorMessage(m_conn->conn), conv);
|
||||
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
}
|
||||
|
||||
continue_without_error:
|
||||
int resultsRetrieved = 0;
|
||||
PGresult *lastResult = 0;
|
||||
bool connExecutionCancelled = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// This is a 'joinable' thread, it is not advisable to call 'delete'
|
||||
// function on this.
|
||||
// Hence - it does not make sense to use the function 'testdestroy' here.
|
||||
// We introduced the 'CancelExecution' function for the same purpose.
|
||||
//
|
||||
// We will have to call the CancelExecution function of pgConn to
|
||||
// inform the backend that the user has cancelled the execution.
|
||||
//
|
||||
// We will need to consume all the results before quiting from the thread.
|
||||
// Otherwise - the connection object will become unusable, and throw
|
||||
// error - because libpq connections expects application to consume all
|
||||
// the result, before executing another query
|
||||
//
|
||||
if (m_cancelled && rc != pgQueryResultEvent::PGQ_EXECUTION_CANCELLED)
|
||||
{
|
||||
// We shouldn't be calling cancellation multiple time
|
||||
if (!connExecutionCancelled)
|
||||
{
|
||||
m_conn->CancelExecution();
|
||||
connExecutionCancelled = true;
|
||||
}
|
||||
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
|
||||
|
||||
err.msg_primary = _("Execution Cancelled");
|
||||
|
||||
if (lastResult)
|
||||
{
|
||||
PQclear(lastResult);
|
||||
lastResult = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rc = PQconsumeInput(m_conn->conn)) != 1)
|
||||
{
|
||||
if (m_cancelled)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
|
||||
err.msg_primary = _("Execution Cancelled");
|
||||
|
||||
if (lastResult)
|
||||
{
|
||||
PQclear(lastResult);
|
||||
lastResult = NULL;
|
||||
}
|
||||
// There is nothing more to consume.
|
||||
// We can quit the thread now.
|
||||
//
|
||||
// Raise the event - if the component asked for it on
|
||||
// execution cancellation.
|
||||
if (m_eventOnCancellation)
|
||||
RaiseEvent(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
|
||||
}
|
||||
if (PQstatus(m_conn->conn) == CONNECTION_BAD)
|
||||
{
|
||||
err.msg_primary = _("Connection to the database server lost");
|
||||
rc = pgQueryResultEvent::PGQ_CONN_LOST;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;
|
||||
}
|
||||
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
|
||||
if (PQisBusy(m_conn->conn))
|
||||
{
|
||||
Yield();
|
||||
this->Sleep(10);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// if resultToRetrieve is given, the nth result will be returned,
|
||||
// otherwise the last result set will be returned.
|
||||
// all others are discarded
|
||||
PGresult *res = PQgetResult(m_conn->conn);
|
||||
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
if((PQresultStatus(res) == PGRES_NONFATAL_ERROR) ||
|
||||
(PQresultStatus(res) == PGRES_FATAL_ERROR) ||
|
||||
(PQresultStatus(res) == PGRES_BAD_RESPONSE))
|
||||
{
|
||||
result = res;
|
||||
err.SetError(res, &conv);
|
||||
|
||||
// Wait for the execution to be finished
|
||||
// We need to fetch all the results, before sending the error
|
||||
// message
|
||||
do
|
||||
{
|
||||
if (PQconsumeInput(m_conn->conn) != 1)
|
||||
{
|
||||
if (m_cancelled)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
|
||||
|
||||
// Release the result as the query execution has been cancelled by the
|
||||
// user
|
||||
if (result)
|
||||
PQclear(result);
|
||||
|
||||
result = NULL;
|
||||
|
||||
if (m_eventOnCancellation)
|
||||
RaiseEvent(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
goto out_of_consume_input_loop;
|
||||
}
|
||||
|
||||
if ((res = PQgetResult(m_conn->conn)) == NULL)
|
||||
{
|
||||
goto out_of_consume_input_loop;
|
||||
}
|
||||
|
||||
// Release the temporary results
|
||||
PQclear(res);
|
||||
res = NULL;
|
||||
|
||||
if (PQisBusy(m_conn->conn))
|
||||
{
|
||||
Yield();
|
||||
this->Sleep(10);
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined (__WXMSW__) || (EDB_LIBPQ)
|
||||
// there should be 2 results in the callable statement - the first is the
|
||||
// dummy, the second contains our out params.
|
||||
if (useCallable)
|
||||
{
|
||||
PQclear(res);
|
||||
result = PQiGetOutResult(m_conn->conn);
|
||||
}
|
||||
#endif
|
||||
if (PQresultStatus(res) == PGRES_COPY_IN)
|
||||
{
|
||||
rc = PGRES_COPY_IN;
|
||||
PQputCopyEnd(m_conn->conn, "not supported by pgadmin");
|
||||
}
|
||||
|
||||
if (PQresultStatus(res) == PGRES_COPY_OUT)
|
||||
{
|
||||
int copyRc;
|
||||
char *buf;
|
||||
int copyRows = 0;
|
||||
int lastCopyRc = 0;
|
||||
|
||||
rc = PGRES_COPY_OUT;
|
||||
|
||||
AppendMessage(_("query returned copy data:\n"));
|
||||
|
||||
while((copyRc = PQgetCopyData(m_conn->conn, &buf, 1)) >= 0)
|
||||
{
|
||||
// Ignore processing the query result, when it has already been
|
||||
// cancelled by the user
|
||||
if (m_cancelled)
|
||||
{
|
||||
if (!connExecutionCancelled)
|
||||
{
|
||||
m_conn->CancelExecution();
|
||||
connExecutionCancelled = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf != NULL)
|
||||
{
|
||||
if (copyRows < 100)
|
||||
{
|
||||
wxString str(buf, conv);
|
||||
wxCriticalSectionLocker cs(m_criticalSection);
|
||||
m_queries[m_currIndex]->m_message.Append(str);
|
||||
|
||||
}
|
||||
else if (copyRows == 100)
|
||||
AppendMessage(_("Query returned more than 100 copy rows, discarding the rest...\n"));
|
||||
|
||||
PQfreemem(buf);
|
||||
}
|
||||
if (copyRc > 0)
|
||||
copyRows++;
|
||||
|
||||
if (lastCopyRc == 0 && copyRc == 0)
|
||||
{
|
||||
Yield();
|
||||
this->Sleep(10);
|
||||
}
|
||||
if (copyRc == 0)
|
||||
{
|
||||
if (!PQconsumeInput(m_conn->conn))
|
||||
{
|
||||
// It might be the case - it is a result of the
|
||||
// execution cancellation.
|
||||
if (m_cancelled)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
|
||||
|
||||
// Release the result as the query execution has been cancelled by the
|
||||
// user
|
||||
if (result)
|
||||
PQclear(result);
|
||||
|
||||
result = NULL;
|
||||
|
||||
if (m_eventOnCancellation)
|
||||
RaiseEvent(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
if (PQstatus(m_conn->conn) == CONNECTION_BAD)
|
||||
{
|
||||
err.msg_primary = _("Connection to the database server lost");
|
||||
rc = pgQueryResultEvent::PGQ_CONN_LOST;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT;
|
||||
|
||||
err.msg_primary = wxString(PQerrorMessage(m_conn->conn), conv);
|
||||
}
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
}
|
||||
lastCopyRc = copyRc;
|
||||
}
|
||||
|
||||
res = PQgetResult(m_conn->conn);
|
||||
|
||||
if (!res)
|
||||
break;
|
||||
}
|
||||
|
||||
resultsRetrieved++;
|
||||
|
||||
// Save the current result, as asked by the component
|
||||
// But - only if the execution is not cancelled
|
||||
if (!m_cancelled && resultsRetrieved == resultToRetrieve)
|
||||
{
|
||||
result = res;
|
||||
insertedOid = PQoidValue(res);
|
||||
if (insertedOid && insertedOid != (Oid) - 1)
|
||||
AppendMessage(wxString::Format(_("query inserted one row with oid %d.\n"), insertedOid));
|
||||
else
|
||||
AppendMessage(wxString::Format(wxPLURAL("query result with %d row will be returned.\n", "query result with %d rows will be returned.\n",
|
||||
PQntuples(result)), PQntuples(result)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastResult)
|
||||
{
|
||||
if (!m_cancelled && PQntuples(lastResult))
|
||||
AppendMessage(wxString::Format(wxPLURAL("query result with %d row discarded.\n", "query result with %d rows discarded.\n",
|
||||
PQntuples(lastResult)), PQntuples(lastResult)));
|
||||
PQclear(lastResult);
|
||||
}
|
||||
lastResult = res;
|
||||
}
|
||||
|
||||
out_of_consume_input_loop:
|
||||
if (m_cancelled)
|
||||
{
|
||||
rc = pgQueryResultEvent::PGQ_EXECUTION_CANCELLED;
|
||||
|
||||
// Release the result as the query execution has been cancelled by the
|
||||
// user
|
||||
if (result)
|
||||
PQclear(result);
|
||||
|
||||
result = NULL;
|
||||
|
||||
if (m_eventOnCancellation)
|
||||
RaiseEvent(rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = lastResult;
|
||||
|
||||
err.SetError(result, &conv);
|
||||
|
||||
AppendMessage(wxT("\n"));
|
||||
|
||||
rc = PQresultStatus(result);
|
||||
if (rc == PGRES_TUPLES_OK)
|
||||
{
|
||||
dataSet = new pgSet(result, m_conn, conv, m_conn->needColQuoting);
|
||||
dataSet->MoveFirst();
|
||||
}
|
||||
else if (rc == PGRES_COMMAND_OK)
|
||||
{
|
||||
char *s = PQcmdTuples(result);
|
||||
if (*s)
|
||||
rowsInserted = atol(s);
|
||||
}
|
||||
else if (rc == PGRES_FATAL_ERROR ||
|
||||
rc == PGRES_NONFATAL_ERROR ||
|
||||
rc == PGRES_BAD_RESPONSE)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
AppendMessage(wxString(PQresultErrorMessage(result), conv));
|
||||
PQclear(result);
|
||||
result = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendMessage(wxString(PQerrorMessage(m_conn->conn), conv));
|
||||
}
|
||||
|
||||
return(RaiseEvent(rc));
|
||||
}
|
||||
|
||||
insertedOid = PQoidValue(result);
|
||||
if (insertedOid == (Oid) - 1)
|
||||
insertedOid = 0;
|
||||
|
||||
return(RaiseEvent(1));
|
||||
}
|
||||
|
||||
int pgQueryThread::RaiseEvent(int _retval)
|
||||
{
|
||||
#if !defined(PGSCLI)
|
||||
if (m_caller)
|
||||
{
|
||||
pgQueryResultEvent resultEvent(GetId(), m_queries[m_currIndex], m_queries[m_currIndex]->m_eventID);
|
||||
|
||||
// client data
|
||||
resultEvent.SetClientData(m_queries[m_currIndex]->m_data);
|
||||
resultEvent.SetInt(_retval);
|
||||
|
||||
m_caller->AddPendingEvent(resultEvent);
|
||||
}
|
||||
#endif
|
||||
return _retval;
|
||||
}
|
||||
|
||||
|
||||
void *pgQueryThread::Entry()
|
||||
{
|
||||
do
|
||||
{
|
||||
if (m_currIndex < (((int)m_queries.GetCount()) - 1))
|
||||
{
|
||||
// Create the PGcancel object to enable cancelling the running
|
||||
// query
|
||||
m_conn->SetConnCancel();
|
||||
m_currIndex++;
|
||||
|
||||
m_queries[m_currIndex]->m_returnCode = -2;
|
||||
m_queries[m_currIndex]->m_rowsInserted = -1l;
|
||||
|
||||
wxLogSql(wxT("Thread executing query (%d:%s:%d): %s"),
|
||||
m_currIndex + 1, m_conn->GetHost().c_str(), m_conn->GetPort(),
|
||||
m_queries[m_currIndex]->m_query.c_str());
|
||||
|
||||
// register the notice processor for the current query
|
||||
m_conn->RegisterNoticeProcessor(m_processor, m_noticeHandler);
|
||||
|
||||
// execute the current query now
|
||||
Execute();
|
||||
|
||||
// remove the notice processor now
|
||||
m_conn->RegisterNoticeProcessor(0, 0);
|
||||
|
||||
// reset the PGcancel object
|
||||
m_conn->ResetConnCancel();
|
||||
}
|
||||
|
||||
if (!m_multiQueries || m_cancelled)
|
||||
break;
|
||||
|
||||
wxThread::Sleep(10);
|
||||
}
|
||||
while (true);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
int pgQueryThread::DeleteReleasedQueries()
|
||||
{
|
||||
int res = 0,
|
||||
idx = 0;
|
||||
|
||||
if (m_queriesLock.TryLock() == wxMUTEX_BUSY)
|
||||
return res;
|
||||
|
||||
for (; idx <= m_currIndex; idx++)
|
||||
{
|
||||
if (m_queries[idx]->m_resultSet != NULL)
|
||||
{
|
||||
pgSet *set = m_queries[idx]->m_resultSet;
|
||||
m_queries[idx]->m_resultSet = NULL;
|
||||
delete set;
|
||||
set = NULL;
|
||||
|
||||
res++;
|
||||
}
|
||||
}
|
||||
m_queriesLock.Unlock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
pgError pgQueryThread::GetResultError(int idx)
|
||||
{
|
||||
wxMutexLocker lock(m_queriesLock);
|
||||
|
||||
if (idx == -1)
|
||||
idx = m_currIndex;
|
||||
|
||||
return m_queries[idx]->m_err;
|
||||
}
|
||||
|
||||
|
||||
const wxString &pgBatchQuery::GetErrorMessage()
|
||||
{
|
||||
return m_err.msg_primary;
|
||||
}
|
||||
|
||||
pgBatchQuery::~pgBatchQuery()
|
||||
{
|
||||
if (m_resultSet)
|
||||
{
|
||||
delete m_resultSet;
|
||||
m_resultSet = NULL;
|
||||
}
|
||||
|
||||
if (m_params)
|
||||
{
|
||||
WX_CLEAR_ARRAY((*m_params));
|
||||
delete m_params;
|
||||
m_params = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool pgBatchQuery::Release()
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
if (m_resultSet != NULL)
|
||||
{
|
||||
res = true;
|
||||
|
||||
pgSet *set = m_resultSet;
|
||||
m_resultSet = NULL;
|
||||
|
||||
if (set)
|
||||
delete set;
|
||||
set = NULL;
|
||||
}
|
||||
|
||||
if (m_params)
|
||||
{
|
||||
res = true;
|
||||
|
||||
WX_CLEAR_ARRAY((*m_params));
|
||||
delete m_params;
|
||||
m_params = NULL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
pgQueryResultEvent::pgQueryResultEvent(
|
||||
unsigned long _thrdId, pgBatchQuery *_qry, int _id) :
|
||||
wxCommandEvent(PGQueryResultEvent, _id), m_thrdId(_thrdId),
|
||||
m_query(_qry) { }
|
||||
|
||||
pgQueryResultEvent::pgQueryResultEvent(const pgQueryResultEvent &_ev)
|
||||
: wxCommandEvent(_ev), m_thrdId(_ev.m_thrdId), m_query(_ev.m_query) { }
|
||||
|
||||
|
||||
pgParam::pgParam(Oid _type, void *_val, int _len, short _mode)
|
||||
: m_type(_type), m_val(_val), m_len(_len), m_mode(_mode)
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case PGOID_TYPE_CHAR:
|
||||
case PGOID_TYPE_NAME:
|
||||
case PGOID_TYPE_TEXT:
|
||||
case PGOID_TYPE_VARCHAR:
|
||||
case PGOID_TYPE_CSTRING:
|
||||
m_format = 0;
|
||||
default:
|
||||
m_format = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// wxString data
|
||||
pgParam::pgParam(Oid _oid, wxString *_val, wxMBConv *_conv, short _mode)
|
||||
: m_mode(_mode)
|
||||
{
|
||||
if (m_mode == PG_PARAM_OUT || !_val)
|
||||
{
|
||||
m_len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_len = _val->Len();
|
||||
}
|
||||
if (_val)
|
||||
{
|
||||
char *str = (char *)malloc(m_len + 1);
|
||||
if (!_val->IsEmpty() && _mode != PG_PARAM_OUT)
|
||||
{
|
||||
strncpy(str,
|
||||
(const char *)_val->mb_str(
|
||||
*(_conv != NULL ? _conv : &wxConvLocal)), m_len);
|
||||
str[m_len] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
str[0] = '\0';
|
||||
}
|
||||
m_val = (void *)(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_val = NULL;
|
||||
}
|
||||
m_type = _oid;
|
||||
|
||||
// text format
|
||||
m_format = 0;
|
||||
}
|
||||
|
||||
|
||||
pgParam::~pgParam()
|
||||
{
|
||||
if (m_val)
|
||||
free(m_val);
|
||||
m_val = NULL;
|
||||
}
|
||||
|
||||
|
||||
int pgParam::GetFormat()
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
443
db/pgSet.cpp
Normal file
443
db/pgSet.cpp
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// pgSet.cpp - PostgreSQL ResultSet class
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// PostgreSQL headers
|
||||
#include <libpq-fe.h>
|
||||
|
||||
// App headers
|
||||
#include "db/pgSet.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "utils/sysLogger.h"
|
||||
#include "utils/pgDefs.h"
|
||||
|
||||
pgSet::pgSet()
|
||||
: conv(wxConvLibc)
|
||||
{
|
||||
conn = 0;
|
||||
res = 0;
|
||||
nCols = 0;
|
||||
nRows = 0;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
pgSet::pgSet(PGresult *newRes, pgConn *newConn, wxMBConv &cnv, bool needColQt)
|
||||
: conv(cnv)
|
||||
{
|
||||
needColQuoting = needColQt;
|
||||
|
||||
conn = newConn;
|
||||
res = newRes;
|
||||
|
||||
// Make sure we have tuples
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
nCols = 0;
|
||||
nRows = 0;
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nCols = PQnfields(res);
|
||||
for (int x = 0; x < nCols + 1; x++)
|
||||
{
|
||||
colTypes.Add(wxT(""));
|
||||
colFullTypes.Add(wxT(""));
|
||||
colClasses.Add(0);
|
||||
}
|
||||
|
||||
nRows = PQntuples(res);
|
||||
MoveFirst();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pgSet::~pgSet()
|
||||
{
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
OID pgSet::ColTypeOid(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return PQftype(res, col);
|
||||
}
|
||||
|
||||
long pgSet::ColTypeMod(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return PQfmod(res, col);
|
||||
}
|
||||
|
||||
|
||||
long pgSet::GetInsertedCount() const
|
||||
{
|
||||
char *cnt = PQcmdTuples(res);
|
||||
if (!*cnt)
|
||||
return -1;
|
||||
else
|
||||
return atol(cnt);
|
||||
}
|
||||
|
||||
|
||||
pgTypClass pgSet::ColTypClass(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
if (colClasses[col] != 0)
|
||||
return (pgTypClass)colClasses[col];
|
||||
|
||||
wxString typoid = ExecuteScalar(
|
||||
wxT("SELECT CASE WHEN typbasetype=0 THEN oid else typbasetype END AS basetype\n")
|
||||
wxT(" FROM pg_type WHERE oid=") + NumToStr(ColTypeOid(col)));
|
||||
|
||||
switch (StrToLong(typoid))
|
||||
{
|
||||
case PGOID_TYPE_BOOL:
|
||||
colClasses[col] = PGTYPCLASS_BOOL;
|
||||
break;
|
||||
case PGOID_TYPE_INT8:
|
||||
case PGOID_TYPE_INT2:
|
||||
case PGOID_TYPE_INT4:
|
||||
case PGOID_TYPE_OID:
|
||||
case PGOID_TYPE_XID:
|
||||
case PGOID_TYPE_TID:
|
||||
case PGOID_TYPE_CID:
|
||||
case PGOID_TYPE_FLOAT4:
|
||||
case PGOID_TYPE_FLOAT8:
|
||||
case PGOID_TYPE_MONEY:
|
||||
case PGOID_TYPE_BIT:
|
||||
case PGOID_TYPE_NUMERIC:
|
||||
colClasses[col] = PGTYPCLASS_NUMERIC;
|
||||
break;
|
||||
case PGOID_TYPE_BYTEA:
|
||||
case PGOID_TYPE_CHAR:
|
||||
case PGOID_TYPE_NAME:
|
||||
case PGOID_TYPE_TEXT:
|
||||
case PGOID_TYPE_VARCHAR:
|
||||
colClasses[col] = PGTYPCLASS_STRING;
|
||||
break;
|
||||
case PGOID_TYPE_TIMESTAMP:
|
||||
case PGOID_TYPE_TIMESTAMPTZ:
|
||||
case PGOID_TYPE_TIME:
|
||||
case PGOID_TYPE_TIMETZ:
|
||||
case PGOID_TYPE_INTERVAL:
|
||||
colClasses[col] = PGTYPCLASS_DATE;
|
||||
break;
|
||||
default:
|
||||
colClasses[col] = PGTYPCLASS_OTHER;
|
||||
break;
|
||||
}
|
||||
|
||||
return (pgTypClass)colClasses[col];
|
||||
}
|
||||
|
||||
|
||||
wxString pgSet::ColType(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
if (!colTypes[col].IsEmpty())
|
||||
return colTypes[col];
|
||||
|
||||
wxString szSQL, szResult;
|
||||
szSQL.Printf(wxT("SELECT format_type(oid,NULL) as typname FROM pg_type WHERE oid = %d"), (int)ColTypeOid(col));
|
||||
szResult = ExecuteScalar(szSQL);
|
||||
colTypes[col] = szResult;
|
||||
|
||||
return szResult;
|
||||
}
|
||||
|
||||
wxString pgSet::ColFullType(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
if (!colFullTypes[col].IsEmpty())
|
||||
return colFullTypes[col];
|
||||
|
||||
wxString szSQL, szResult;
|
||||
szSQL.Printf(wxT("SELECT format_type(oid,%d) as typname FROM pg_type WHERE oid = %d"), (int)ColTypeMod(col), (int)ColTypeOid(col));
|
||||
szResult = ExecuteScalar(szSQL);
|
||||
colFullTypes[col] = szResult;
|
||||
|
||||
return szResult;
|
||||
}
|
||||
|
||||
int pgSet::ColScale(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
wxString pgSet::ColName(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return wxString(PQfname(res, col), conv);
|
||||
}
|
||||
|
||||
|
||||
int pgSet::ColNumber(const wxString &colname) const
|
||||
{
|
||||
int col;
|
||||
|
||||
if (needColQuoting)
|
||||
{
|
||||
wxString quotedColName = colname;
|
||||
quotedColName.Replace(wxT("\""), wxT("\"\""));
|
||||
col = PQfnumber(res, (wxT("\"") + quotedColName + wxT("\"")).mb_str(conv));
|
||||
}
|
||||
else
|
||||
col = PQfnumber(res, colname.mb_str(conv));
|
||||
|
||||
if (col < 0)
|
||||
{
|
||||
wxLogError(__("Column not found in pgSet: %s"), colname.c_str());
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
bool pgSet::HasColumn(const wxString &colname) const
|
||||
{
|
||||
if (needColQuoting)
|
||||
{
|
||||
wxString quotedColName = colname;
|
||||
quotedColName.Replace(wxT("\""), wxT("\"\""));
|
||||
return (PQfnumber(res, (wxT("\"") + quotedColName + wxT("\"")).mb_str(conv)) < 0 ? false : true);
|
||||
}
|
||||
else
|
||||
return (PQfnumber(res, colname.mb_str(conv)) < 0 ? false : true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *pgSet::GetCharPtr(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return PQgetvalue(res, pos - 1, col);
|
||||
}
|
||||
|
||||
|
||||
char *pgSet::GetCharPtr(const wxString &col) const
|
||||
{
|
||||
return PQgetvalue(res, pos - 1, ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
wxString pgSet::GetVal(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return wxString(GetCharPtr(col), conv);
|
||||
}
|
||||
|
||||
|
||||
wxString pgSet::GetVal(const wxString &colname) const
|
||||
{
|
||||
return GetVal(ColNumber(colname));
|
||||
}
|
||||
|
||||
|
||||
long pgSet::GetLong(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
char *c = PQgetvalue(res, pos - 1, col);
|
||||
if (c)
|
||||
return atol(c);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long pgSet::GetLong(const wxString &col) const
|
||||
{
|
||||
char *c = PQgetvalue(res, pos - 1, ColNumber(col));
|
||||
if (c)
|
||||
return atol(c);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool pgSet::GetBool(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
char *c = PQgetvalue(res, pos - 1, col);
|
||||
if (c)
|
||||
{
|
||||
if (*c == 't' || *c == '1' || !strcmp(c, "on"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool pgSet::GetBool(const wxString &col) const
|
||||
{
|
||||
return GetBool(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
wxDateTime pgSet::GetDateTime(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
wxDateTime dt;
|
||||
wxString str = GetVal(col);
|
||||
/* This hasn't just been used. ( Is not infinity ) */
|
||||
if (!str.IsEmpty())
|
||||
dt.ParseDateTime(str);
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
wxDateTime pgSet::GetDateTime(const wxString &col) const
|
||||
{
|
||||
return GetDateTime(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
wxDateTime pgSet::GetDate(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
wxDateTime dt;
|
||||
wxString str = GetVal(col);
|
||||
/* This hasn't just been used. ( Is not infinity ) */
|
||||
if (!str.IsEmpty())
|
||||
dt.ParseDate(str);
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
wxDateTime pgSet::GetDate(const wxString &col) const
|
||||
{
|
||||
return GetDate(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
double pgSet::GetDouble(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
return StrToDouble(GetVal(col));
|
||||
}
|
||||
|
||||
|
||||
double pgSet::GetDouble(const wxString &col) const
|
||||
{
|
||||
return GetDouble(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
wxULongLong pgSet::GetLongLong(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
char *c = PQgetvalue(res, pos - 1, col);
|
||||
if (c)
|
||||
return atolonglong(c);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
wxULongLong pgSet::GetLongLong(const wxString &col) const
|
||||
{
|
||||
return GetLongLong(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
OID pgSet::GetOid(const int col) const
|
||||
{
|
||||
wxASSERT(col < nCols && col >= 0);
|
||||
|
||||
char *c = PQgetvalue(res, pos - 1, col);
|
||||
if (c)
|
||||
return (OID)strtoul(c, 0, 10);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
OID pgSet::GetOid(const wxString &col) const
|
||||
{
|
||||
return GetOid(ColNumber(col));
|
||||
}
|
||||
|
||||
|
||||
wxString pgSet::ExecuteScalar(const wxString &sql) const
|
||||
{
|
||||
return conn->ExecuteScalar(sql);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
pgSetIterator::pgSetIterator(pgConn *conn, const wxString &qry)
|
||||
{
|
||||
set = conn->ExecuteSet(qry);
|
||||
first = true;
|
||||
}
|
||||
|
||||
|
||||
pgSetIterator::pgSetIterator(pgSet *s)
|
||||
{
|
||||
set = s;
|
||||
first = true;
|
||||
}
|
||||
|
||||
|
||||
pgSetIterator::~pgSetIterator()
|
||||
{
|
||||
if (set)
|
||||
delete set;
|
||||
}
|
||||
|
||||
|
||||
bool pgSetIterator::RowsLeft()
|
||||
{
|
||||
if (!set)
|
||||
return false;
|
||||
|
||||
if (first)
|
||||
{
|
||||
if (!set->NumRows())
|
||||
return false;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
set->MoveNext();
|
||||
|
||||
return !set->Eof();
|
||||
}
|
||||
|
||||
|
||||
bool pgSetIterator::MovePrev()
|
||||
{
|
||||
if (!set)
|
||||
return false;
|
||||
|
||||
set->MovePrevious();
|
||||
return true;
|
||||
}
|
||||
538
dd/dditems/figures/ddColumnFigure.cpp
Normal file
538
dd/dditems/figures/ddColumnFigure.cpp
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddColumnFigure.cpp - Minimal Composite Figure for a column of a table
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/pen.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddColumnFigure.h"
|
||||
#include "dd/dditems/figures/ddTextTableItemFigure.h"
|
||||
#include "dd/dditems/tools/ddColumnFigureTool.h"
|
||||
#include "dd/dditems/figures/ddColumnKindIcon.h"
|
||||
#include "dd/dditems/figures/ddColumnOptionIcon.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipItem.h"
|
||||
|
||||
void ddColumnFigure::Init(wxString &columnName, ddTableFigure *owner, ddRelationshipItem *sourceFk)
|
||||
{
|
||||
setKindId(DDCOLUMNFIGURE);
|
||||
fkSource = sourceFk;
|
||||
usedAsFkDestFor = NULL;
|
||||
setOwnerTable(owner);
|
||||
deactivateGenFkName(); //initializae by default at not generate auto fk name
|
||||
columnText = new ddTextTableItemFigure(columnName, dt_null, this);
|
||||
kindImage = new ddColumnKindIcon(this);
|
||||
optionImage = new ddColumnOptionIcon(this);
|
||||
|
||||
|
||||
//Initialize displaybox and image coords
|
||||
basicDisplayBox.SetPosition(0, wxPoint(0, 0));
|
||||
basicDisplayBox.SetSize( columnText->displayBox().GetSize());
|
||||
if(kindImage && optionImage)
|
||||
{
|
||||
basicDisplayBox.width += kindImage->displayBox().GetSize().GetWidth() + 3;
|
||||
basicDisplayBox.width += optionImage->displayBox().GetSize().GetWidth() + 3;
|
||||
columnText->displayBox().x[0] += kindImage->displayBox().GetSize().GetWidth() + 2;
|
||||
columnText->displayBox().x[0] += optionImage->displayBox().GetSize().GetWidth() + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
basicDisplayBox.width += 22; //default value = 1 + 8 + 3 + 8 + 2
|
||||
columnText->displayBox().x[0] += 22;
|
||||
}
|
||||
|
||||
//Set Value default Attributes
|
||||
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
|
||||
pgAttNumColNumber = -1;
|
||||
}
|
||||
|
||||
ddColumnFigure::ddColumnFigure(wxString &columnName, ddTableFigure *owner, ddRelationshipItem *sourceFk)
|
||||
{
|
||||
Init(columnName, owner, sourceFk);
|
||||
}
|
||||
|
||||
//Constructor for creation of existing column from storage
|
||||
ddColumnFigure::ddColumnFigure(wxString &columnName, ddTableFigure *owner, ddColumnOptionType option, bool isGenFk, bool isPkColumn, wxString colDataType, int p, int s, int ukIdx, ddRelationshipItem *srcFk, ddRelationshipItem *usedAsFk )
|
||||
{
|
||||
Init(columnName, owner, srcFk);
|
||||
setColumnOption(option);
|
||||
generateFkName = isGenFk;
|
||||
kindImage->setPrimaryKey(isPkColumn);
|
||||
|
||||
int dtype = columnText->dataTypes().Index(colDataType, false);
|
||||
if(dtype != wxNOT_FOUND)
|
||||
{
|
||||
setDataType((ddDataType)dtype);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(wxString::Format(_("%s column has a non compatible data type (%s).\n%s will now have the default data type."),
|
||||
columnName.c_str(), colDataType.c_str(), columnName.c_str()));
|
||||
setDataType((ddDataType)0);
|
||||
}
|
||||
|
||||
columnText->setPrecision(p);
|
||||
columnText->setScale(s);
|
||||
kindImage->setUkIndex(ukIdx);
|
||||
|
||||
usedAsFkDestFor = usedAsFk;
|
||||
if(ownerTable)
|
||||
ownerTable->updateTableSize();
|
||||
|
||||
}
|
||||
|
||||
ddColumnFigure::~ddColumnFigure()
|
||||
{
|
||||
if(columnText)
|
||||
delete columnText;
|
||||
if(kindImage)
|
||||
delete kindImage;
|
||||
if(optionImage)
|
||||
delete optionImage;
|
||||
}
|
||||
|
||||
void ddColumnFigure::AddPosForNewDiagram()
|
||||
{
|
||||
//Add position for new displaybox at new diagram
|
||||
hdAttributeFigure::AddPosForNewDiagram();
|
||||
//Add position to each figure inside this composite figure
|
||||
if(kindImage)
|
||||
kindImage->AddPosForNewDiagram();
|
||||
if(optionImage)
|
||||
optionImage->AddPosForNewDiagram();
|
||||
if(columnText)
|
||||
columnText->AddPosForNewDiagram();
|
||||
}
|
||||
|
||||
void ddColumnFigure::RemovePosOfDiagram(int posIdx)
|
||||
{
|
||||
//Remove this position at displaybox of this figure
|
||||
hdAttributeFigure::RemovePosOfDiagram(posIdx);
|
||||
//Remove this position at each figure inside this composite figure
|
||||
if(kindImage)
|
||||
kindImage->RemovePosOfDiagram(posIdx);
|
||||
if(optionImage)
|
||||
optionImage->RemovePosOfDiagram(posIdx);
|
||||
if(columnText)
|
||||
columnText->RemovePosOfDiagram(posIdx);
|
||||
|
||||
}
|
||||
|
||||
void ddColumnFigure::basicMoveBy(int posIdx, int x, int y)
|
||||
{
|
||||
hdAbstractFigure::basicMoveBy(posIdx, x, y);
|
||||
if(kindImage)
|
||||
kindImage->moveBy(posIdx, x, y);
|
||||
if(optionImage)
|
||||
optionImage->moveBy(posIdx, x, y);
|
||||
columnText->moveBy(posIdx, x, y);
|
||||
}
|
||||
|
||||
void ddColumnFigure::moveTo(int posIdx, int x, int y)
|
||||
{
|
||||
hdAbstractFigure::moveTo(posIdx, x, y);
|
||||
int distance = 0;
|
||||
|
||||
if(kindImage)
|
||||
{
|
||||
kindImage->moveTo(posIdx, x, y);
|
||||
distance += kindImage->displayBox().GetSize().GetWidth() + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance += 11; //value = 8 + 3
|
||||
}
|
||||
|
||||
if(optionImage)
|
||||
{
|
||||
optionImage->moveTo(posIdx, x + distance, y);
|
||||
distance += optionImage->displayBox().GetSize().GetWidth() + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance += 11; //value = 8 + 3
|
||||
}
|
||||
|
||||
columnText->moveTo(posIdx, x + distance, y);
|
||||
}
|
||||
|
||||
|
||||
bool ddColumnFigure::containsPoint(int posIdx, int x, int y)
|
||||
{
|
||||
bool out = false;
|
||||
|
||||
if(columnText->containsPoint(posIdx, x, y))
|
||||
out = true;
|
||||
else if(kindImage->containsPoint(posIdx, x, y))
|
||||
out = true;
|
||||
else if(optionImage->containsPoint(posIdx, x, y))
|
||||
out = true;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
hdMultiPosRect &ddColumnFigure::getBasicDisplayBox()
|
||||
{
|
||||
return basicDisplayBox;
|
||||
}
|
||||
|
||||
void ddColumnFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
columnText->draw(context, view);
|
||||
if(kindImage)
|
||||
kindImage->draw(context, view);
|
||||
if(optionImage)
|
||||
optionImage->draw(context, view);
|
||||
}
|
||||
|
||||
void ddColumnFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
columnText->drawSelected(context, view);
|
||||
if(kindImage)
|
||||
kindImage->drawSelected(context, view);
|
||||
if(optionImage)
|
||||
optionImage->drawSelected(context, view);
|
||||
}
|
||||
|
||||
hdIFigure *ddColumnFigure::findFigure(int posIdx, int x, int y)
|
||||
{
|
||||
hdIFigure *out = NULL;
|
||||
|
||||
if(columnText->containsPoint(posIdx, x, y))
|
||||
out = columnText;
|
||||
|
||||
if(kindImage && kindImage->containsPoint(posIdx, x, y))
|
||||
out = kindImage;
|
||||
|
||||
if(optionImage && optionImage->containsPoint(posIdx, x, y))
|
||||
out = optionImage;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
hdITool *ddColumnFigure::CreateFigureTool(hdDrawingView *view, hdITool *defaultTool)
|
||||
{
|
||||
return new ddColumnFigureTool(view, this, defaultTool);
|
||||
}
|
||||
|
||||
hdIFigure *ddColumnFigure::getFigureAt(int pos)
|
||||
{
|
||||
if(pos == 0)
|
||||
return (hdIFigure *) kindImage;
|
||||
|
||||
if(pos == 1)
|
||||
return (hdIFigure *) optionImage;
|
||||
|
||||
if(pos == 2)
|
||||
return (hdIFigure *) columnText;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ddTableFigure *ddColumnFigure::getOwnerTable()
|
||||
{
|
||||
return ownerTable;
|
||||
}
|
||||
|
||||
void ddColumnFigure::setOwnerTable(ddTableFigure *table)
|
||||
{
|
||||
ownerTable = table;
|
||||
}
|
||||
|
||||
void ddColumnFigure::displayBoxUpdate()
|
||||
{
|
||||
if(kindImage && optionImage)
|
||||
{
|
||||
columnText->displayBoxUpdate();
|
||||
basicDisplayBox.width = columnText->displayBox().GetSize().GetWidth();
|
||||
basicDisplayBox.width += kindImage->displayBox().GetSize().GetWidth() + 3;
|
||||
basicDisplayBox.width += optionImage->displayBox().GetSize().GetWidth() + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
columnText->displayBoxUpdate();
|
||||
basicDisplayBox.width += 22; //default value = 1 + 8 + 3 + 8 +2
|
||||
}
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isNull()
|
||||
{
|
||||
return optionImage->getOption() == null;
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isNotNull()
|
||||
{
|
||||
return optionImage->getOption() == notnull;
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isNone()
|
||||
{
|
||||
return kindImage->isNone();
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isPrimaryKey()
|
||||
{
|
||||
return kindImage->isPrimaryKey();
|
||||
}
|
||||
|
||||
void ddColumnFigure::disablePrimaryKey()
|
||||
{
|
||||
kindImage->disablePrimaryKey();
|
||||
}
|
||||
|
||||
void ddColumnFigure::enablePrimaryKey()
|
||||
{
|
||||
kindImage->enablePrimaryKey();
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isUniqueKey()
|
||||
{
|
||||
return kindImage->isUniqueKey();
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isUniqueKey(int uniqueIndex)
|
||||
{
|
||||
return kindImage->isUniqueKey(uniqueIndex);
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isPlain()
|
||||
{
|
||||
return kindImage->isNone();
|
||||
}
|
||||
|
||||
void ddColumnFigure::setColumnKindToNone()
|
||||
{
|
||||
kindImage->disablePrimaryKey();
|
||||
kindImage->disableUniqueKey();
|
||||
//Foreign key cannot be changed by set / toggle functions
|
||||
}
|
||||
|
||||
void ddColumnFigure::setRightIconForColumn()
|
||||
{
|
||||
kindImage->setRightIconForColumn();
|
||||
}
|
||||
|
||||
void ddColumnFigure::toggleColumnKind(ddColumnType type, hdDrawingView *view)
|
||||
{
|
||||
kindImage->toggleColumnKind(type, view);
|
||||
}
|
||||
|
||||
void ddColumnFigure::setColumnOption(ddColumnOptionType type)
|
||||
{
|
||||
optionImage->changeIcon(type);
|
||||
}
|
||||
|
||||
wxString &ddColumnFigure::getColumnName(bool datatype)
|
||||
{
|
||||
return columnText->getText(datatype);
|
||||
}
|
||||
|
||||
void ddColumnFigure::setPrecision(int n)
|
||||
{
|
||||
columnText->setPrecision(n);
|
||||
}
|
||||
|
||||
int ddColumnFigure::getPrecision()
|
||||
{
|
||||
return columnText->getPrecision();
|
||||
}
|
||||
|
||||
void ddColumnFigure::setScale(int n)
|
||||
{
|
||||
columnText->setScale(n);
|
||||
}
|
||||
|
||||
int ddColumnFigure::getScale()
|
||||
{
|
||||
return columnText->getScale();
|
||||
}
|
||||
|
||||
int ddColumnFigure::getUniqueConstraintIndex()
|
||||
{
|
||||
return kindImage->getUniqueConstraintIndex();
|
||||
}
|
||||
|
||||
void ddColumnFigure::setUniqueConstraintIndex(int i)
|
||||
{
|
||||
kindImage->setUniqueConstraintIndex(i);
|
||||
}
|
||||
|
||||
ddColumnOptionType ddColumnFigure::getColumnOption()
|
||||
{
|
||||
return optionImage->getOption();
|
||||
}
|
||||
|
||||
ddDataType ddColumnFigure::getDataType()
|
||||
{
|
||||
return columnText->getDataType();
|
||||
}
|
||||
|
||||
void ddColumnFigure::setDataType(ddDataType type)
|
||||
{
|
||||
columnText->setDataType(type);
|
||||
}
|
||||
|
||||
void ddColumnFigure::setColumnName(wxString name)
|
||||
{
|
||||
columnText->setText(name);
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isForeignKeyFromPk()
|
||||
{
|
||||
if(fkSource)
|
||||
{
|
||||
return fkSource->isForeignKeyFromPk();
|
||||
}
|
||||
else if(usedAsFkDestFor)
|
||||
{
|
||||
return usedAsFkDestFor->isForeignKeyFromPk();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isForeignKey()
|
||||
{
|
||||
bool a = isUserCreatedForeignKey();
|
||||
bool b = isGeneratedForeignKey();
|
||||
return (a || b);
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isGeneratedForeignKey()
|
||||
{
|
||||
return fkSource != NULL;
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isUserCreatedForeignKey()
|
||||
{
|
||||
return usedAsFkDestFor != NULL;
|
||||
}
|
||||
|
||||
ddRelationshipItem *ddColumnFigure::getRelatedFkItem()
|
||||
{
|
||||
return usedAsFkDestFor;
|
||||
}
|
||||
|
||||
void ddColumnFigure::setAsUserCreatedFk(ddRelationshipItem *relatedFkItem)
|
||||
{
|
||||
usedAsFkDestFor = relatedFkItem;
|
||||
//Now fix icons of kind of column
|
||||
if(relatedFkItem == NULL)
|
||||
{
|
||||
kindImage->setRightIconForColumn();
|
||||
}
|
||||
}
|
||||
|
||||
bool ddColumnFigure::isFkNameGenerated()
|
||||
{
|
||||
return generateFkName;
|
||||
}
|
||||
|
||||
void ddColumnFigure::activateGenFkName()
|
||||
{
|
||||
generateFkName = true;
|
||||
}
|
||||
|
||||
void ddColumnFigure::deactivateGenFkName()
|
||||
{
|
||||
generateFkName = false;
|
||||
}
|
||||
|
||||
wxString ddColumnFigure::generateSQL(bool forAlterColumn)
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = wxT("\"") + getColumnName() + wxT("\" ");
|
||||
if(forAlterColumn)
|
||||
tmp += wxT("TYPE ");
|
||||
tmp += columnText->getType();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ddRelationshipItem *ddColumnFigure::getFkSource()
|
||||
{
|
||||
return fkSource;
|
||||
}
|
||||
|
||||
void ddColumnFigure::setTextColour(wxColour colour)
|
||||
{
|
||||
columnText->fontColorAttribute->fontColor = colour;
|
||||
}
|
||||
|
||||
|
||||
//Validate status of column for SQL DDL generation
|
||||
bool ddColumnFigure::validateColumn(wxString &errors)
|
||||
{
|
||||
bool out = true;
|
||||
wxString tmp;
|
||||
|
||||
if(usedAsFkDestFor)
|
||||
{
|
||||
//Validate if relationship is marked as identifying but column isn't marked as primary key
|
||||
wxString sourceTableName = usedAsFkDestFor->sourceTableName();
|
||||
wxString destTableName = usedAsFkDestFor->destTableName();
|
||||
wxString fkColName = usedAsFkDestFor->fkColumn->getColumnName(false);
|
||||
wxString sourceColName = usedAsFkDestFor->original->getColumnName(false);
|
||||
|
||||
if(usedAsFkDestFor->relationIsIdentifying() && !this->isPrimaryKey())
|
||||
{
|
||||
out = false;
|
||||
tmp = _("relation between table: ") + sourceTableName + _(" and table: ") + destTableName;
|
||||
tmp += _(" is marked as identifying but user created foreign key column: ") + fkColName;
|
||||
tmp += _(" isn't set as primary key \n");
|
||||
errors.Append(tmp);
|
||||
}
|
||||
//Validate if relationship is marked as optional but column is mandatory
|
||||
if( !usedAsFkDestFor->relationIsMandatory() && this->isNotNull())
|
||||
{
|
||||
out = false;
|
||||
tmp = _("relation between table:") + sourceTableName + _(" and table: ") + destTableName;
|
||||
tmp += _("is marked as optional but user created foreign key column: ") + fkColName;
|
||||
tmp += _("is set as mandatory \n");
|
||||
errors.Append(tmp);
|
||||
}
|
||||
//Validate if relationship is marked as mandatory buy column is optional
|
||||
if( usedAsFkDestFor->relationIsMandatory() && this->isNull())
|
||||
{
|
||||
out = false;
|
||||
tmp = _("relation between table:") + sourceTableName + _(" and table: ") + destTableName +
|
||||
tmp += _(" is marked as mandatory but user created foreign key column: ") + fkColName;
|
||||
tmp += _("is set as optional \n");
|
||||
errors.Append(tmp);
|
||||
}
|
||||
|
||||
//Validate datatype compatibility (right now only exactly same datatype)
|
||||
if(this->getDataType() != usedAsFkDestFor->original->getDataType())
|
||||
{
|
||||
out = false;
|
||||
tmp = _("User created foreign key column: ") + fkColName + _("have different datatype from source column of relationship: ");
|
||||
tmp += sourceColName + _(" \n");
|
||||
errors.Append(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
void ddColumnFigure::setFkSource(ddRelationshipItem *newColumn)
|
||||
{
|
||||
fkSource = newColumn;
|
||||
}
|
||||
|
||||
//Used by reverse engineering functions
|
||||
wxString ddColumnFigure::getRawDataType()
|
||||
{
|
||||
return columnText->getType(true);
|
||||
}
|
||||
435
dd/dditems/figures/ddColumnKindIcon.cpp
Normal file
435
dd/dditems/figures/ddColumnKindIcon.cpp
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddColumnKindIcon.cpp - Figure container for kind of Column Images (fk,pk,uk) only used
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/pen.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddColumnKindIcon.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "dd/dditems/figures/ddColumnFigure.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddforeignkey.pngc"
|
||||
#include "images/ddprimarykey.pngc"
|
||||
#include "images/ddunique.pngc"
|
||||
#include "images/ddprimaryforeignkey.pngc"
|
||||
#include "images/ddprimarykeyuniquekey.pngc"
|
||||
#include "images/ddforeignkeyfromuk.pngc"
|
||||
#include "images/ddprimaryforeignkeyfromuk.pngc"
|
||||
#include "images/ddforeignkeyuniquekey.pngc"
|
||||
#include "images/ddforeignkeyuniquekeyfromuk.pngc"
|
||||
|
||||
ddColumnKindIcon::ddColumnKindIcon(ddColumnFigure *owner)
|
||||
{
|
||||
setKindId(DDCOLUMNKINDICON);
|
||||
ownerColumn = owner;
|
||||
// Initialize with an image to allow initial size calculations
|
||||
icon = wxBitmap(*ddprimarykey_png_img);
|
||||
iconToDraw = NULL;
|
||||
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
|
||||
isPk = false;
|
||||
ukIndex = -1;
|
||||
|
||||
//Set Value default Attributes
|
||||
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
|
||||
}
|
||||
|
||||
ddColumnKindIcon::~ddColumnKindIcon()
|
||||
{
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::createMenu(wxMenu &mnu)
|
||||
{
|
||||
wxMenuItem *item;
|
||||
|
||||
item = mnu.AppendCheckItem(MNU_DDCTPKEY, _("Primary key"));
|
||||
item->Check(isPrimaryKey());
|
||||
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
|
||||
item = mnu.AppendCheckItem(MNU_DDCTUKEY, _("Unique key"));
|
||||
item->Check(isUniqueKey());
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
|
||||
{
|
||||
toggleColumnKind((ddColumnType)event.GetId(), view);
|
||||
}
|
||||
|
||||
ddColumnFigure *ddColumnKindIcon::getOwnerColumn()
|
||||
{
|
||||
return ownerColumn;
|
||||
}
|
||||
|
||||
//if columntype attribute (type) is active then disable, is disable then active.
|
||||
void ddColumnKindIcon::toggleColumnKind(ddColumnType type, hdDrawingView *view, bool interaction)
|
||||
{
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case pk:
|
||||
//Set Pk attribute
|
||||
if(isPrimaryKey())
|
||||
{
|
||||
disablePrimaryKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
enablePrimaryKey();
|
||||
if(getOwnerColumn()->isNull())
|
||||
{
|
||||
getOwnerColumn()->setColumnOption(notnull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case uk:
|
||||
if(isUniqueKey())
|
||||
{
|
||||
disableUniqueKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
uniqueConstraintManager(false, view, interaction);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::basicDraw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
if(iconToDraw)
|
||||
{
|
||||
hdRect copy = displayBox().gethdRect(view->getIdx());
|
||||
view->CalcScrolledPosition(copy.x, copy.y, ©.x, ©.y);
|
||||
//Adding a yellow circle to increase visibility of uk index
|
||||
if(isUniqueKey())
|
||||
{
|
||||
context.SetBrush(wxBrush(wxColour(wxT("YELLOW")), wxSOLID));
|
||||
context.SetPen(wxPen(wxColour(wxT("YELLOW"))));
|
||||
context.DrawCircle(copy.x + 6, copy.y + 7, 4);
|
||||
}
|
||||
//Draw icon
|
||||
context.DrawBitmap(*iconToDraw, copy.GetPosition(), true);
|
||||
//Draw Uk index if needed
|
||||
if(isUniqueKey() && ukIndex > 0)
|
||||
{
|
||||
wxFont font = fontAttribute->font();
|
||||
font.SetPointSize(6);
|
||||
context.SetFont(font);
|
||||
wxString inumber = wxString::Format(wxT("%d"), (int)ukIndex + 1);
|
||||
context.DrawText(inumber, copy.x + 4, copy.y + 2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
basicDraw(context, view);
|
||||
}
|
||||
|
||||
int ddColumnKindIcon::getWidth()
|
||||
{
|
||||
if(iconToDraw)
|
||||
return iconToDraw->GetWidth();
|
||||
else
|
||||
return 8;
|
||||
}
|
||||
|
||||
int ddColumnKindIcon::getHeight()
|
||||
{
|
||||
if(iconToDraw)
|
||||
return iconToDraw->GetHeight();
|
||||
else
|
||||
return 10;
|
||||
}
|
||||
|
||||
/*
|
||||
ddColumnType ddColumnKindIcon::getKind()
|
||||
{
|
||||
return colType;
|
||||
}
|
||||
*/
|
||||
|
||||
int ddColumnKindIcon::getUniqueConstraintIndex()
|
||||
{
|
||||
return ukIndex;
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::setUniqueConstraintIndex(int i)
|
||||
{
|
||||
ukIndex = i;
|
||||
}
|
||||
|
||||
bool ddColumnKindIcon::uniqueConstraintManager(bool ukCol, hdDrawingView *view, bool interaction)
|
||||
{
|
||||
wxString tmpString;
|
||||
bool isColUk = true;
|
||||
|
||||
if(ukCol) //if already this column kind is Unique Key then convert in a normal column
|
||||
{
|
||||
syncUkIndexes();
|
||||
setUniqueConstraintIndex(-1);
|
||||
}
|
||||
else //colType!=uk
|
||||
{
|
||||
if(interaction)
|
||||
{
|
||||
if(ownerColumn->getOwnerTable()->getUkConstraintsNames().Count() == 0)
|
||||
{
|
||||
tmpString = getOwnerColumn()->getOwnerTable()->getTableName();
|
||||
tmpString.append(wxT("_uk"));
|
||||
tmpString = wxGetTextFromUser(wxT("Name of new Unique Key constraint:"), tmpString, tmpString, view);
|
||||
if(tmpString.length() > 0)
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(tmpString);
|
||||
ukIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
setUniqueConstraintIndex(-1);
|
||||
}
|
||||
}
|
||||
else //>0
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(wxString(wxT("Add new Unique Constraint...")));
|
||||
int i = wxGetSingleChoiceIndex(wxT("Select Unique Key to add Column"), wxT("Select Unique Key to add Column:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames(), view);
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().RemoveAt(getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Count() - 1);
|
||||
if(i >= 0)
|
||||
{
|
||||
if(i == getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Count())
|
||||
{
|
||||
tmpString = getOwnerColumn()->getOwnerTable()->getTableName();
|
||||
tmpString.append(wxT("_uk"));
|
||||
|
||||
int newIndex = i + 1;
|
||||
wxString inumber = wxString::Format(wxT("%s%d"), tmpString.c_str(), (int)newIndex);
|
||||
//Validate new name of uk doesn't exists
|
||||
while(getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Index(inumber, false) != -1)
|
||||
{
|
||||
newIndex++;
|
||||
inumber = wxString::Format(wxT("%s%d"), tmpString.c_str(), (int)newIndex);
|
||||
}
|
||||
inumber = wxString::Format(wxT("%d"), (int)newIndex);
|
||||
tmpString.append(inumber);
|
||||
tmpString = wxGetTextFromUser(wxT("Name of new Unique Key constraint:"), tmpString, tmpString, view);
|
||||
if(tmpString.length() > 0)
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Add(tmpString);
|
||||
ukIndex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
setUniqueConstraintIndex(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ukIndex = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setUniqueConstraintIndex(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else //without user interaction
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//synchronize observers if this uk column is used as source of fk
|
||||
setRightIconForColumn();
|
||||
getOwnerColumn()->getOwnerTable()->updateFkObservers();
|
||||
|
||||
if(!isUniqueKey())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
//synchronize uk indexes when an uk is change kind from uk to other and other index should be update with that info
|
||||
void ddColumnKindIcon::syncUkIndexes()
|
||||
{
|
||||
ddColumnFigure *col;
|
||||
bool lastUk = true;
|
||||
hdIteratorBase *iterator = getOwnerColumn()->getOwnerTable()->figuresEnumerator();
|
||||
iterator->Next(); //First Figure is Main Rect
|
||||
iterator->Next(); //Second Figure is Table Title
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
col = (ddColumnFigure *) iterator->Next();
|
||||
|
||||
if(col != getOwnerColumn() && (col->getUniqueConstraintIndex() == getOwnerColumn()->getUniqueConstraintIndex()))
|
||||
{
|
||||
lastUk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(lastUk)
|
||||
{
|
||||
//here uks indexes are fixed
|
||||
iterator->ResetIterator();
|
||||
iterator->Next(); //First Figure is Main Rect
|
||||
iterator->Next(); //Second Figure is Table Title
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
col = (ddColumnFigure *) iterator->Next();
|
||||
if( col->getUniqueConstraintIndex() > getOwnerColumn()->getUniqueConstraintIndex() )
|
||||
col->setUniqueConstraintIndex(col->getUniqueConstraintIndex() - 1);
|
||||
}
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().RemoveAt(getOwnerColumn()->getUniqueConstraintIndex());
|
||||
getOwnerColumn()->setUniqueConstraintIndex(-1);
|
||||
}
|
||||
delete iterator;
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::setRightIconForColumn()
|
||||
{
|
||||
//for a pk Column
|
||||
if(isPrimaryKey())
|
||||
{
|
||||
if(isForeignKey())
|
||||
{
|
||||
if(getOwnerColumn()->isForeignKeyFromPk())
|
||||
{
|
||||
icon = wxBitmap(*ddprimaryforeignkey_png_img);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = wxBitmap(*ddprimaryforeignkeyfromuk_png_img);
|
||||
}
|
||||
}
|
||||
else if(isUniqueKey())
|
||||
{
|
||||
icon = wxBitmap(*ddprimarykeyuniquekey_png_img);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = wxBitmap(*ddprimarykey_png_img);
|
||||
}
|
||||
}
|
||||
else if(isUniqueKey())
|
||||
{
|
||||
if(isForeignKey())
|
||||
{
|
||||
if(getOwnerColumn()->isForeignKeyFromPk())
|
||||
{
|
||||
icon = wxBitmap(*ddforeignkeyuniquekey_png_img);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = wxBitmap(*ddforeignkeyuniquekeyfromuk_png_img);
|
||||
}
|
||||
|
||||
}
|
||||
else if(isPrimaryKey())
|
||||
{
|
||||
icon = wxBitmap(*ddprimarykeyuniquekey_png_img);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = wxBitmap(*ddunique_png_img);
|
||||
}
|
||||
|
||||
}
|
||||
else if(isForeignKey() && !isPrimaryKey() && !isUniqueKey() )
|
||||
{
|
||||
|
||||
if(getOwnerColumn()->isForeignKeyFromPk())
|
||||
{
|
||||
icon = wxBitmap(*ddforeignkey_png_img);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = wxBitmap(*ddforeignkeyfromuk_png_img);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(isNone())
|
||||
{
|
||||
iconToDraw = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
iconToDraw = &icon;
|
||||
}
|
||||
}
|
||||
|
||||
bool ddColumnKindIcon::isPrimaryKey()
|
||||
{
|
||||
return isPk;
|
||||
}
|
||||
|
||||
bool ddColumnKindIcon::isUniqueKey()
|
||||
{
|
||||
return ukIndex >= 0;
|
||||
}
|
||||
|
||||
bool ddColumnKindIcon::isUniqueKey(int uniqueIndex)
|
||||
{
|
||||
return (ukIndex == uniqueIndex);
|
||||
}
|
||||
|
||||
bool ddColumnKindIcon::isNone()
|
||||
{
|
||||
return !isUniqueKey() && !isPrimaryKey() && !isForeignKey();
|
||||
}
|
||||
bool ddColumnKindIcon::isForeignKey()
|
||||
{
|
||||
return getOwnerColumn()->isForeignKey();
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::disableUniqueKey()
|
||||
{
|
||||
syncUkIndexes(); //prepare to remove uk attribute to this column
|
||||
ukIndex = -1;
|
||||
getOwnerColumn()->getOwnerTable()->updateFkObservers();
|
||||
setRightIconForColumn();
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::disablePrimaryKey()
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->prepareForDeleteFkColumn(getOwnerColumn());
|
||||
isPk = false;
|
||||
getOwnerColumn()->getOwnerTable()->updateFkObservers();
|
||||
setRightIconForColumn();
|
||||
}
|
||||
|
||||
void ddColumnKindIcon::enablePrimaryKey()
|
||||
{
|
||||
isPk = true;
|
||||
getOwnerColumn()->getOwnerTable()->updateFkObservers();
|
||||
setRightIconForColumn();
|
||||
}
|
||||
|
||||
//Only used for figures created from storage
|
||||
void ddColumnKindIcon::setPrimaryKey(bool value)
|
||||
{
|
||||
isPk = value;
|
||||
setRightIconForColumn();
|
||||
}
|
||||
|
||||
//Only used for figures created from storage
|
||||
void ddColumnKindIcon::setUkIndex(int ukIdx)
|
||||
{
|
||||
ukIndex = ukIdx;
|
||||
}
|
||||
128
dd/dditems/figures/ddColumnOptionIcon.cpp
Normal file
128
dd/dditems/figures/ddColumnOptionIcon.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddColumnOptionIcon.cpp - Part of composite column figure, allow to choose between: not null and null
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/pen.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddColumnOptionIcon.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "dd/dditems/figures/ddColumnFigure.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddnull.pngc"
|
||||
#include "images/ddnotnull.pngc"
|
||||
|
||||
ddColumnOptionIcon::ddColumnOptionIcon(ddColumnFigure *owner)
|
||||
{
|
||||
setKindId(DDCOLUMNOPTIONICON);
|
||||
ownerColumn = owner;
|
||||
colOption = null;
|
||||
icon = wxBitmap(*ddnull_png_img);
|
||||
iconToDraw = &icon;
|
||||
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
|
||||
|
||||
//Set Value default Attributes
|
||||
fontAttribute->font().SetPointSize(owner->fontAttribute->font().GetPointSize());
|
||||
}
|
||||
|
||||
ddColumnOptionIcon::~ddColumnOptionIcon()
|
||||
{
|
||||
}
|
||||
|
||||
void ddColumnOptionIcon::createMenu(wxMenu &mnu)
|
||||
{
|
||||
wxMenuItem *item;
|
||||
|
||||
item = mnu.AppendCheckItem(MNU_COLNULL, _("NULL"));
|
||||
item->Check(colOption == null);
|
||||
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
|
||||
item = mnu.AppendCheckItem(MNU_COLNOTNULL, _("Not NULL"));
|
||||
item->Check(colOption == notnull);
|
||||
item->Enable(!getOwnerColumn()->isGeneratedForeignKey());
|
||||
}
|
||||
|
||||
void ddColumnOptionIcon::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
|
||||
{
|
||||
changeIcon((ddColumnOptionType)event.GetId());
|
||||
}
|
||||
|
||||
void ddColumnOptionIcon::changeIcon(ddColumnOptionType type)
|
||||
{
|
||||
colOption = type;
|
||||
switch(type)
|
||||
{
|
||||
case MNU_COLNULL:
|
||||
icon = wxBitmap(*ddnull_png_img);
|
||||
if(getOwnerColumn()->isPrimaryKey())
|
||||
{
|
||||
if(getOwnerColumn()->isForeignKey() || getOwnerColumn()->isUniqueKey())
|
||||
{
|
||||
getOwnerColumn()->toggleColumnKind(pk); //remove pk attribute because column now is null
|
||||
getOwnerColumn()->setRightIconForColumn();
|
||||
}
|
||||
else
|
||||
{
|
||||
getOwnerColumn()->disablePrimaryKey();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MNU_COLNOTNULL:
|
||||
icon = wxBitmap(*ddnotnull_png_img);
|
||||
break;
|
||||
}
|
||||
getBasicDisplayBox().SetSize(wxSize(getWidth(), getHeight()));
|
||||
}
|
||||
|
||||
void ddColumnOptionIcon::basicDraw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
if(iconToDraw)
|
||||
{
|
||||
hdRect copy = displayBox().gethdRect(view->getIdx());
|
||||
view->CalcScrolledPosition(copy.x, copy.y, ©.x, ©.y);
|
||||
context.DrawBitmap(*iconToDraw, copy.GetPosition(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnOptionIcon::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
basicDraw(context, view);
|
||||
}
|
||||
|
||||
int ddColumnOptionIcon::getWidth()
|
||||
{
|
||||
if(iconToDraw)
|
||||
return iconToDraw->GetWidth();
|
||||
else
|
||||
return 8;
|
||||
}
|
||||
|
||||
int ddColumnOptionIcon::getHeight()
|
||||
{
|
||||
if(iconToDraw)
|
||||
return iconToDraw->GetHeight();
|
||||
else
|
||||
return 10;
|
||||
}
|
||||
|
||||
ddColumnOptionType ddColumnOptionIcon::getOption()
|
||||
{
|
||||
return colOption;
|
||||
}
|
||||
|
||||
ddColumnFigure *ddColumnOptionIcon::getOwnerColumn()
|
||||
{
|
||||
return ownerColumn;
|
||||
}
|
||||
930
dd/dditems/figures/ddRelationshipFigure.cpp
Normal file
930
dd/dditems/figures/ddRelationshipFigure.cpp
Normal file
|
|
@ -0,0 +1,930 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddRelationshipFigure.cpp - Figure to draw foreign keys between tables.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipItem.h"
|
||||
#include "dd/ddmodel/ddDrawingEditor.h"
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "dd/dditems/utilities/ddSelectKindFksDialog.h"
|
||||
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
|
||||
|
||||
|
||||
ddRelationshipFigure::ddRelationshipFigure():
|
||||
hdLineConnection()
|
||||
{
|
||||
constraintName = wxEmptyString;
|
||||
setKindId(DDRELATIONSHIPFIGURE);
|
||||
fkFromPk = true;
|
||||
fkMandatory = true;
|
||||
fkOneToMany = true;
|
||||
fkIdentifying = false;
|
||||
matchSimple = false;
|
||||
ukIndex = -1;
|
||||
disconnectedEndTable = NULL;
|
||||
paintingFkColumns = false;
|
||||
//DEFERRABLE, NOT DEFERRABLE, INITIALLY IMMEDIATE, INITIALLY DEFERRED can be added in a future
|
||||
onUpdateAction = FK_ACTION_NO;
|
||||
onDeleteAction = FK_ACTION_NO;
|
||||
enablePopUp();
|
||||
}
|
||||
|
||||
ddRelationshipFigure::ddRelationshipFigure(int posIdx, hdIFigure *figure1, hdIFigure *figure2):
|
||||
hdLineConnection(posIdx, figure1, figure2)
|
||||
{
|
||||
enablePopUp();
|
||||
}
|
||||
|
||||
ddRelationshipFigure::~ddRelationshipFigure()
|
||||
{
|
||||
columnsHashMap::iterator it;
|
||||
ddRelationshipItem *item;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
delete item;
|
||||
}
|
||||
chm.clear();
|
||||
}
|
||||
|
||||
wxString ddRelationshipFigure::getConstraintName()
|
||||
{
|
||||
return constraintName;
|
||||
}
|
||||
|
||||
//This function avoid a bug with recursive indentifying relationships when
|
||||
//a column is removed from pk or delete.
|
||||
void ddRelationshipFigure::prepareFkForDelete(ddColumnFigure *column)
|
||||
{
|
||||
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
|
||||
ddRelationshipItem *fkColumnRelItem;
|
||||
|
||||
columnsHashMap::iterator it;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
fkColumnRelItem = it->second;
|
||||
if(fkColumnRelItem->original == column)
|
||||
fkColumnRelItem->original = NULL;
|
||||
}
|
||||
//go recursive to next table
|
||||
if(endTable)
|
||||
endTable->prepareForDeleteFkColumn(column);
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::updateForeignKey()
|
||||
{
|
||||
if(getEndFigure() && getStartFigure() && getStartFigure()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo) && getEndFigure()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo))
|
||||
{
|
||||
ddTableFigure *startTable = (ddTableFigure *) getStartFigure();
|
||||
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
|
||||
ddColumnFigure *col;
|
||||
ddRelationshipItem *fkColumnRelItem;
|
||||
|
||||
//STEP 0: Look for changes on source columns names and short table names.
|
||||
|
||||
//Before iterate over all columns of source table look if any column used at relationship
|
||||
//and changed their name and updated it when needed
|
||||
columnsHashMap::iterator it;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
fkColumnRelItem = it->second;
|
||||
if(fkColumnRelItem->original != NULL) //original column wasn't mark for delete.
|
||||
{
|
||||
//if autonaming is set on this item and column name at original source column of fk have been changed
|
||||
if(!fkColumnRelItem->original->getColumnName(false).IsSameAs(fkColumnRelItem->originalStartColName, false))
|
||||
{
|
||||
chm[fkColumnRelItem->original->getColumnName(false)] = fkColumnRelItem;
|
||||
chm[fkColumnRelItem->originalStartColName] = NULL;
|
||||
chm.erase(it);
|
||||
fkColumnRelItem->syncAutoFkName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//STEP 1: Look at all source table columns add and delete fk
|
||||
hdIteratorBase *iterator = startTable->figuresEnumerator();
|
||||
iterator->Next(); //first figure is main rect
|
||||
iterator->Next(); //second figure is table title
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
col = (ddColumnFigure *) iterator->Next();
|
||||
if(fkFromPk) //fk is generated from a pk column: add new fk/pk(s) column(s) from source fk table to destination
|
||||
{
|
||||
//STEP 1.1a: Add fk columns from table source pk if not exists using same name
|
||||
it = chm.find(col->getColumnName());
|
||||
bool NotFound = it == chm.end();
|
||||
|
||||
if( col->isPrimaryKey() && NotFound )
|
||||
{
|
||||
fkColumnRelItem = new ddRelationshipItem(this, col, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : none) );
|
||||
chm[col->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
|
||||
endTable->addColumn(0, fkColumnRelItem->fkColumn);
|
||||
updateConnection(0);
|
||||
}
|
||||
|
||||
//STEP 1.2a: Delete old Fk columns not pk now or deleted from source fk table.
|
||||
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
|
||||
bool repeat;
|
||||
do
|
||||
{
|
||||
repeat = false;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
fkColumnRelItem = it->second;
|
||||
if( fkColumnRelItem->original == NULL || !fkColumnRelItem->original->isPrimaryKey() || !startTable->includes(fkColumnRelItem->original) ) //order matters fkColumnRelItem->original==NULL short circuit evaluation should be first
|
||||
{
|
||||
if(fkColumnRelItem->isAutomaticallyGenerated()) //don't remove from fk_dest table fk column created from existing column, just mark now as not foreign key
|
||||
{
|
||||
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
|
||||
}
|
||||
else
|
||||
{
|
||||
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
|
||||
}
|
||||
chm.erase(it);
|
||||
delete fkColumnRelItem;
|
||||
repeat = true;
|
||||
updateConnection(0);
|
||||
}
|
||||
if (repeat)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(repeat);
|
||||
}
|
||||
else //fk is generated from a uk column: add new fk/pk(s) column(s) from source fk table to destination
|
||||
{
|
||||
//STEP 1.1b: Add fk columns from table source uk if not exists
|
||||
columnsHashMap::iterator it = chm.find(col->getColumnName());
|
||||
bool NotFound = it == chm.end();
|
||||
|
||||
if( col->isUniqueKey(ukIndex) && NotFound )
|
||||
{
|
||||
fkColumnRelItem = new ddRelationshipItem(this, col, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : none) );
|
||||
chm[col->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
|
||||
endTable->addColumn(0, fkColumnRelItem->fkColumn);
|
||||
updateConnection(0);
|
||||
}
|
||||
|
||||
//STEP 1.2b: Delete old Fk columns not pk now or deleted from source fk table.
|
||||
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
|
||||
bool repeat;
|
||||
do
|
||||
{
|
||||
repeat = false;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
fkColumnRelItem = it->second;
|
||||
if( fkColumnRelItem->original == NULL || !fkColumnRelItem->original->isUniqueKey(ukIndex) || !startTable->includes(fkColumnRelItem->original) ) //order matters fkColumnRelItem->original==NULL short circuit evaluation should be first
|
||||
{
|
||||
if(fkColumnRelItem->isAutomaticallyGenerated()) //don't remove from fk_dest table fk column created from existing column, just mark now as not foreign key
|
||||
{
|
||||
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
|
||||
}
|
||||
else
|
||||
{
|
||||
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
|
||||
}
|
||||
chm.erase(it);
|
||||
delete fkColumnRelItem;
|
||||
repeat = true;
|
||||
updateConnection(0);
|
||||
}
|
||||
if (repeat)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(repeat);
|
||||
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
//Now if relationship is an identifying one, I should alert all observers of my observer to update it.
|
||||
if(fkIdentifying)
|
||||
{
|
||||
if(endTable)
|
||||
endTable->updateFkObservers();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(wxT("Error invalid kind of start figure at relationship"), wxT("Error invalid kind of start figure at relationship"), wxICON_ERROR | wxOK);
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::createMenu(wxMenu &mnu)
|
||||
{
|
||||
wxMenu *submenu;
|
||||
wxMenuItem *item;
|
||||
wxString tmp;
|
||||
|
||||
submenu = new wxMenu(_("Select one"));
|
||||
|
||||
tmp = _("Foreign Key From");
|
||||
mnu.AppendSubMenu(submenu, tmp);
|
||||
item = submenu->AppendCheckItem(MNU_FKEYFROMPKEY, _("Primary Key"));
|
||||
item->Check(fkFromPk);
|
||||
|
||||
if(getStartFigure())
|
||||
{
|
||||
ddTableFigure *startTable = (ddTableFigure *) getStartFigure();
|
||||
int i, last = startTable->getUkConstraintsNames().Count();
|
||||
int eventID; //Hack to allow multiple submenu items for select uk as fk origin
|
||||
for(i = 0; i < last; i++)
|
||||
{
|
||||
eventID = MNU_FKEYFROMUKEYBASE + i;
|
||||
tmp = _("Unique Key: ");
|
||||
tmp += startTable->getUkConstraintsNames()[i];
|
||||
item = submenu->AppendCheckItem(eventID, tmp );
|
||||
item->Check(!fkFromPk && ukIndex == i);
|
||||
}
|
||||
}
|
||||
mnu.AppendSeparator();
|
||||
//disable right now item = mnu.AppendCheckItem(MNU_FKEYCUSTOMMAPPING, _("Foreign Key Columns Custom Mapping"));
|
||||
item = mnu.AppendCheckItem(MNU_MANDATORYRELATIONSHIP, _("Mandatory relationship kind"));
|
||||
item->Check(fkMandatory);
|
||||
item = mnu.AppendCheckItem(MNU_IDENTIFYINGRELATIONSHIP, _("Identifying relationship"));
|
||||
item->Check(fkIdentifying);
|
||||
mnu.AppendSeparator();
|
||||
item = mnu.AppendCheckItem(MNU_1MRELATIONSHIP, _("1:M"));
|
||||
item->Check(fkOneToMany);
|
||||
mnu.AppendSeparator();
|
||||
mnu.Append(MNU_FKCONSTRAINTNAME, _("Foreign Key Constraint Name"));
|
||||
|
||||
submenu = new wxMenu(_("Select one"));
|
||||
item = submenu->AppendCheckItem(MNU_FKMATCHTYPESIMPLE, _("Type Simple"));
|
||||
item->Check(matchSimple);
|
||||
item = submenu->AppendCheckItem(MNU_FKMATCHTYPEFULL, _("Type Full"));
|
||||
item->Check(!matchSimple);
|
||||
tmp = _("Match Type ");
|
||||
if(matchSimple)
|
||||
tmp += _("[ Simple ]");
|
||||
else
|
||||
tmp += _("[ Full ]");
|
||||
mnu.AppendSubMenu(submenu, tmp);
|
||||
|
||||
tmp = _("On Delete ");
|
||||
submenu = new wxMenu(_("Select one"));
|
||||
item = submenu->AppendCheckItem(MNU_FKONDELETENOACTION, _("No Action"));
|
||||
item->Check(onDeleteAction == FK_ACTION_NO);
|
||||
if(onDeleteAction == FK_ACTION_NO)
|
||||
tmp += _("[ No Action ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONDELETERESTRICT, _("Restrict"));
|
||||
item->Check(onDeleteAction == FK_RESTRICT);
|
||||
if(onDeleteAction == FK_RESTRICT)
|
||||
tmp += _("[ Restrict ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONDELETECASCADE, _("Cascade"));
|
||||
item->Check(onDeleteAction == FK_CASCADE);
|
||||
if(onDeleteAction == FK_CASCADE)
|
||||
tmp += _("[ Cascade ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONDELETESETNULL, _("Set Null"));
|
||||
item->Check(onDeleteAction == FK_SETNULL);
|
||||
if(onDeleteAction == FK_SETNULL)
|
||||
tmp += _("[ Set Null ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONDELETESETDEFAULT, _("Set Default"));
|
||||
item->Check(onDeleteAction == FK_SETDEFAULT);
|
||||
if(onDeleteAction == FK_SETDEFAULT)
|
||||
tmp += _("[ Set Default ]");
|
||||
mnu.AppendSubMenu(submenu, tmp);
|
||||
|
||||
tmp = _("On Update ");
|
||||
submenu = new wxMenu(_("Select one"));
|
||||
item = submenu->AppendCheckItem(MNU_FKONUPDATENOACTION, _("No Action"));
|
||||
item->Check(onUpdateAction == FK_ACTION_NO);
|
||||
if(onUpdateAction == FK_ACTION_NO)
|
||||
tmp += _("[ No Action ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONUPDATERESTRICT, _("Restrict"));
|
||||
item->Check(onUpdateAction == FK_RESTRICT);
|
||||
if(onUpdateAction == FK_RESTRICT)
|
||||
tmp += _("[ Restrict ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONUPDATECASCADE, _("Cascade"));
|
||||
item->Check(onUpdateAction == FK_CASCADE);
|
||||
if(onUpdateAction == FK_CASCADE)
|
||||
tmp += _("[ Cascade ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONUPDATESETNULL, _("Set Null"));
|
||||
item->Check(onUpdateAction == FK_SETNULL);
|
||||
if(onUpdateAction == FK_SETNULL)
|
||||
tmp += _("[ Set Null ]");
|
||||
item = submenu->AppendCheckItem(MNU_FKONUPDATESETDEFAULT, _("Set Default"));
|
||||
item->Check(onUpdateAction == FK_SETDEFAULT);
|
||||
if(onUpdateAction == FK_SETDEFAULT)
|
||||
tmp += _("[ Set Default ]");
|
||||
mnu.AppendSubMenu(submenu, tmp);
|
||||
|
||||
mnu.AppendSeparator();
|
||||
mnu.Append(MNU_DELETERELATIONSHIP, _("Delete Relationship..."));
|
||||
};
|
||||
|
||||
void ddRelationshipFigure::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
|
||||
{
|
||||
int answer;
|
||||
ddTableFigure *startTable = NULL;
|
||||
ddTableFigure *endTable = NULL;
|
||||
wxTextEntryDialog *nameDialog = NULL;
|
||||
hdRemoveDeleteDialog *delremDialog = NULL;
|
||||
ddSelectKindFksDialog *mappingDialog = NULL;
|
||||
wxString tmpString;
|
||||
|
||||
switch(event.GetId())
|
||||
{
|
||||
case MNU_MANDATORYRELATIONSHIP:
|
||||
fkMandatory = !fkMandatory;
|
||||
if(fkMandatory)
|
||||
{
|
||||
setLinePen(wxPen(*wxBLACK_PEN));
|
||||
setOptionAtForeignKeys(notnull);
|
||||
}
|
||||
else
|
||||
{
|
||||
fkIdentifying = false;
|
||||
setLinePen(wxPen(*wxBLACK, 1, wxSHORT_DASH));
|
||||
setOptionAtForeignKeys(null);
|
||||
}
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_IDENTIFYINGRELATIONSHIP:
|
||||
fkMandatory = true;
|
||||
setLinePen(wxPen(*wxBLACK_PEN));
|
||||
fkIdentifying = !fkIdentifying;
|
||||
fkOneToMany = true;
|
||||
updatePkAtFkCols();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKCONSTRAINTNAME:
|
||||
startTable = (ddTableFigure *) getStartFigure();
|
||||
endTable = (ddTableFigure *) getEndFigure();
|
||||
if(constraintName.IsEmpty() && startTable && endTable )
|
||||
{
|
||||
constraintName = endTable->getTableName();
|
||||
constraintName += _("_");
|
||||
constraintName += startTable->getTableName();
|
||||
}
|
||||
nameDialog = new wxTextEntryDialog(view, wxT("Change Constraint Name"), wxT("Constraint Name"), constraintName );
|
||||
answer = nameDialog->ShowModal();
|
||||
if (answer == wxID_OK)
|
||||
{
|
||||
tmpString = nameDialog->GetValue();
|
||||
constraintName = tmpString;
|
||||
view->notifyChanged();
|
||||
}
|
||||
delete nameDialog;
|
||||
break;
|
||||
case MNU_FKMATCHTYPEFULL:
|
||||
matchSimple = false;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKMATCHTYPESIMPLE:
|
||||
matchSimple = true;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONDELETENOACTION:
|
||||
onDeleteAction = FK_ACTION_NO;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONDELETERESTRICT:
|
||||
onDeleteAction = FK_RESTRICT;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONDELETECASCADE:
|
||||
onDeleteAction = FK_CASCADE;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONDELETESETNULL:
|
||||
onDeleteAction = FK_SETNULL;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONDELETESETDEFAULT:
|
||||
onDeleteAction = FK_SETDEFAULT;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
|
||||
case MNU_FKONUPDATENOACTION:
|
||||
onUpdateAction = FK_ACTION_NO;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONUPDATERESTRICT:
|
||||
onUpdateAction = FK_RESTRICT;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONUPDATECASCADE:
|
||||
onUpdateAction = FK_CASCADE;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONUPDATESETNULL:
|
||||
onUpdateAction = FK_SETNULL;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKONUPDATESETDEFAULT:
|
||||
onUpdateAction = FK_SETDEFAULT;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_1MRELATIONSHIP:
|
||||
fkOneToMany = !fkOneToMany;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_DELETERELATIONSHIP:
|
||||
if(getStartFigure() && getEndFigure())
|
||||
{
|
||||
ddTableFigure *t1 = (ddTableFigure *)getStartFigure();
|
||||
ddTableFigure *t2 = (ddTableFigure *)getEndFigure();
|
||||
//Relationship can be delete only NOT REMOVED
|
||||
delremDialog = new hdRemoveDeleteDialog(
|
||||
wxString::Format(_("Are you sure you wish to delete relationship between tables %s and %s?"),
|
||||
t1->getTableName().c_str(), t2->getTableName().c_str()),
|
||||
_("Delete relationship?"),
|
||||
(wxScrolledWindow *)view,
|
||||
false);
|
||||
answer = delremDialog->ShowModal();
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
if (answer == DD_DELETE)
|
||||
{
|
||||
editor->removeFromAllSelections(this);
|
||||
removeForeignKeys();
|
||||
disconnectStart();
|
||||
disconnectEnd();
|
||||
//Hack to autodelete relationship
|
||||
ddRelationshipFigure *r = this;
|
||||
if(r)
|
||||
editor->deleteModelFigure(r);
|
||||
editor->getDesign()->refreshBrowser();
|
||||
view->notifyChanged();
|
||||
}
|
||||
else if(answer == DD_REMOVE)
|
||||
{
|
||||
editor->getExistingDiagram(view->getIdx())->removeFromSelection(this);
|
||||
editor->getExistingDiagram(view->getIdx())->remove(this);
|
||||
view->notifyChanged();
|
||||
}
|
||||
delete delremDialog;
|
||||
}
|
||||
break;
|
||||
case MNU_FKEYFROMPKEY:
|
||||
fkFromPk = true;
|
||||
updateForeignKey();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_FKEYCUSTOMMAPPING:
|
||||
//disable right now
|
||||
/*
|
||||
mappingDialog = new ddSelectKindFksDialog(view,this);
|
||||
mappingDialog->ShowModal();
|
||||
delete mappingDialog;
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
//Hack to allow multiple selection of Uk in submenu
|
||||
answer = event.GetId();
|
||||
if( answer >= MNU_FKEYFROMUKEYBASE)
|
||||
{
|
||||
fkFromPk = false;
|
||||
ukIndex = answer - MNU_FKEYFROMUKEYBASE;
|
||||
updateForeignKey();
|
||||
view->notifyChanged();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ddRelationshipFigure::getIdentifying()
|
||||
{
|
||||
return fkIdentifying;
|
||||
}
|
||||
|
||||
bool ddRelationshipFigure::getOneToMany()
|
||||
{
|
||||
return fkOneToMany;
|
||||
}
|
||||
|
||||
bool ddRelationshipFigure::getMandatory()
|
||||
{
|
||||
return fkMandatory;
|
||||
}
|
||||
|
||||
|
||||
// relationship is observed by several tables at same time, one is the
|
||||
// owner (start connector table) others are just observers of that
|
||||
// relationship (end connectors table)
|
||||
void ddRelationshipFigure::connectEnd(hdIConnector *end, hdDrawingView *view)
|
||||
{
|
||||
ddSelectKindFksDialog *mappingDialog = NULL;
|
||||
hdLineConnection::connectEnd(end);
|
||||
if(view)
|
||||
view->Refresh();
|
||||
|
||||
//Hack to allow abort of mapping of Fk
|
||||
int answer = -1; //By default
|
||||
|
||||
//If connecte destination and start figure
|
||||
if(getEndFigure() && getStartFigure())
|
||||
{
|
||||
mappingDialog = new ddSelectKindFksDialog(view, this);
|
||||
answer = mappingDialog->ShowModal();
|
||||
delete mappingDialog;
|
||||
if(answer == wxID_OK)
|
||||
{
|
||||
if(view)
|
||||
{
|
||||
view->AcceptsFocus();
|
||||
view->SetFocus();
|
||||
}
|
||||
updateForeignKey();
|
||||
}
|
||||
}
|
||||
|
||||
//Is Start is connected Tell to check consistency
|
||||
if(getStartFigure())
|
||||
{
|
||||
getStartTable()->setSelectFkDestMode(false);
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
editor->checkAllDigramsRelConsistency();
|
||||
}
|
||||
|
||||
//Hack to Abort Connection because user press cancel button at fk mapping dialog
|
||||
if (answer == wxID_CANCEL)
|
||||
{
|
||||
//By disconnecting both sides I force to delete relationship at ConnectionCreationTool
|
||||
getStartTable()->setSelectFkDestMode(false);
|
||||
this->disconnectStart();
|
||||
this->disconnectEnd();
|
||||
}
|
||||
}
|
||||
|
||||
bool ddRelationshipFigure::isForeignKeyFromPk()
|
||||
{
|
||||
return fkFromPk;
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::setFkFrom(bool primaryKey, int useUkIndex, bool issueUpdateFk)
|
||||
{
|
||||
if(useUkIndex >= 0)
|
||||
{
|
||||
fkFromPk = false;
|
||||
ukIndex = useUkIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
fkFromPk = true;
|
||||
ukIndex = -1;
|
||||
}
|
||||
|
||||
if(issueUpdateFk)
|
||||
updateForeignKey();
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::connectStart(hdIConnector *start, hdDrawingView *view)
|
||||
{
|
||||
hdLineConnection::connectStart(start);
|
||||
if(getEndFigure() && getStartFigure())
|
||||
updateForeignKey();
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::disconnectStart(hdDrawingView *view)
|
||||
{
|
||||
paintingFkColumns = false;
|
||||
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
|
||||
disconnectedEndTable = (ddTableFigure *) getEndFigure();
|
||||
removeForeignKeys();
|
||||
hdLineConnection::disconnectStart();
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::disconnectEnd(hdDrawingView *view)
|
||||
{
|
||||
paintingFkColumns = false;
|
||||
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
|
||||
disconnectedEndTable = (ddTableFigure *) getEndFigure();
|
||||
hdLineConnection::disconnectEnd();
|
||||
removeForeignKeys();
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::addExistingColumnFk(ddColumnFigure *startTablesourceCol, wxString destColumn)
|
||||
{
|
||||
ddTableFigure *endTable = (ddTableFigure *) getEndFigure();
|
||||
ddRelationshipItem *fkColumnRelItem;
|
||||
ddColumnFigure *endTablesourceCol = endTable->getColumnByName(destColumn);
|
||||
//Create a new relationship item but with an existing column for fk at destination table
|
||||
if(endTablesourceCol)
|
||||
{
|
||||
fkColumnRelItem = new ddRelationshipItem(this, startTablesourceCol, endTable, (fkMandatory ? notnull : null), (fkIdentifying ? pk : noaction), endTablesourceCol);
|
||||
//Mark it as Custom Fk (fk from existing column not an automatic generated)
|
||||
endTablesourceCol->setAsUserCreatedFk(fkColumnRelItem);
|
||||
chm[startTablesourceCol->getColumnName()] = fkColumnRelItem; //hashmap key will be original table name always
|
||||
updateConnection(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::removeForeignKeys()
|
||||
{
|
||||
if(disconnectedEndTable)
|
||||
{
|
||||
columnsHashMap::iterator it;
|
||||
ddRelationshipItem *fkColumnRelItem;
|
||||
|
||||
//This part of code (repeat) is a hack cause every time a column is delete hashmap is modified inside and becomes invalid iterator at that loop
|
||||
bool repeat;
|
||||
do
|
||||
{
|
||||
repeat = false;
|
||||
for( it = chm.begin(); it != chm.end(); ++it )
|
||||
{
|
||||
wxString key = it->first;
|
||||
fkColumnRelItem = it->second;
|
||||
if(fkColumnRelItem->getDestinationTable()->includes(fkColumnRelItem->fkColumn))
|
||||
{
|
||||
//Remove fk column only if that column is automatically generated
|
||||
if(fkColumnRelItem->isAutomaticallyGenerated())
|
||||
{
|
||||
fkColumnRelItem->getDestinationTable()->removeColumn(0, fkColumnRelItem->fkColumn);
|
||||
} //is an existing column use as fk
|
||||
else
|
||||
{
|
||||
//Mark as existing column not used as foreign key destination
|
||||
fkColumnRelItem->fkColumn->setAsUserCreatedFk(NULL);
|
||||
}
|
||||
chm.erase(it);
|
||||
delete fkColumnRelItem;
|
||||
repeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(repeat);
|
||||
chm.clear();
|
||||
disconnectedEndTable = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::setOptionAtForeignKeys(ddColumnOptionType type)
|
||||
{
|
||||
columnsHashMap::iterator it;
|
||||
ddRelationshipItem *item;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
if(item->isAutomaticallyGenerated())
|
||||
item->fkColumn->setColumnOption(type);
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::updatePkAtFkCols()
|
||||
{
|
||||
bool changed = false;
|
||||
columnsHashMap::iterator it;
|
||||
ddRelationshipItem *item;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
if(item->isAutomaticallyGenerated()) //only update fk status at fk NOT created from an existing column
|
||||
{
|
||||
if(fkIdentifying)
|
||||
{
|
||||
item->fkColumn->enablePrimaryKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
item->fkColumn->disablePrimaryKey();
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(changed) //set as identifying relationship (hierarchy)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) getEndFigure();
|
||||
table->updateFkObservers();
|
||||
}
|
||||
}
|
||||
wxString ddRelationshipFigure::generateSQL(wxString schemaName)
|
||||
{
|
||||
wxString tmp;
|
||||
if(chm.size() > 0)
|
||||
{
|
||||
tmp = wxT("\nALTER TABLE ");
|
||||
if(!schemaName.IsEmpty())
|
||||
tmp += wxT("\"") + schemaName + wxT("\".");
|
||||
tmp += wxT("\"") + ((ddTableFigure *)getEndFigure())->getTableName() + wxT("\"") ;
|
||||
|
||||
tmp += wxT(" ADD ");
|
||||
if(!constraintName.IsEmpty())
|
||||
{
|
||||
tmp += wxT("CONSTRAINT \"") + constraintName + wxT("\" ");
|
||||
}
|
||||
tmp += wxT("FOREIGN KEY ( ");
|
||||
columnsHashMap::iterator it, end;
|
||||
ddRelationshipItem *item;
|
||||
for( it = chm.begin(); it != chm.end(); ++it )
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
tmp += wxT("\"") + item->fkColumn->getColumnName() + wxT("\"");
|
||||
end = it;
|
||||
end++;
|
||||
if(end != chm.end())
|
||||
{
|
||||
tmp += wxT(" , ");
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp += wxT(" )");
|
||||
}
|
||||
}
|
||||
|
||||
tmp += wxT(" REFERENCES ");
|
||||
if(!schemaName.IsEmpty())
|
||||
tmp += wxT("\"") + schemaName + _("\".");
|
||||
tmp += wxT("\"") + ((ddTableFigure *)getStartFigure())->getTableName() + wxT("\"") ;
|
||||
|
||||
tmp += wxT(" ( ");
|
||||
for( it = chm.begin(); it != chm.end(); ++it )
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
tmp += wxT("\"") + item->original->getColumnName() + wxT("\"");
|
||||
end = it;
|
||||
end++;
|
||||
if(end != chm.end())
|
||||
{
|
||||
tmp += wxT(" , ");
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp += wxT(" )");
|
||||
}
|
||||
}
|
||||
|
||||
if(matchSimple)
|
||||
tmp += wxT(" MATCH SIMPLE ");
|
||||
else
|
||||
tmp += wxT(" MATCH FULL ");
|
||||
|
||||
tmp += wxT(" ON DELETE ");
|
||||
switch(onDeleteAction)
|
||||
{
|
||||
case FK_ACTION_NO:
|
||||
tmp += wxT(" NO ACTION ");
|
||||
break;
|
||||
case FK_RESTRICT:
|
||||
tmp += wxT(" RESTRICT ");
|
||||
break;
|
||||
case FK_CASCADE:
|
||||
tmp += wxT(" CASCADE ");
|
||||
break;
|
||||
case FK_SETNULL:
|
||||
tmp += wxT(" SET NULL ");
|
||||
break;
|
||||
case FK_SETDEFAULT:
|
||||
tmp += wxT(" SET DEFAULT ");
|
||||
break;
|
||||
}
|
||||
|
||||
tmp += wxT(" ON UPDATE ");
|
||||
switch(onUpdateAction)
|
||||
{
|
||||
case FK_ACTION_NO:
|
||||
tmp += wxT(" NO ACTION ");
|
||||
break;
|
||||
case FK_RESTRICT:
|
||||
tmp += wxT(" RESTRICT ");
|
||||
break;
|
||||
case FK_CASCADE:
|
||||
tmp += wxT(" CASCADE ");
|
||||
break;
|
||||
case FK_SETNULL:
|
||||
tmp += wxT(" SET NULL ");
|
||||
break;
|
||||
case FK_SETDEFAULT:
|
||||
tmp += wxT(" SET DEFAULT ");
|
||||
break;
|
||||
}
|
||||
|
||||
tmp += _(";\n");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
ddTableFigure *ddRelationshipFigure::getStartTable()
|
||||
{
|
||||
return (ddTableFigure *) getStartFigure();
|
||||
}
|
||||
|
||||
ddTableFigure *ddRelationshipFigure::getEndTable()
|
||||
{
|
||||
return (ddTableFigure *) getEndFigure();
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
hdLineConnection::basicDrawSelected(context, view);
|
||||
if(getEndFigure() && getStartFigure())
|
||||
{
|
||||
paintingFkColumns = true;
|
||||
changeFkOSTextColor(wxColour(255, 0, 0), wxColour(255, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
hdLineConnection::basicDraw(context, view);
|
||||
if(getEndFigure() && getStartFigure() && paintingFkColumns)
|
||||
{
|
||||
paintingFkColumns = false;
|
||||
changeFkOSTextColor( *wxBLACK, *wxBLACK, true );
|
||||
}
|
||||
}
|
||||
|
||||
void ddRelationshipFigure::changeFkOSTextColor(wxColour originalColour, wxColour fkColour, bool reset)
|
||||
{
|
||||
columnsHashMap::iterator it;
|
||||
ddRelationshipItem *item;
|
||||
for (it = chm.begin(); it != chm.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
item = it->second;
|
||||
if(item->original)
|
||||
{
|
||||
if(!reset)
|
||||
item->original->setTextColour(originalColour);
|
||||
else
|
||||
item->original->setTextColour(item->original->getOwnerTable()->fontColorAttribute->fontColor);
|
||||
|
||||
}
|
||||
if(item->fkColumn)
|
||||
{
|
||||
if(!reset)
|
||||
item->fkColumn->setTextColour(fkColour);
|
||||
else
|
||||
item->fkColumn->setTextColour(item->fkColumn->getOwnerTable()->fontColorAttribute->fontColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
int ddRelationshipFigure::getUkIndex()
|
||||
{
|
||||
return ukIndex;
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
actionKind ddRelationshipFigure::getOnUpdateAction()
|
||||
{
|
||||
return onUpdateAction;
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
actionKind ddRelationshipFigure::getOnDeleteAction()
|
||||
{
|
||||
return onDeleteAction;
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
bool ddRelationshipFigure::getMatchSimple()
|
||||
{
|
||||
return matchSimple;
|
||||
}
|
||||
|
||||
//Used by persistence classes
|
||||
void ddRelationshipFigure::initRelationValues( ddTableFigure *source, ddTableFigure *destination, int ukIdx, wxString constraint, actionKind onUpdate, actionKind onDelete, bool simpleMatch, bool identifying, bool oneToMany, bool mandatory, bool fromPk )
|
||||
{
|
||||
ukIndex = ukIdx;
|
||||
fkFromPk = fromPk;
|
||||
fkMandatory = mandatory;
|
||||
fkOneToMany = oneToMany;
|
||||
fkIdentifying = identifying;
|
||||
matchSimple = simpleMatch;
|
||||
onUpdateAction = onUpdate;
|
||||
onDeleteAction = onDelete;
|
||||
constraintName = constraint;
|
||||
|
||||
//I need to add two points to relationship if there aren't any points for it a first position
|
||||
int i, start = pointCount(0);
|
||||
for(i = start; i < 2 ; i++)
|
||||
{
|
||||
addPoint(0, -100, -100);
|
||||
}
|
||||
|
||||
//reestablish connections between figures
|
||||
hdLineConnection::connectStart(source->connectorAt(0, getStartPoint(0).x, getStartPoint(0).y));
|
||||
hdLineConnection::connectEnd(destination->connectorAt(0, getEndPoint(0).x, getEndPoint(0).y));
|
||||
}
|
||||
132
dd/dditems/figures/ddRelationshipItem.cpp
Normal file
132
dd/dditems/figures/ddRelationshipItem.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddRelationshipItem.cpp - Items (fk columns) inside a relationship figure hashmap
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddRelationshipItem.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
|
||||
ddRelationshipItem::ddRelationshipItem()
|
||||
{
|
||||
ownerRel = NULL;
|
||||
original = NULL;
|
||||
destinationTable = NULL;
|
||||
}
|
||||
|
||||
ddRelationshipItem::ddRelationshipItem(ddRelationshipFigure *owner, ddColumnFigure *originalColumn, ddTableFigure *destination, ddColumnOptionType type, ddColumnType colType, ddColumnFigure *existingColumn)
|
||||
{
|
||||
ownerRel = owner;
|
||||
original = originalColumn;
|
||||
originalStartColName = original->getColumnName(false);
|
||||
destinationTable = destination;
|
||||
|
||||
if(existingColumn == NULL) //Fk destination column will be automatically generated
|
||||
{
|
||||
generatedName = autoGenerateNameForFk();
|
||||
fkColumn = new ddColumnFigure( generatedName, destinationTable, this);
|
||||
fkColumn->setColumnOption(type);
|
||||
fkColumn->toggleColumnKind(colType);
|
||||
fkColumn->activateGenFkName(); //By default fk name is generate by using ( alias | tableName) . ColumnName combination
|
||||
fkIsAutoGenerated = true;
|
||||
}
|
||||
else //using existing column as fk destination (Validation Required at user choices compatibility of types, precision and scale)
|
||||
{
|
||||
generatedName = wxEmptyString;
|
||||
fkColumn = existingColumn;
|
||||
fkColumn->setAsUserCreatedFk(this);
|
||||
fkColumn->deactivateGenFkName();
|
||||
fkIsAutoGenerated = false;
|
||||
}
|
||||
|
||||
fkColumn->setRightIconForColumn();
|
||||
}
|
||||
|
||||
ddRelationshipItem::~ddRelationshipItem()
|
||||
{
|
||||
}
|
||||
|
||||
void ddRelationshipItem::initRelationshipItemValues(ddRelationshipFigure *owner, ddTableFigure *destination, bool fromExistingColumn, ddColumnFigure *fkCol, ddColumnFigure *sourceCol, wxString initialColName)
|
||||
{
|
||||
ownerRel = owner;
|
||||
destinationTable = destination;
|
||||
fkIsAutoGenerated = fromExistingColumn;
|
||||
original = sourceCol;
|
||||
fkColumn = fkCol;
|
||||
originalStartColName = initialColName;
|
||||
if(fkIsAutoGenerated)
|
||||
fkColumn->setFkSource(this);
|
||||
else
|
||||
fkColumn->setAsUserCreatedFk(this);
|
||||
|
||||
fkColumn->setRightIconForColumn();
|
||||
}
|
||||
|
||||
|
||||
bool ddRelationshipItem::isAutomaticallyGenerated()
|
||||
{
|
||||
return fkIsAutoGenerated;
|
||||
}
|
||||
|
||||
wxString ddRelationshipItem::autoGenerateNameForFk()
|
||||
{
|
||||
wxString newName;
|
||||
newName = original->getOwnerTable()->getTableName();
|
||||
newName.append(wxT("_"));
|
||||
newName.append(originalStartColName);
|
||||
return newName;
|
||||
}
|
||||
|
||||
void ddRelationshipItem::syncAutoFkName()
|
||||
{
|
||||
originalStartColName = original->getColumnName(false); //Because original name was probably changed, now I should update it.
|
||||
if(fkColumn->isGeneratedForeignKey() && fkColumn->isFkNameGenerated() )
|
||||
{
|
||||
fkColumn->setColumnName(autoGenerateNameForFk());
|
||||
//Update all connections, but need to be notified to all views only doing it right now for first view
|
||||
ownerRel->updateConnection(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ddRelationshipItem::relationIsIdentifying()
|
||||
{
|
||||
return ownerRel->getIdentifying();
|
||||
}
|
||||
|
||||
bool ddRelationshipItem::relationIsMandatory()
|
||||
{
|
||||
return ownerRel->getMandatory();
|
||||
}
|
||||
|
||||
wxString ddRelationshipItem::sourceTableName()
|
||||
{
|
||||
return original->getOwnerTable()->getTableName();
|
||||
}
|
||||
|
||||
wxString ddRelationshipItem::destTableName()
|
||||
{
|
||||
return destinationTable->getTableName();
|
||||
}
|
||||
|
||||
bool ddRelationshipItem::isForeignKeyFromPk()
|
||||
{
|
||||
if(ownerRel)
|
||||
{
|
||||
return ownerRel->isForeignKeyFromPk();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
154
dd/dditems/figures/ddRelationshipTerminal.cpp
Normal file
154
dd/dditems/figures/ddRelationshipTerminal.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddRelationshipTerminal.cpp - Draw inverse arrows at fk terminal based on kind of relationship.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddRelationshipTerminal.h"
|
||||
#include "hotdraw/utilities/hdPoint.h"
|
||||
#include "hotdraw/utilities/hdRect.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "hotdraw/utilities/hdGeometry.h"
|
||||
|
||||
ddRelationshipTerminal::ddRelationshipTerminal(ddRelationshipFigure *owner, bool endFigureTerminal)
|
||||
{
|
||||
ownerFigure = owner;
|
||||
endTerminal = endFigureTerminal;
|
||||
}
|
||||
|
||||
ddRelationshipTerminal::~ddRelationshipTerminal()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddRelationshipTerminal::draw (wxBufferedDC &context, hdPoint &a, hdPoint &b, hdDrawingView *view)
|
||||
{
|
||||
hdGeometry g;
|
||||
hdPoint points[3];
|
||||
|
||||
context.SetPen(terminalLinePen);
|
||||
|
||||
hdPoint aCopy = a, bCopy = b;
|
||||
view->CalcScrolledPosition(aCopy.x, aCopy.y, &aCopy.x, &aCopy.y);
|
||||
view->CalcScrolledPosition(bCopy.x, bCopy.y, &bCopy.x, &bCopy.y);
|
||||
|
||||
if(endTerminal)
|
||||
{
|
||||
//Calc a point very far away of center of table to intersect one of the sides lines of the table rectangle figure
|
||||
double X = aCopy.x + (bCopy.x - aCopy.x) * 0.9;
|
||||
double Y = aCopy.y + (bCopy.y - aCopy.y) * 0.9;
|
||||
|
||||
if(ownerFigure->getEndFigure() && ownerFigure->getOneToMany())
|
||||
{
|
||||
hdRect r = ownerFigure->getEndFigure()->displayBox().gethdRect(view->getIdx());
|
||||
|
||||
view->CalcScrolledPosition(r.x, r.y, &r.x, &r.y);
|
||||
|
||||
int centerX = r.x + r.width / 2;
|
||||
int centerY = r.y + r.height / 2;
|
||||
|
||||
context.SetPen(*wxBLACK_PEN);
|
||||
context.SetBrush(*wxBLACK_BRUSH);
|
||||
|
||||
double XX, YY, distance;
|
||||
|
||||
//Calculate a new point to a given distance from the end of the relationship to draw many ( ----<| ) connector
|
||||
//first calculate vector from point1 & point2
|
||||
double vectorx = aCopy.x - bCopy.x;
|
||||
double vectory = aCopy.y - bCopy.y;
|
||||
//calculate the length
|
||||
double length = sqrt(vectorx * vectorx + vectory * vectory);
|
||||
//normalize the vector to unit length
|
||||
double normalizevx = vectorx / length;
|
||||
double normalizevy = vectory / length;
|
||||
distance = -15;
|
||||
//calculate point a given distance
|
||||
XX = bCopy.x + normalizevx * (length + distance);
|
||||
YY = bCopy.y + normalizevy * (length + distance);
|
||||
|
||||
wxPoint intersectionLine1(centerX, centerY);
|
||||
wxPoint intersectionLine2(X, Y);
|
||||
|
||||
//TOP
|
||||
if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopLeft(), r.GetTopRight()))
|
||||
{
|
||||
points[0] = wxPoint(XX, YY);
|
||||
points[1] = wxPoint(aCopy.x - 7, aCopy.y);
|
||||
points[2] = wxPoint(aCopy.x + 7, aCopy.y);
|
||||
context.DrawPolygon(3, points);
|
||||
|
||||
if(ownerFigure->getIdentifying())
|
||||
{
|
||||
context.SetPen(wxPen(*wxBLACK, 2));
|
||||
context.DrawLine(wxPoint(XX - 7, YY), wxPoint(XX + 7, YY));
|
||||
context.SetPen(*wxBLACK_PEN);
|
||||
}
|
||||
|
||||
} //RIGHT
|
||||
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopRight(), r.GetBottomRight()))
|
||||
{
|
||||
points[0] = wxPoint(XX, YY);
|
||||
points[1] = wxPoint(aCopy.x, aCopy.y - 7);
|
||||
points[2] = wxPoint(aCopy.x, aCopy.y + 7);
|
||||
context.DrawPolygon(3, points);
|
||||
|
||||
if(ownerFigure->getIdentifying())
|
||||
{
|
||||
context.SetPen(wxPen(*wxBLACK, 2));
|
||||
context.DrawLine(wxPoint(XX, YY - 7), wxPoint(XX, YY + 7));
|
||||
context.SetPen(*wxBLACK_PEN);
|
||||
}
|
||||
} //BOTTOM
|
||||
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetBottomLeft(), r.GetBottomRight()))
|
||||
{
|
||||
points[0] = wxPoint(XX, YY);
|
||||
points[1] = wxPoint(aCopy.x - 7, aCopy.y);
|
||||
points[2] = wxPoint(aCopy.x + 7, aCopy.y);
|
||||
context.DrawPolygon(3, points);
|
||||
|
||||
if(ownerFigure->getIdentifying())
|
||||
{
|
||||
context.SetPen(wxPen(*wxBLACK, 2));
|
||||
context.DrawLine(wxPoint(XX - 7, YY), wxPoint(XX + 7, YY));
|
||||
context.SetPen(*wxBLACK_PEN);
|
||||
}
|
||||
} //LEFT
|
||||
else if(g.intersection(intersectionLine1, intersectionLine2, r.GetTopLeft(), r.GetBottomLeft()))
|
||||
{
|
||||
points[0] = wxPoint(XX, YY);
|
||||
points[1] = wxPoint(aCopy.x, aCopy.y - 7);
|
||||
points[2] = wxPoint(aCopy.x, aCopy.y + 7);
|
||||
context.DrawPolygon(3, points);
|
||||
|
||||
if(ownerFigure->getIdentifying())
|
||||
{
|
||||
context.SetPen(wxPen(*wxBLACK, 2));
|
||||
context.DrawLine(wxPoint(XX, YY - 7), wxPoint(XX, YY + 7));
|
||||
context.SetPen(*wxBLACK_PEN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//CENTER of star figure or invalid place, do nothing
|
||||
}
|
||||
|
||||
value = hdPoint(XX, YY);
|
||||
return value;
|
||||
}
|
||||
value = hdPoint(0, 0);
|
||||
return value;
|
||||
}
|
||||
value = hdPoint(0, 0);
|
||||
return value;
|
||||
}
|
||||
1798
dd/dditems/figures/ddTableFigure.cpp
Normal file
1798
dd/dditems/figures/ddTableFigure.cpp
Normal file
File diff suppressed because it is too large
Load diff
582
dd/dditems/figures/ddTextTableItemFigure.cpp
Normal file
582
dd/dditems/figures/ddTextTableItemFigure.cpp
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddTextTableItemFigure.cpp - Draw a column inside a table
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/numdlg.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/figures/ddTextTableItemFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipItem.h"
|
||||
#include "dd/dditems/tools/ddColumnTextTool.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "hotdraw/figures/hdSimpleTextFigure.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "dd/ddmodel/ddDrawingEditor.h"
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/utilities/ddPrecisionScaleDialog.h"
|
||||
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
|
||||
|
||||
ddTextTableItemFigure::ddTextTableItemFigure(wxString &columnName, ddDataType dataType, ddColumnFigure *owner):
|
||||
hdSimpleTextFigure(columnName)
|
||||
{
|
||||
setKindId(DDTEXTTABLEITEMFIGURE);
|
||||
ownerTable = NULL; //table name item is the only one case of use of this variable
|
||||
oneTimeNoAlias = false;
|
||||
columnType = dataType;
|
||||
this->setEditable(true);
|
||||
enablePopUp();
|
||||
ownerColumn = owner;
|
||||
showDataType = true;
|
||||
showAlias = false;
|
||||
recalculateDisplayBox();
|
||||
precision = -1;
|
||||
scale = -1;
|
||||
|
||||
if(owner) //is Column Object
|
||||
{
|
||||
fontColorAttribute->fontColor = owner->getOwnerTable()->fontColorAttribute->fontColor;
|
||||
fontSelColorAttribute->fontColor = owner->getOwnerTable()->fontSelColorAttribute->fontColor;
|
||||
}
|
||||
}
|
||||
|
||||
ddTextTableItemFigure::~ddTextTableItemFigure()
|
||||
{
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::recalculateDisplayBox()
|
||||
{
|
||||
if (ownerColumn && ownerColumn->getOwnerTable())
|
||||
{
|
||||
hdSimpleTextFigure::recalculateDisplayBox();
|
||||
displayBox().width = ownerColumn->getOwnerTable()->getFiguresMaxWidth();
|
||||
}
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::displayBoxUpdate()
|
||||
{
|
||||
recalculateDisplayBox();
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setOwnerTable(ddTableFigure *table)
|
||||
{
|
||||
ownerTable = table;
|
||||
if(table)//is Table Name object
|
||||
{
|
||||
fontColorAttribute->fontColor = ownerTable->fontColorAttribute->fontColor;
|
||||
fontSelColorAttribute->fontColor = ownerTable->fontSelColorAttribute->fontColor;
|
||||
}
|
||||
}
|
||||
|
||||
wxString &ddTextTableItemFigure::getText(bool extended)
|
||||
{
|
||||
if(showDataType && extended && getOwnerColumn())
|
||||
{
|
||||
wxString ddType = dataTypes()[getDataType()]; //Should use getDataType() & getPrecision(), because when column is fk, type is not taken from this column, instead from original column (source of fk)
|
||||
bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
|
||||
if( havePrecision && getPrecision() >= 0)
|
||||
{
|
||||
ddType.Truncate(ddType.Find(wxT("(")));
|
||||
if(getScale() == -1)
|
||||
ddType += wxString::Format(wxT("(%d)"), getPrecision());
|
||||
else
|
||||
ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
|
||||
}
|
||||
//Fix to serial is integer at automatically generated foreign key
|
||||
if(getDataType() == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
|
||||
ddType = dataTypes()[dt_integer];
|
||||
|
||||
out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" : ")) + ddType );
|
||||
return out;
|
||||
}
|
||||
else if( showAlias && getOwnerColumn() == NULL )
|
||||
{
|
||||
if(!oneTimeNoAlias)
|
||||
out = wxString( hdSimpleTextFigure::getText() + wxString(wxT(" ( ")) + colAlias + wxString(wxT(" ) ")) );
|
||||
else
|
||||
{
|
||||
out = wxString( hdSimpleTextFigure::getText() );
|
||||
oneTimeNoAlias = false;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
return hdSimpleTextFigure::getText();
|
||||
}
|
||||
}
|
||||
|
||||
wxString ddTextTableItemFigure::getType(bool raw)
|
||||
{
|
||||
wxString ddType = dataTypes()[columnType];
|
||||
if(raw)
|
||||
return ddType;
|
||||
|
||||
bool havePrecision = columnType == dt_numeric || columnType == dt_bit || columnType == dt_char || columnType == dt_interval || columnType == dt_varbit || columnType == dt_varchar;
|
||||
if( havePrecision && getPrecision() >= 0)
|
||||
{
|
||||
ddType.Truncate(ddType.Find(wxT("(")));
|
||||
if(getScale() == -1)
|
||||
ddType += wxString::Format(wxT("(%d)"), getPrecision());
|
||||
else
|
||||
ddType += wxString::Format(wxT("(%d,%d)"), getPrecision(), getScale());
|
||||
}
|
||||
|
||||
//Fix to serial is integer at automatically generated foreign key
|
||||
if(columnType == dt_serial && getOwnerColumn()->isGeneratedForeignKey())
|
||||
ddType = dataTypes()[dt_integer];
|
||||
|
||||
return ddType;
|
||||
}
|
||||
|
||||
//WARNING: event ID must match enum ddDataType!!! this event was created on view
|
||||
void ddTextTableItemFigure::OnGenericPopupClick(wxCommandEvent &event, hdDrawingView *view)
|
||||
{
|
||||
wxTextEntryDialog *nameDialog = NULL;
|
||||
ddPrecisionScaleDialog *numericDialog = NULL;
|
||||
wxString tmpString;
|
||||
int answer;
|
||||
int tmpprecision;
|
||||
long tmpvalue;
|
||||
hdRemoveDeleteDialog *delremDialog = NULL;
|
||||
|
||||
switch(event.GetId())
|
||||
{
|
||||
case MNU_DDADDCOLUMN:
|
||||
nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Add a column"));
|
||||
answer = nameDialog->ShowModal();
|
||||
if (answer == wxID_OK)
|
||||
{
|
||||
tmpString = nameDialog->GetValue();
|
||||
getOwnerColumn()->getOwnerTable()->addColumn(view->getIdx(), new ddColumnFigure(tmpString, getOwnerColumn()->getOwnerTable()));
|
||||
view->notifyChanged();
|
||||
}
|
||||
delete nameDialog;
|
||||
break;
|
||||
case MNU_DELCOLUMN:
|
||||
answer = wxMessageBox(wxT("Are you sure you wish to delete column ") + getText(true) + wxT("?"), wxT("Delete column?"), wxYES_NO | wxNO_DEFAULT, view);
|
||||
if (answer == wxYES)
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->removeColumn(view->getIdx(), getOwnerColumn());
|
||||
view->notifyChanged();
|
||||
}
|
||||
break;
|
||||
case MNU_AUTONAMCOLUMN:
|
||||
getOwnerColumn()->activateGenFkName();
|
||||
getOwnerColumn()->getFkSource()->syncAutoFkName();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_RENAMECOLUMN:
|
||||
nameDialog = new wxTextEntryDialog(view, wxT("New column name"), wxT("Rename Column"), getText());
|
||||
nameDialog->ShowModal();
|
||||
if(getOwnerColumn()->isGeneratedForeignKey()) //after a manual user column rename, deactivated automatic generation of fk name.
|
||||
getOwnerColumn()->deactivateGenFkName();
|
||||
setText(nameDialog->GetValue());
|
||||
delete nameDialog;
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_NOTNULL:
|
||||
if(getOwnerColumn()->isNotNull())
|
||||
getOwnerColumn()->setColumnOption(null);
|
||||
else
|
||||
getOwnerColumn()->setColumnOption(notnull);
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_PKEY:
|
||||
if(getOwnerColumn()->isPrimaryKey())
|
||||
{
|
||||
getOwnerColumn()->disablePrimaryKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
getOwnerColumn()->enablePrimaryKey();
|
||||
getOwnerColumn()->setColumnOption(notnull);
|
||||
}
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_UKEY:
|
||||
getOwnerColumn()->toggleColumnKind(uk, view);
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPESERIAL:
|
||||
setDataType(dt_serial); //Should use setDataType always to set this value to allow fk to work flawlessly
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPEBOOLEAN:
|
||||
setDataType(dt_boolean);
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPEINTEGER:
|
||||
setDataType(dt_integer);
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPEMONEY:
|
||||
setDataType(dt_money);
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPEVARCHAR:
|
||||
setDataType(dt_varchar);
|
||||
tmpprecision = wxGetNumberFromUser(_("Varchar size"),
|
||||
_("Size for varchar datatype"),
|
||||
_("Varchar size"),
|
||||
getPrecision(), 0, 255, view);
|
||||
if (tmpprecision >= 0)
|
||||
{
|
||||
setPrecision(tmpprecision);
|
||||
setScale(-1);
|
||||
}
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
view->notifyChanged();
|
||||
break;
|
||||
case MNU_TYPEOTHER:
|
||||
answer = wxGetSingleChoiceIndex(wxT("New column datatype"), wxT("Column Datatypes"), dataTypes(), view);
|
||||
if(answer >= 0)
|
||||
{
|
||||
view->notifyChanged();
|
||||
if(answer == dt_varchar || answer == dt_bit || answer == dt_char || answer == dt_interval || answer == dt_varbit)
|
||||
{
|
||||
tmpprecision = wxGetNumberFromUser(_("datatype size"),
|
||||
_("Size for datatype"),
|
||||
_("size"),
|
||||
getPrecision(), 0, 255, view);
|
||||
if (tmpprecision >= 0)
|
||||
{
|
||||
setPrecision(tmpprecision);
|
||||
setScale(-1);
|
||||
}
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
}
|
||||
if(answer == dt_numeric)
|
||||
{
|
||||
numericDialog = new ddPrecisionScaleDialog( view,
|
||||
NumToStr((long)getPrecision()),
|
||||
NumToStr((long)getScale()));
|
||||
numericDialog->ShowModal();
|
||||
numericDialog->GetValue1().ToLong(&tmpvalue);
|
||||
setPrecision(tmpvalue);
|
||||
numericDialog->GetValue2().ToLong(&tmpvalue);
|
||||
setScale(tmpvalue);
|
||||
delete numericDialog;
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
}
|
||||
|
||||
|
||||
setDataType( (ddDataType) answer );
|
||||
recalculateDisplayBox();
|
||||
getOwnerColumn()->displayBoxUpdate();
|
||||
getOwnerColumn()->getOwnerTable()->updateTableSize();
|
||||
}
|
||||
break;
|
||||
case MNU_TYPEPKEY_CONSTRAINTNAME:
|
||||
tmpString = wxGetTextFromUser(wxT("New name of primary key:"), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), getOwnerColumn()->getOwnerTable()->getPkConstraintName(), view);
|
||||
if(tmpString.length() > 0)
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->setPkConstraintName(tmpString);
|
||||
view->notifyChanged();
|
||||
}
|
||||
break;
|
||||
case MNU_TYPEUKEY_CONSTRAINTNAME:
|
||||
answer = wxGetSingleChoiceIndex(wxT("Select Unique Key constraint to edit name"), wxT("Select Unique Constraint to edit name:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames(), view);
|
||||
if(answer >= 0)
|
||||
{
|
||||
tmpString = wxGetTextFromUser(wxT("Change name of Unique Key constraint:"), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer), view);
|
||||
if(tmpString.length() > 0)
|
||||
{
|
||||
getOwnerColumn()->getOwnerTable()->getUkConstraintsNames().Item(answer) = tmpString;
|
||||
view->notifyChanged();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MNU_DELTABLE:
|
||||
|
||||
delremDialog = new hdRemoveDeleteDialog(wxT("Are you sure you wish to delete table ") + getOwnerColumn()->getOwnerTable()->getTableName() + wxT("?"), wxT("Delete table?"), view);
|
||||
answer = delremDialog->ShowModal();
|
||||
ddTableFigure *table = getOwnerColumn()->getOwnerTable();
|
||||
if (answer == DD_DELETE)
|
||||
{
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
//Unselect table at all diagrams
|
||||
editor->removeFromAllSelections(table);
|
||||
//Drop foreign keys with this table as origin or destination
|
||||
table->processDeleteAlert(view->getDrawing());
|
||||
//Drop table
|
||||
editor->deleteModelFigure(table);
|
||||
editor->getDesign()->refreshBrowser();
|
||||
view->notifyChanged();
|
||||
}
|
||||
else if(answer == DD_REMOVE)
|
||||
{
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
editor->getExistingDiagram(view->getIdx())->removeFromSelection(table);
|
||||
editor->getExistingDiagram(view->getIdx())->remove(table);
|
||||
view->notifyChanged();
|
||||
}
|
||||
delete delremDialog;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::createMenu(wxMenu &mnu)
|
||||
{
|
||||
wxMenu *submenu;
|
||||
wxMenuItem *item;
|
||||
|
||||
mnu.Append(MNU_DDADDCOLUMN, _("Add a column..."));
|
||||
item = mnu.Append(MNU_DELCOLUMN, _("Delete the selected column..."));
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
item->Enable(false);
|
||||
mnu.Append(MNU_RENAMECOLUMN, _("Rename the selected column..."));
|
||||
if(getOwnerColumn()->isGeneratedForeignKey() && !getOwnerColumn()->isFkNameGenerated())
|
||||
mnu.Append(MNU_AUTONAMCOLUMN, _("Activate fk auto-naming..."));
|
||||
mnu.AppendSeparator();
|
||||
item = mnu.AppendCheckItem(MNU_NOTNULL, _("Not NULL constraint"));
|
||||
if(getOwnerColumn()->isNotNull())
|
||||
item->Check(true);
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
item->Enable(false);
|
||||
mnu.AppendSeparator();
|
||||
item = mnu.AppendCheckItem(MNU_PKEY, _("Primary Key"));
|
||||
if(getOwnerColumn()->isPrimaryKey())
|
||||
item->Check(true);
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
item->Enable(false);
|
||||
item = mnu.AppendCheckItem(MNU_UKEY, _("Unique Key"));
|
||||
if(getOwnerColumn()->isUniqueKey())
|
||||
item->Check(true);
|
||||
mnu.AppendSeparator();
|
||||
submenu = new wxMenu();
|
||||
item = mnu.AppendSubMenu(submenu, _("Column datatype"));
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
item->Enable(false);
|
||||
item = submenu->AppendCheckItem(MNU_TYPESERIAL, _("serial"));
|
||||
item->Check(columnType == dt_bigint);
|
||||
item = submenu->AppendCheckItem(MNU_TYPEBOOLEAN, _("boolean"));
|
||||
item->Check(columnType == dt_boolean);
|
||||
item = submenu->AppendCheckItem(MNU_TYPEINTEGER, _("integer"));
|
||||
item->Check(columnType == dt_integer);
|
||||
item = submenu->AppendCheckItem(MNU_TYPEMONEY, _("money"));
|
||||
item->Check(columnType == dt_money);
|
||||
item = submenu->AppendCheckItem(MNU_TYPEVARCHAR, _("varchar(n)"));
|
||||
item->Check(columnType == dt_varchar);
|
||||
item = submenu->Append(MNU_TYPEOTHER, _("Choose another datatype..."));
|
||||
mnu.AppendSeparator();
|
||||
mnu.Append(MNU_TYPEPKEY_CONSTRAINTNAME, _("Primary Key Constraint name..."));
|
||||
mnu.Append(MNU_TYPEUKEY_CONSTRAINTNAME, _("Unique Key Constraint name..."));
|
||||
mnu.AppendSeparator();
|
||||
mnu.Append(MNU_DELTABLE, _("Delete table..."));
|
||||
};
|
||||
|
||||
|
||||
const wxArrayString ddTextTableItemFigure::dataTypes()
|
||||
{
|
||||
if(ddDatatypes.IsEmpty())
|
||||
{
|
||||
//Fast access ddDatatypes
|
||||
ddDatatypes.Add(wxT("ANY"));
|
||||
ddDatatypes.Add(wxT("serial"));
|
||||
ddDatatypes.Add(wxT("boolean"));
|
||||
ddDatatypes.Add(wxT("integer"));
|
||||
ddDatatypes.Add(wxT("money"));
|
||||
ddDatatypes.Add(wxT("varchar(n)"));
|
||||
//Normal access ddDatatypes
|
||||
ddDatatypes.Add(wxT("bigint"));
|
||||
ddDatatypes.Add(wxT("bit(n)"));
|
||||
ddDatatypes.Add(wxT("bytea"));
|
||||
ddDatatypes.Add(wxT("char(n)"));
|
||||
ddDatatypes.Add(wxT("cidr"));
|
||||
ddDatatypes.Add(wxT("circle"));
|
||||
ddDatatypes.Add(wxT("date"));
|
||||
ddDatatypes.Add(wxT("double precision"));
|
||||
ddDatatypes.Add(wxT("inet"));
|
||||
ddDatatypes.Add(wxT("interval(n)"));
|
||||
ddDatatypes.Add(wxT("line"));
|
||||
ddDatatypes.Add(wxT("lseg"));
|
||||
ddDatatypes.Add(wxT("macaddr"));
|
||||
ddDatatypes.Add(wxT("numeric(p,s)"));
|
||||
ddDatatypes.Add(wxT("path"));
|
||||
ddDatatypes.Add(wxT("point"));
|
||||
ddDatatypes.Add(wxT("polygon"));
|
||||
ddDatatypes.Add(wxT("real"));
|
||||
ddDatatypes.Add(wxT("smallint"));
|
||||
ddDatatypes.Add(wxT("text"));
|
||||
ddDatatypes.Add(wxT("time"));
|
||||
ddDatatypes.Add(wxT("timestamp"));
|
||||
ddDatatypes.Add(wxT("varbit(n)"));
|
||||
}
|
||||
return ddDatatypes;
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setText(wxString textString)
|
||||
{
|
||||
hdSimpleTextFigure::setText(textString);
|
||||
|
||||
//Hack to allow column text to submit new size of text signal to tablefigure
|
||||
//and then recalculate displaybox. Helps with fk autorenaming too.
|
||||
if(ownerColumn)
|
||||
{
|
||||
ownerColumn->displayBoxUpdate();
|
||||
ownerColumn->getOwnerTable()->updateTableSize();
|
||||
ownerColumn->getOwnerTable()->updateFkObservers();
|
||||
}
|
||||
}
|
||||
|
||||
wxString ddTextTableItemFigure::getAlias()
|
||||
{
|
||||
return colAlias;
|
||||
}
|
||||
|
||||
//Activate use of alias or short names at ddtextTableItems like TableNames [Columns don' use it]
|
||||
void ddTextTableItemFigure::setAlias(wxString alias)
|
||||
{
|
||||
if(alias.length() <= 0 || alias.length() > 3 )
|
||||
{
|
||||
showAlias = false;
|
||||
colAlias = wxEmptyString;
|
||||
}
|
||||
else
|
||||
{
|
||||
showAlias = true;
|
||||
colAlias = alias;
|
||||
}
|
||||
recalculateDisplayBox();
|
||||
ownerTable->updateFkObservers(); //Only triggered by a tableName item [Not a column]
|
||||
ownerTable->updateTableSize();
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setOneTimeNoAlias()
|
||||
{
|
||||
oneTimeNoAlias = true;
|
||||
}
|
||||
|
||||
ddColumnFigure *ddTextTableItemFigure::getOwnerColumn()
|
||||
{
|
||||
return ownerColumn;
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setOwnerColumn(ddColumnFigure *column)
|
||||
{
|
||||
ownerColumn = column;
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setShowDataType(bool value)
|
||||
{
|
||||
showDataType = value;
|
||||
}
|
||||
|
||||
hdITool *ddTextTableItemFigure::CreateFigureTool(hdDrawingView *view, hdITool *defaultTool)
|
||||
{
|
||||
if(getOwnerColumn())
|
||||
{
|
||||
return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Column Name"), wxT("Rename Column")) : defaultTool;
|
||||
}
|
||||
else
|
||||
{
|
||||
setOneTimeNoAlias();
|
||||
return textEditable ? new ddColumnTextTool(view, this, defaultTool, false, wxT("New Table Name"), wxT("Rename Table")) : defaultTool;
|
||||
}
|
||||
}
|
||||
|
||||
int ddTextTableItemFigure::getTextWidth()
|
||||
{
|
||||
int w, h;
|
||||
getFontMetrics(w, h);
|
||||
return w;
|
||||
}
|
||||
|
||||
int ddTextTableItemFigure::getTextHeight()
|
||||
{
|
||||
int w, h;
|
||||
getFontMetrics(w, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
ddDataType ddTextTableItemFigure::getDataType()
|
||||
{
|
||||
if(!getOwnerColumn()->isGeneratedForeignKey())
|
||||
return columnType;
|
||||
else
|
||||
{
|
||||
columnType = getOwnerColumn()->getFkSource()->original->getDataType();
|
||||
return columnType;
|
||||
}
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setDataType(ddDataType type)
|
||||
{
|
||||
columnType = type;
|
||||
ownerColumn->getOwnerTable()->updateSizeOfObservers();
|
||||
}
|
||||
|
||||
int ddTextTableItemFigure::getPrecision()
|
||||
{
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
{
|
||||
precision = getOwnerColumn()->getFkSource()->original->getPrecision();
|
||||
return precision;
|
||||
}
|
||||
else
|
||||
{
|
||||
return precision;
|
||||
}
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setPrecision(int value)
|
||||
{
|
||||
if(!getOwnerColumn()->isGeneratedForeignKey())
|
||||
{
|
||||
precision = value;
|
||||
ownerColumn->getOwnerTable()->updateSizeOfObservers();
|
||||
}
|
||||
}
|
||||
|
||||
void ddTextTableItemFigure::setScale(int value)
|
||||
{
|
||||
if(!getOwnerColumn()->isGeneratedForeignKey())
|
||||
{
|
||||
scale = value;
|
||||
ownerColumn->getOwnerTable()->updateSizeOfObservers();
|
||||
}
|
||||
}
|
||||
|
||||
int ddTextTableItemFigure::getScale()
|
||||
{
|
||||
if(getOwnerColumn()->isGeneratedForeignKey())
|
||||
{
|
||||
scale = getOwnerColumn()->getFkSource()->original->getScale();
|
||||
return scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
25
dd/dditems/figures/module.mk
Normal file
25
dd/dditems/figures/module.mk
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/figures/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/figures/ddColumnFigure.cpp \
|
||||
dd/dditems/figures/ddColumnKindIcon.cpp \
|
||||
dd/dditems/figures/ddColumnOptionIcon.cpp \
|
||||
dd/dditems/figures/ddRelationshipFigure.cpp \
|
||||
dd/dditems/figures/ddRelationshipItem.cpp \
|
||||
dd/dditems/figures/ddRelationshipTerminal.cpp \
|
||||
dd/dditems/figures/ddTableFigure.cpp \
|
||||
dd/dditems/figures/ddTextTableItemFigure.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/figures/module.mk
|
||||
|
||||
include dd/dditems/figures/xml/module.mk
|
||||
1828
dd/dditems/figures/xml/ddXmlStorage.cpp
Normal file
1828
dd/dditems/figures/xml/ddXmlStorage.cpp
Normal file
File diff suppressed because it is too large
Load diff
16
dd/dditems/figures/xml/module.mk
Normal file
16
dd/dditems/figures/xml/module.mk
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/figures/xml/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/figures/xml/ddXmlStorage.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/figures/xml/module.mk
|
||||
76
dd/dditems/handles/ddAddColButtonHandle.cpp
Normal file
76
dd/dditems/handles/ddAddColButtonHandle.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddAddColButtonHandle.cpp - A handle for a table figure that allow to graphically add columns
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddAddColButtonHandle.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddAddColumnCursor.pngc"
|
||||
|
||||
ddAddColButtonHandle::ddAddColButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
|
||||
hdButtonHandle(owner, buttonLocator, buttonImage, size)
|
||||
{
|
||||
}
|
||||
|
||||
ddAddColButtonHandle::~ddAddColButtonHandle()
|
||||
{
|
||||
}
|
||||
|
||||
void ddAddColButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
wxTextEntryDialog nameDialog(view, wxT("New column name"), wxT("Add a column"));
|
||||
bool again;
|
||||
do
|
||||
{
|
||||
again = false;
|
||||
int answer = nameDialog.ShowModal();
|
||||
if (answer == wxID_OK)
|
||||
{
|
||||
wxString name = nameDialog.GetValue();
|
||||
if(table->getColByName(name) == NULL)
|
||||
{
|
||||
table->addColumn(view->getIdx(), new ddColumnFigure(name, table));
|
||||
view->notifyChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString msg(wxT("Error trying to add new column '"));
|
||||
msg.Append(name);
|
||||
msg.Append(wxT("' column name already in use"));
|
||||
wxMessageDialog info( view, msg ,
|
||||
wxT("Column name already in use"),
|
||||
wxNO_DEFAULT | wxOK | wxICON_EXCLAMATION);
|
||||
again = true;
|
||||
info.ShowModal();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
while(again);
|
||||
view->Refresh();
|
||||
}
|
||||
|
||||
void ddAddColButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddAddColButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
73
dd/dditems/handles/ddAddFkButtonHandle.cpp
Normal file
73
dd/dditems/handles/ddAddFkButtonHandle.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddAddFkButtonHandle.cpp - A handle for a table figure that allow to graphically add relationships (fk)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddAddFkButtonHandle.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "hotdraw/tools/hdConnectionCreationTool.h"
|
||||
#include "hotdraw/utilities/hdMouseEvent.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipTerminal.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddRelationshipCursor.pngc"
|
||||
|
||||
ddAddFkButtonHandle::ddAddFkButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
|
||||
hdButtonHandle(owner, buttonLocator, buttonImage, size)
|
||||
{
|
||||
}
|
||||
|
||||
ddAddFkButtonHandle::~ddAddFkButtonHandle()
|
||||
{
|
||||
}
|
||||
|
||||
void ddAddFkButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
if(getOwner()->ms_classInfo.IsKindOf(&ddTableFigure::ms_classInfo))
|
||||
{
|
||||
ddRelationshipFigure *fkConnection = new ddRelationshipFigure();
|
||||
//Check figure available positions for diagrams, add at least needed to allow initialization of the class
|
||||
int i, start;
|
||||
start = fkConnection->displayBox().CountPositions();
|
||||
for(i = start; i < (view->getIdx() + 1); i++)
|
||||
{
|
||||
fkConnection->AddPosForNewDiagram();
|
||||
}
|
||||
fkConnection->setStartTerminal(new ddRelationshipTerminal(fkConnection, false));
|
||||
fkConnection->setEndTerminal(new ddRelationshipTerminal(fkConnection, true));
|
||||
hdConnectionCreationTool *conn = new hdConnectionCreationTool(view, fkConnection);
|
||||
view->setTool(conn);
|
||||
// Simulate button down to start connection of foreign key
|
||||
wxMouseEvent e(wxEVT_LEFT_DOWN);
|
||||
e.m_x = event.GetPosition().x;
|
||||
e.m_y = event.GetPosition().y;
|
||||
e.SetEventObject(view);
|
||||
hdMouseEvent evento(e, view);
|
||||
conn->mouseDown(evento);
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
table->setSelectFkDestMode(true);
|
||||
view->notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ddAddFkButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddAddFkButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
62
dd/dditems/handles/ddMinMaxTableButtonHandle.cpp
Normal file
62
dd/dditems/handles/ddMinMaxTableButtonHandle.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddMinMaxTableButtonHandle.cpp - A handle for a table figure that allow to graphically minimize or maximize table window size
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddMinMaxTableButtonHandle.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddMinMaxCursor.pngc"
|
||||
|
||||
ddMinMaxTableButtonHandle::ddMinMaxTableButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxBitmap &buttonSecondImage, wxSize &size):
|
||||
hdButtonHandle(owner, buttonLocator, buttonImage, size)
|
||||
{
|
||||
buttonMaximizeImage = buttonSecondImage;
|
||||
tmpImage = buttonImage;
|
||||
showFirst = true;
|
||||
}
|
||||
|
||||
ddMinMaxTableButtonHandle::~ddMinMaxTableButtonHandle()
|
||||
{
|
||||
}
|
||||
|
||||
void ddMinMaxTableButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddMinMaxTableButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddMinMaxTableButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
|
||||
if(showFirst)
|
||||
{
|
||||
buttonIcon = buttonMaximizeImage;
|
||||
table->setColumnsWindow(view->getIdx(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonIcon = tmpImage;
|
||||
table->setColumnsWindow(table->getTotalColumns(), true);
|
||||
}
|
||||
showFirst = !showFirst;
|
||||
view->notifyChanged();
|
||||
}
|
||||
77
dd/dditems/handles/ddRemoveTableButtonHandle.cpp
Normal file
77
dd/dditems/handles/ddRemoveTableButtonHandle.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddRemoveTableButtonHandle.cpp - A handle for a table figure that allow to delete it
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddRemoveTableButtonHandle.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
|
||||
#include "dd/ddmodel/ddDrawingEditor.h"
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddDeleteTableCursor.pngc"
|
||||
|
||||
ddRemoveTableButtonHandle::ddRemoveTableButtonHandle(hdIFigure *owner, hdILocator *buttonLocator , wxBitmap &buttonImage, wxSize &size):
|
||||
hdButtonHandle(owner, buttonLocator, buttonImage, size)
|
||||
{
|
||||
}
|
||||
|
||||
ddRemoveTableButtonHandle::~ddRemoveTableButtonHandle()
|
||||
{
|
||||
}
|
||||
|
||||
void ddRemoveTableButtonHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddRemoveTableButtonHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
void ddRemoveTableButtonHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
if(view && getOwner())
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
hdRemoveDeleteDialog dialog(_("Are you sure you wish to delete table ") + table->getTableName() + wxT("?"), _("Delete table?"), view);
|
||||
int answer = dialog.ShowModal();
|
||||
if (answer == DD_DELETE)
|
||||
{
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
//Unselect table at all diagrams
|
||||
editor->removeFromAllSelections(table);
|
||||
//Drop foreign keys with this table as origin or destination
|
||||
table->processDeleteAlert(view->getDrawing());
|
||||
//Drop table
|
||||
editor->deleteModelFigure(table);
|
||||
editor->getDesign()->refreshBrowser();
|
||||
editor->checkRelationshipsConsistency(view->getIdx());
|
||||
view->notifyChanged();
|
||||
}
|
||||
else if(answer == DD_REMOVE)
|
||||
{
|
||||
ddDrawingEditor *editor = (ddDrawingEditor *) view->editor();
|
||||
editor->getExistingDiagram(view->getIdx())->removeFromSelection(table);
|
||||
editor->getExistingDiagram(view->getIdx())->remove(table);
|
||||
editor->checkRelationshipsConsistency(view->getIdx());
|
||||
view->notifyChanged();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
131
dd/dditems/handles/ddScrollBarHandle.cpp
Normal file
131
dd/dditems/handles/ddScrollBarHandle.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddScrollBarHandle.cpp - A handle for a table figure that allow to scroll it when table is not in full size
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddScrollBarHandle.h"
|
||||
#include "hotdraw/utilities/hdPoint.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "hotdraw/utilities/hdGeometry.h"
|
||||
|
||||
//Images
|
||||
#include "images/ddUp.pngc"
|
||||
#include "images/ddDown.pngc"
|
||||
|
||||
ddScrollBarHandle::ddScrollBarHandle(ddTableFigure *owner, hdILocator *scrollBarLocator , wxSize &size):
|
||||
hdLocatorHandle(owner, scrollBarLocator)
|
||||
{
|
||||
table = owner;
|
||||
scrollLocator = scrollBarLocator;
|
||||
displayBox.SetSize(size);
|
||||
upBitmap = wxBitmap(*ddUp_png_img);
|
||||
downBitmap = wxBitmap(*ddDown_png_img);
|
||||
}
|
||||
|
||||
ddScrollBarHandle::~ddScrollBarHandle()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
wxCursor ddScrollBarHandle::createCursor()
|
||||
{
|
||||
return wxCursor(wxCURSOR_HAND);
|
||||
}
|
||||
|
||||
//avoid to use inflate on this handle
|
||||
hdRect &ddScrollBarHandle::getDisplayBox(int posIdx)
|
||||
{
|
||||
hdPoint p = locate(posIdx);
|
||||
displayBox.width = 11; //as defined at locator
|
||||
displayBox.height = table->getColsSpace().height;
|
||||
displayBox.SetPosition(p);
|
||||
return displayBox;
|
||||
}
|
||||
|
||||
void ddScrollBarHandle::draw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
int idx = view->getIdx();
|
||||
context.SetBrush(*wxWHITE_BRUSH);
|
||||
wxPoint copy = getDisplayBox(idx).GetPosition();
|
||||
view->CalcScrolledPosition(copy.x, copy.y, ©.x, ©.y);
|
||||
context.DrawRectangle(copy.x, copy.y, getDisplayBox(idx).width, getDisplayBox(idx).height);
|
||||
context.DrawBitmap(upBitmap, copy.x + 1, copy.y + 2, true);
|
||||
context.DrawBitmap(downBitmap, copy.x + 1, copy.y + getDisplayBox(idx).height - 2 - downBitmap.GetHeight(), true);
|
||||
|
||||
barSize.SetHeight((getDisplayBox(idx).height - 12) * 0.45);
|
||||
barSize.SetWidth(getDisplayBox(idx).width - 4);
|
||||
|
||||
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
|
||||
if(divBy <= 0)
|
||||
divBy = table->getColumnsWindow();
|
||||
int colOffset = barSize.GetHeight() / divBy;
|
||||
int verticalPosBar = 3 + copy.y + downBitmap.GetHeight() + colOffset * table->getTopColWindowIndex();
|
||||
if(table->getColumnsWindow() > 1)
|
||||
context.DrawRectangle(wxPoint(copy.x + 2, verticalPosBar), barSize);
|
||||
|
||||
}
|
||||
|
||||
void ddScrollBarHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
int idx = view->getIdx();
|
||||
int y = event.GetPosition().y;
|
||||
anchorY = y;
|
||||
if( (y > (getDisplayBox(idx).GetPosition().y + 2)) && (y < (getDisplayBox(idx).GetPosition().y + 2 + 6)) ) //6 image height
|
||||
table->columnsWindowUp(idx);
|
||||
|
||||
if( (y > (getDisplayBox(idx).GetPosition().y + getDisplayBox(idx).height - 2 - downBitmap.GetHeight()) ) && (y < (getDisplayBox(idx).GetPosition().y + getDisplayBox(idx).height - 2) ) )
|
||||
table->columnsWindowDown(idx);
|
||||
view->notifyChanged();
|
||||
}
|
||||
|
||||
void ddScrollBarHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
int y = event.GetPosition().y;
|
||||
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
|
||||
if(divBy <= 0)
|
||||
divBy = table->getColumnsWindow();
|
||||
int colOffset = barSize.GetHeight() / divBy;
|
||||
|
||||
hdGeometry g;
|
||||
if ( g.ddabs(anchorY - y) > colOffset)
|
||||
{
|
||||
if((anchorY - y) > 0)
|
||||
{
|
||||
table->columnsWindowUp(view->getIdx());
|
||||
}
|
||||
else
|
||||
{
|
||||
table->columnsWindowDown(view->getIdx());
|
||||
}
|
||||
anchorY = y;
|
||||
}
|
||||
view->notifyChanged();
|
||||
}
|
||||
|
||||
void ddScrollBarHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddScrollBarHandle::locate(int posIdx)
|
||||
{
|
||||
if(scrollLocator)
|
||||
{
|
||||
pointLocate = scrollLocator->locate(posIdx, getOwner());
|
||||
return pointLocate;
|
||||
}
|
||||
else
|
||||
pointLocate = hdPoint(0, 0);
|
||||
return pointLocate;
|
||||
}
|
||||
94
dd/dditems/handles/ddSouthTableSizeHandle.cpp
Normal file
94
dd/dditems/handles/ddSouthTableSizeHandle.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddSouthTableSizeHandle.cpp - Allow to change table size by using drag and drop from south side of table rectangle
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/handles/ddSouthTableSizeHandle.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "hotdraw/utilities/hdGeometry.h"
|
||||
#include "hotdraw/figures/defaultAttributes/hdFontAttribute.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
|
||||
ddSouthTableSizeHandle::ddSouthTableSizeHandle(ddTableFigure *owner, hdILocator *locator):
|
||||
hdLocatorHandle(owner, locator)
|
||||
{
|
||||
}
|
||||
|
||||
hdRect &ddSouthTableSizeHandle::getDisplayBox(int posIdx)
|
||||
{
|
||||
hdPoint p = locate(posIdx);
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
displayBox.width = table->getFullSpace().width * 0.5; //as defined at locator
|
||||
displayBox.height = 3;
|
||||
displayBox.SetPosition(p);
|
||||
return displayBox;
|
||||
}
|
||||
|
||||
|
||||
wxCursor ddSouthTableSizeHandle::createCursor()
|
||||
{
|
||||
return wxCursor(wxCURSOR_SIZENS);
|
||||
}
|
||||
|
||||
void ddSouthTableSizeHandle::draw(wxBufferedDC &context, hdDrawingView *view)
|
||||
{
|
||||
}
|
||||
|
||||
ddSouthTableSizeHandle::~ddSouthTableSizeHandle()
|
||||
{
|
||||
}
|
||||
|
||||
void ddSouthTableSizeHandle::invokeStart(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
anchorY = event.GetPosition().y;
|
||||
}
|
||||
|
||||
void ddSouthTableSizeHandle::invokeStep(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
int y = event.GetPosition().y;
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
wxFont font = *hdFontAttribute::defaultFont;
|
||||
int colOffset = table->getColDefaultHeight(font);
|
||||
|
||||
int divBy = (table->getTotalColumns() - table->getColumnsWindow());
|
||||
if(divBy <= 0)
|
||||
divBy = table->getColumnsWindow();
|
||||
|
||||
hdGeometry g;
|
||||
if ( g.ddabs(anchorY - y) > colOffset)
|
||||
{
|
||||
if((anchorY - y) > 0)
|
||||
{
|
||||
table->setColumnsWindow(view->getIdx(), table->getColumnsWindow() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
table->setColumnsWindow(view->getIdx(), table->getColumnsWindow() + 1);
|
||||
}
|
||||
anchorY = y;
|
||||
}
|
||||
|
||||
//hack to update relationship position when table size change
|
||||
table->manuallyNotifyChange(view->getIdx());
|
||||
view->notifyChanged();
|
||||
}
|
||||
|
||||
void ddSouthTableSizeHandle::invokeEnd(hdMouseEvent &event, hdDrawingView *view)
|
||||
{
|
||||
//hack to update relationship position when table size change
|
||||
ddTableFigure *table = (ddTableFigure *) getOwner();
|
||||
table->manuallyNotifyChange(view->getIdx());
|
||||
view->notifyChanged();
|
||||
}
|
||||
21
dd/dditems/handles/module.mk
Normal file
21
dd/dditems/handles/module.mk
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/handles/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/handles/ddAddColButtonHandle.cpp \
|
||||
dd/dditems/handles/ddAddFkButtonHandle.cpp \
|
||||
dd/dditems/handles/ddMinMaxTableButtonHandle.cpp \
|
||||
dd/dditems/handles/ddRemoveTableButtonHandle.cpp \
|
||||
dd/dditems/handles/ddScrollBarHandle.cpp \
|
||||
dd/dditems/handles/ddSouthTableSizeHandle.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/handles/module.mk
|
||||
41
dd/dditems/locators/ddAddColLocator.cpp
Normal file
41
dd/dditems/locators/ddAddColLocator.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddAddColLocator.cpp - Locate table add column button inside a table.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddAddColLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddAddColLocator::ddAddColLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddAddColLocator::~ddAddColLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddAddColLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
locatePoint.x = table->getTitleRect().GetBottomRight(posIdx).x - 20;
|
||||
locatePoint.y = table->getTitleRect().GetBottomRight(posIdx).y - 9;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
41
dd/dditems/locators/ddAddFkLocator.cpp
Normal file
41
dd/dditems/locators/ddAddFkLocator.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddAddFkLocator.cpp - Locate table add fk relationship button inside a table.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddAddFkLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddAddFkLocator::ddAddFkLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddAddFkLocator::~ddAddFkLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddAddFkLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
locatePoint.x = table->getTitleRect().GetBottomRight(posIdx).x - 10;
|
||||
locatePoint.y = table->getTitleRect().GetBottomRight(posIdx).y - 9;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
44
dd/dditems/locators/ddMinMaxTableLocator.cpp
Normal file
44
dd/dditems/locators/ddMinMaxTableLocator.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddMinMaxTableLocator.cpp - Locate table minimize/maximize button inside a table.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddMinMaxTableLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddMinMaxTableLocator::ddMinMaxTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddMinMaxTableLocator::~ddMinMaxTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddMinMaxTableLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
int x = table->displayBox().x[posIdx] + table->displayBox().width - 20; //(8+2)
|
||||
int y = table->displayBox().y[posIdx] + 6;
|
||||
|
||||
locatePoint.x = x;
|
||||
locatePoint.y = y;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
44
dd/dditems/locators/ddRemoveTableLocator.cpp
Normal file
44
dd/dditems/locators/ddRemoveTableLocator.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddRemoveColLocator.cpp - Locate table delete button inside a table.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddRemoveTableLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddRemoveTableLocator::ddRemoveTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddRemoveTableLocator::~ddRemoveTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddRemoveTableLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
int x = table->displayBox().x[posIdx] + table->displayBox().width - 10; //(8+2)
|
||||
int y = table->displayBox().y[posIdx] + 6;
|
||||
|
||||
locatePoint.x = x;
|
||||
locatePoint.y = y;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
45
dd/dditems/locators/ddScrollBarTableLocator.cpp
Normal file
45
dd/dditems/locators/ddScrollBarTableLocator.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddScrollBarTableLocator.cpp - Locate table scrollbar inside a table.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddScrollBarTableLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddScrollBarTableLocator::ddScrollBarTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddScrollBarTableLocator::~ddScrollBarTableLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddScrollBarTableLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
;
|
||||
int x = table->getColsSpace().GetTopRight(posIdx).x - 11; //scrollwidth
|
||||
int y = table->getColsSpace().y[posIdx];
|
||||
|
||||
locatePoint.x = x;
|
||||
locatePoint.y = y;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
45
dd/dditems/locators/ddTableBottomLocator.cpp
Normal file
45
dd/dditems/locators/ddTableBottomLocator.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddTableBottomLocator.cpp - Locate bottom (south) of table for use of south table size handle.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/locators/ddTableBottomLocator.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
ddTableBottomLocator::ddTableBottomLocator()
|
||||
{
|
||||
}
|
||||
|
||||
ddTableBottomLocator::~ddTableBottomLocator()
|
||||
{
|
||||
}
|
||||
|
||||
hdPoint &ddTableBottomLocator::locate(int posIdx, hdIFigure *owner)
|
||||
{
|
||||
if(owner)
|
||||
{
|
||||
ddTableFigure *table = (ddTableFigure *) owner;
|
||||
|
||||
int x = table->getFullSpace().GetLeftBottom(posIdx).x + table->getFullSpace().width * 0.25;
|
||||
int y = table->getFullSpace().GetLeftBottom(posIdx).y - 2;
|
||||
|
||||
locatePoint.x = x;
|
||||
locatePoint.y = y;
|
||||
return locatePoint;
|
||||
}
|
||||
locatePoint.x = 0;
|
||||
locatePoint.y = 0;
|
||||
return locatePoint;
|
||||
}
|
||||
21
dd/dditems/locators/module.mk
Normal file
21
dd/dditems/locators/module.mk
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/locators/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/locators/ddAddColLocator.cpp \
|
||||
dd/dditems/locators/ddAddFkLocator.cpp \
|
||||
dd/dditems/locators/ddMinMaxTableLocator.cpp \
|
||||
dd/dditems/locators/ddRemoveTableLocator.cpp \
|
||||
dd/dditems/locators/ddScrollBarTableLocator.cpp \
|
||||
dd/dditems/locators/ddTableBottomLocator.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/locators/module.mk
|
||||
19
dd/dditems/module.mk
Normal file
19
dd/dditems/module.mk
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
include dd/dditems/figures/module.mk
|
||||
include dd/dditems/handles/module.mk
|
||||
include dd/dditems/locators/module.mk
|
||||
include dd/dditems/tools/module.mk
|
||||
include dd/dditems/utilities/module.mk
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/module.mk
|
||||
130
dd/dditems/tools/ddColumnFigureTool.cpp
Normal file
130
dd/dditems/tools/ddColumnFigureTool.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddColumnFigureTool.cpp - Improvement to hdFigureTool to work with composite table figures
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/tools/ddColumnFigureTool.h"
|
||||
#include "dd/dditems/figures/ddColumnFigure.h"
|
||||
#include "hotdraw/tools/hdDragTrackerTool.h"
|
||||
|
||||
|
||||
ddColumnFigureTool::ddColumnFigureTool(hdDrawingView *view, hdIFigure *fig, hdITool *dt):
|
||||
hdFigureTool(view, fig, dt)
|
||||
{
|
||||
delegateTool = NULL;
|
||||
}
|
||||
|
||||
ddColumnFigureTool::~ddColumnFigureTool()
|
||||
{
|
||||
//This tool destructor is at compositeTool, because this is only a selection tool and neither tool belongs to it.
|
||||
hdITool *tmpDefault = hdFigureTool::getDefaultTool();
|
||||
hdFigureTool *tmpDelegateDefault;
|
||||
|
||||
if(delegateTool->ms_classInfo.IsKindOf(&hdFigureTool::ms_classInfo))
|
||||
tmpDelegateDefault = (hdFigureTool *)delegateTool;
|
||||
else
|
||||
tmpDelegateDefault = NULL;
|
||||
|
||||
if(delegateTool && delegateTool != tmpDefault)
|
||||
{
|
||||
//Hack to avoid delete defaultTool (Delegate->defaultTool) of delegate tool
|
||||
// if this is the same as defaultTool (this->defaultTool) of this Object.
|
||||
if(tmpDelegateDefault && tmpDelegateDefault->getDefaultTool() == tmpDefault)
|
||||
tmpDelegateDefault->setDefaultTool(NULL);
|
||||
|
||||
//Hack to avoid delete hdDragTrackerTool Default twice because this figure is only used inside
|
||||
//a table, and then create a compositeTool and default in both tools is hdDragTrackerTool
|
||||
//but I can't hard code this is Composite because that class should remain generic
|
||||
if(tmpDelegateDefault->getDefaultTool()->ms_classInfo.IsKindOf(&hdDragTrackerTool::ms_classInfo))
|
||||
tmpDelegateDefault->setDefaultTool(NULL);
|
||||
delete delegateTool;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ddColumnFigureTool::setDefaultTool(hdITool *dt)
|
||||
{
|
||||
hdFigureTool::setDefaultTool(dt);
|
||||
}
|
||||
|
||||
hdITool *ddColumnFigureTool::getDefaultTool()
|
||||
{
|
||||
if(delegateTool)
|
||||
{
|
||||
return delegateTool;
|
||||
}
|
||||
else
|
||||
{
|
||||
return hdFigureTool::getDefaultTool();
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnFigureTool::mouseDown(hdMouseEvent &event)
|
||||
{
|
||||
int x = event.GetPosition().x, y = event.GetPosition().y;
|
||||
ddColumnFigure *cfigure = (ddColumnFigure *) getFigure();
|
||||
hdIFigure *figure = cfigure->findFigure(event.getView()->getIdx(), x, y);
|
||||
|
||||
if(figure)
|
||||
{
|
||||
setDelegateTool(event.getView(), figure->CreateFigureTool(event.getView(), getDefaultTool()));
|
||||
}
|
||||
else
|
||||
{
|
||||
setDelegateTool(event.getView(), getDefaultTool());
|
||||
}
|
||||
|
||||
if(delegateTool)
|
||||
{
|
||||
delegateTool->mouseDown(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnFigureTool::activate(hdDrawingView *view)
|
||||
{
|
||||
if(delegateTool)
|
||||
{
|
||||
delegateTool->activate(view);
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnFigureTool::deactivate(hdDrawingView *view)
|
||||
{
|
||||
if(delegateTool)
|
||||
{
|
||||
delegateTool->deactivate(view);
|
||||
}
|
||||
}
|
||||
|
||||
void ddColumnFigureTool::setDelegateTool(hdDrawingView *view, hdITool *tool)
|
||||
{
|
||||
if(delegateTool)
|
||||
{
|
||||
delegateTool->deactivate(view);
|
||||
delete delegateTool;
|
||||
delegateTool = NULL;
|
||||
}
|
||||
|
||||
delegateTool = tool;
|
||||
if(delegateTool)
|
||||
{
|
||||
delegateTool->activate(view);
|
||||
}
|
||||
}
|
||||
|
||||
hdITool *ddColumnFigureTool::getDelegateTool()
|
||||
{
|
||||
return delegateTool;
|
||||
}
|
||||
83
dd/dditems/tools/ddColumnTextTool.cpp
Normal file
83
dd/dditems/tools/ddColumnTextTool.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddColumnTextTool.cpp - Modification of simple text tool for editing composite figure columns
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/choicdlg.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/tools/ddColumnTextTool.h"
|
||||
#include "dd/dditems/figures/ddTextTableItemFigure.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/utilities/ddTableNameDialog.h"
|
||||
|
||||
class hdDrawingEditor;
|
||||
|
||||
|
||||
ddColumnTextTool::ddColumnTextTool(hdDrawingView *view, hdIFigure *fig, hdITool *dt, bool fastEdit , wxString dialogCaption, wxString dialogMessage):
|
||||
hdSimpleTextTool(view, fig, dt, fastEdit, dialogCaption, dialogMessage)
|
||||
{
|
||||
if(colTextFigure->ms_classInfo.IsKindOf(&ddTextTableItemFigure::ms_classInfo))
|
||||
colTextFigure = (ddTextTableItemFigure *) fig;
|
||||
else
|
||||
colTextFigure = NULL;
|
||||
}
|
||||
|
||||
ddColumnTextTool::~ddColumnTextTool()
|
||||
{
|
||||
}
|
||||
|
||||
void ddColumnTextTool::mouseDown(hdMouseEvent &event)
|
||||
{
|
||||
hdSimpleTextTool::mouseDown(event);
|
||||
}
|
||||
|
||||
bool ddColumnTextTool::callDialog(hdDrawingView *view)
|
||||
{
|
||||
if(colTextFigure->getOwnerColumn() == NULL)
|
||||
{
|
||||
wxString colName = colTextFigure->getText();
|
||||
wxString colShortName = colTextFigure->getAlias();
|
||||
ddTableNameDialog *nameAliasDialog = new ddTableNameDialog(
|
||||
view,
|
||||
colName,
|
||||
colShortName,
|
||||
colTextFigure
|
||||
);
|
||||
|
||||
int answer = nameAliasDialog->ShowModal();
|
||||
bool change = false;
|
||||
|
||||
if(answer == wxOK)
|
||||
{
|
||||
//check if names changed
|
||||
change = ! (colShortName.IsSameAs(nameAliasDialog->GetValue1()) && colShortName.IsSameAs(nameAliasDialog->GetValue2()));
|
||||
if(change)
|
||||
{
|
||||
colTextFigure->setText(nameAliasDialog->GetValue1());
|
||||
colTextFigure->setAlias(nameAliasDialog->GetValue2());
|
||||
view->notifyChanged();
|
||||
}
|
||||
}
|
||||
delete nameAliasDialog;
|
||||
return change;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool change = hdSimpleTextTool::callDialog(view);
|
||||
if( change && colTextFigure->getOwnerColumn()->isGeneratedForeignKey()) //after a manual user column rename, deactivated automatic generation of fk name.
|
||||
colTextFigure->getOwnerColumn()->deactivateGenFkName();
|
||||
return change;
|
||||
}
|
||||
}
|
||||
17
dd/dditems/tools/module.mk
Normal file
17
dd/dditems/tools/module.mk
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/tools/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/tools/ddColumnFigureTool.cpp \
|
||||
dd/dditems/tools/ddColumnTextTool.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/tools/module.mk
|
||||
76
dd/dditems/utilities/ddPrecisionScaleDialog.cpp
Normal file
76
dd/dditems/utilities/ddPrecisionScaleDialog.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddPrecisionScaleDialog.cpp - Utility dialog class to allow user input of table name and short name
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/statline.h>
|
||||
#include <wx/spinctrl.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/utilities/ddPrecisionScaleDialog.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
#define txtPrecision CTRL_TEXT("txtPrecision")
|
||||
#define txtScale CTRL_TEXT("txtScale")
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ddPrecisionScaleDialog, pgDialog)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ddPrecisionScaleDialog::ddPrecisionScaleDialog( wxWindow *parent,
|
||||
const wxString &defaultValue1,
|
||||
const wxString &defaultValue2
|
||||
)
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
LoadResource(parent, wxT("ddPrecisionScaleDialog"));
|
||||
RestorePosition();
|
||||
Init();
|
||||
SetValue1(defaultValue1);
|
||||
SetValue2(defaultValue2);
|
||||
|
||||
txtPrecision->SetFocus();
|
||||
}
|
||||
|
||||
ddPrecisionScaleDialog::~ddPrecisionScaleDialog()
|
||||
{
|
||||
}
|
||||
|
||||
ddPrecisionScaleDialog::ddPrecisionScaleDialog()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void ddPrecisionScaleDialog::Init( )
|
||||
{
|
||||
m_value1 = wxT("0");
|
||||
m_value2 = wxT("0");
|
||||
}
|
||||
|
||||
//Transfer data to the window
|
||||
bool ddPrecisionScaleDialog::TransferDataToWindow()
|
||||
{
|
||||
txtPrecision->SetValue(m_value1);
|
||||
txtScale->SetValue(m_value2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Transfer data from the window
|
||||
bool ddPrecisionScaleDialog::TransferDataFromWindow()
|
||||
{
|
||||
m_value1 = txtPrecision->GetValue();
|
||||
m_value2 = txtScale->GetValue();
|
||||
|
||||
return true;
|
||||
}
|
||||
316
dd/dditems/utilities/ddSelectKindFksDialog.cpp
Normal file
316
dd/dditems/utilities/ddSelectKindFksDialog.cpp
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddSelectKindFksDialog.cpp - Utility dialog class to allow user select destination of fk: automatically generated or existing column
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/statline.h>
|
||||
#include "wx/stattext.h"
|
||||
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/utilities/ddSelectKindFksDialog.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "hotdraw/main/hdDrawingView.h"
|
||||
#include "hotdraw/main/hdDrawingEditor.h"
|
||||
|
||||
|
||||
IMPLEMENT_CLASS( ddSelectKindFksDialog, wxDialog )
|
||||
|
||||
BEGIN_EVENT_TABLE( ddSelectKindFksDialog, wxDialog )
|
||||
EVT_BUTTON(wxID_OK, ddSelectKindFksDialog::OnOkButtonClicked)
|
||||
EVT_BUTTON(wxID_CANCEL, ddSelectKindFksDialog::OnCancelButtonClicked)
|
||||
EVT_CHOICE(DDSELECTKINDFK, ddSelectKindFksDialog::OnChoiceFkKind)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ddSelectKindFksDialog::ddSelectKindFksDialog( wxWindow *parent,
|
||||
ddRelationshipFigure *relation,
|
||||
wxWindowID id,
|
||||
const wxPoint &pos,
|
||||
const wxSize &size,
|
||||
long style
|
||||
)
|
||||
{
|
||||
tablesRelation = relation;
|
||||
Init();
|
||||
Create(parent, id, pos, size, style);
|
||||
}
|
||||
|
||||
ddSelectKindFksDialog::~ddSelectKindFksDialog()
|
||||
{
|
||||
//before delete all items inside to free memory
|
||||
deleteColsControls();
|
||||
delete ok;
|
||||
delete line;
|
||||
}
|
||||
|
||||
ddSelectKindFksDialog::ddSelectKindFksDialog()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::Init( )
|
||||
{
|
||||
// choices.clear(); and delete all items inside hash map
|
||||
|
||||
}
|
||||
|
||||
bool ddSelectKindFksDialog::Create( wxWindow *parent,
|
||||
wxWindowID id,
|
||||
const wxPoint &pos = wxDefaultPosition,
|
||||
const wxSize &size = wxDefaultSize,
|
||||
long style = wxCAPTION )
|
||||
{
|
||||
if (!wxDialog::Create( parent, id, wxT("Select Foreign Key Mapping for each Column"), pos, size, style ))
|
||||
return false;
|
||||
|
||||
CreateControls();
|
||||
|
||||
// This fits the dialog to the minimum size dictated by
|
||||
// the sizers
|
||||
GetSizer()->Fit(this);
|
||||
|
||||
// This ensures that the dialog cannot be sized smaller
|
||||
// than the minimum size
|
||||
|
||||
GetSizer()->SetSizeHints(this);
|
||||
|
||||
// Centre the dialog on the parent or (if none) screen
|
||||
|
||||
Centre();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::CreateControls()
|
||||
{
|
||||
// A top-level sizer
|
||||
|
||||
topSizer = new wxBoxSizer(wxVERTICAL );
|
||||
this->SetSizer(topSizer);
|
||||
|
||||
// A wxChoice to choice kind of foreign key: from Uk or from pk
|
||||
wxArrayString kindFks;
|
||||
kindFks.Add(_("From Primary"));
|
||||
|
||||
int i, last = tablesRelation->getStartTable()->getUkConstraintsNames().Count();
|
||||
wxString tmp;
|
||||
|
||||
for(i = 0; i < last; i++)
|
||||
{
|
||||
tmp = _("Unique Key: ");
|
||||
tmp += tablesRelation->getStartTable()->getUkConstraintsNames()[i];
|
||||
kindFks.Add(tmp);
|
||||
}
|
||||
|
||||
kindFkCtrl = new wxChoice(this, DDSELECTKINDFK, wxDefaultPosition, wxDefaultSize, kindFks);
|
||||
kindFkCtrl->SetStringSelection(_("From Primary"));
|
||||
topSizer->Add(kindFkCtrl, 0, wxALIGN_CENTER, 5);
|
||||
|
||||
// A dividing line before the mapping items and kind of mapping
|
||||
line = new wxStaticLine ( this, wxID_STATIC,
|
||||
wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
topSizer->Add(line, 0, wxGROW | wxALL, 5);
|
||||
|
||||
|
||||
//Hack to allow multiple pairs of wxStaticText wxchoice at dialog
|
||||
colsTopSizer = new wxBoxSizer(wxVERTICAL );
|
||||
topSizer->Add(colsTopSizer);
|
||||
populateColumnsControls(true, -1);
|
||||
|
||||
// A dividing line before the OK and Cancel buttons
|
||||
|
||||
line = new wxStaticLine ( this, wxID_STATIC,
|
||||
wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
topSizer->Add(line, 0, wxGROW | wxALL, 5);
|
||||
|
||||
// A horizontal box sizer to contain Reset, OK, Cancel and Help
|
||||
|
||||
okCancelBox = new wxBoxSizer(wxHORIZONTAL);
|
||||
topSizer->Add(okCancelBox, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
|
||||
|
||||
// The OK button
|
||||
|
||||
ok = new wxButton ( this, wxID_OK, wxT("&OK"),
|
||||
wxDefaultPosition, wxDefaultSize, 0 );
|
||||
okCancelBox->Add(ok, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
// The Cancel button
|
||||
|
||||
cancel = new wxButton ( this, wxID_CANCEL, wxT("&Cancel"),
|
||||
wxDefaultPosition, wxDefaultSize, 0 );
|
||||
okCancelBox->Add(cancel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::OnChoiceFkKind(wxCommandEvent &event)
|
||||
{
|
||||
int selectedUk = kindFkCtrl->GetSelection() == 0 ? -1 : kindFkCtrl->GetSelection() - 1;
|
||||
bool frompk = kindFkCtrl->GetSelection() == 0;
|
||||
|
||||
populateColumnsControls(frompk, selectedUk);
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::deleteColsControls()
|
||||
{
|
||||
choicesControlsHashMap::iterator it;
|
||||
bool repeat;
|
||||
ddSelectFkKindLine *columnLine;
|
||||
do
|
||||
{
|
||||
repeat = false;
|
||||
for (it = choices.begin(); it != choices.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
columnLine = it->second;
|
||||
choices.erase(it);
|
||||
delete columnLine;
|
||||
repeat = true;
|
||||
if (repeat)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(repeat);
|
||||
colsTopSizer->Clear();
|
||||
choices.clear();
|
||||
}
|
||||
void ddSelectKindFksDialog::populateColumnsControls(bool primaryKey, int useUkIndex)
|
||||
{
|
||||
// Adding all columns mapping items
|
||||
ddTableFigure *source = (ddTableFigure *) tablesRelation->getStartFigure();
|
||||
ddTableFigure *destination = (ddTableFigure *) tablesRelation->getEndFigure();
|
||||
|
||||
//Delete existing controllers
|
||||
deleteColsControls();
|
||||
|
||||
//Populate controllers
|
||||
if(primaryKey)
|
||||
{
|
||||
wxArrayString sourceCols = source->getAllFkSourceColsNames(true);
|
||||
wxArrayString destCols = destination->getAllColumnsNames();
|
||||
destCols.Insert(_("Automatically Generated"), 0);
|
||||
int i, last = sourceCols.Count();
|
||||
int eventID;
|
||||
for(i = 0; i < last; i++)
|
||||
{
|
||||
// A sizer to allow lines of controls
|
||||
linesSizer = new wxBoxSizer(wxHORIZONTAL );
|
||||
colsTopSizer->Add(linesSizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
|
||||
//Create controls and put inside linesizer
|
||||
eventID = DDCHOICESELECTBASE + i;
|
||||
ddSelectFkKindLine *newLineControls = new ddSelectFkKindLine(this, sourceCols[i], destCols, eventID);
|
||||
choices[ sourceCols[i] ] = newLineControls;
|
||||
linesSizer->Add(newLineControls->sourceCtrl, 0, wxALIGN_LEFT | wxALL, 5);
|
||||
linesSizer->Add(newLineControls->destinationCtrl, 0, wxGROW | wxALL, 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxArrayString sourceCols = source->getAllFkSourceColsNames(false, useUkIndex);
|
||||
wxArrayString destCols = destination->getAllColumnsNames();
|
||||
destCols.Insert(_("Automatically Generated"), 0);
|
||||
int i, last = sourceCols.Count();
|
||||
int eventID;
|
||||
for(i = 0; i < last; i++)
|
||||
{
|
||||
// A sizer to allow lines of controls
|
||||
linesSizer = new wxBoxSizer(wxHORIZONTAL );
|
||||
colsTopSizer->Add(linesSizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
|
||||
//Create controls and put inside linesizer
|
||||
eventID = DDCHOICESELECTBASE + i;
|
||||
ddSelectFkKindLine *newLineControls = new ddSelectFkKindLine(this, sourceCols[i], destCols, eventID);
|
||||
choices[ sourceCols[i] ] = newLineControls;
|
||||
linesSizer->Add(newLineControls->sourceCtrl, 0, wxALIGN_LEFT | wxALL, 5);
|
||||
linesSizer->Add(newLineControls->destinationCtrl, 0, wxGROW | wxALL, 5);
|
||||
}
|
||||
}
|
||||
this->Layout();
|
||||
this->InvalidateBestSize();
|
||||
this->Fit();
|
||||
topSizer->RecalcSizes();
|
||||
}
|
||||
|
||||
//Transfer data to the window
|
||||
bool ddSelectKindFksDialog::TransferDataToWindow()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//Transfer data from the window
|
||||
bool ddSelectKindFksDialog::TransferDataFromWindow()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::OnEnterPressed( wxCommandEvent &event )
|
||||
{
|
||||
if (event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER)
|
||||
{
|
||||
if ( Validate() && TransferDataFromWindow() )
|
||||
{
|
||||
if ( IsModal() )
|
||||
EndModal(wxID_OK); // If modal
|
||||
else
|
||||
{
|
||||
SetReturnCode(wxID_OK);
|
||||
this->Show(false); // If modeless
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::OnCancelButtonClicked( wxCommandEvent &event )
|
||||
{
|
||||
//Do nothing, just return wxID_CANCEL to don't allow connection
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void ddSelectKindFksDialog::OnOkButtonClicked( wxCommandEvent &event )
|
||||
{
|
||||
choicesControlsHashMap::iterator it;
|
||||
ddSelectFkKindLine *lineOfCtrls;
|
||||
|
||||
int selectedUk = kindFkCtrl->GetSelection() == 0 ? -1 : kindFkCtrl->GetSelection() - 1;
|
||||
bool fromPk = kindFkCtrl->GetSelection() == 0;
|
||||
tablesRelation->setFkFrom(fromPk, selectedUk); //true or bigger from zero both are mutually exclusive
|
||||
|
||||
for( it = choices.begin(); it != choices.end(); ++it )
|
||||
{
|
||||
wxString key = it->first;
|
||||
lineOfCtrls = it->second;
|
||||
if(lineOfCtrls->destinationCtrl->GetSelection() != 0) //No automatic Generated
|
||||
{
|
||||
ddColumnFigure *col = tablesRelation->getStartTable()->getColumnByName(lineOfCtrls->sourceCtrl->GetLabel());
|
||||
tablesRelation->addExistingColumnFk(col, lineOfCtrls->destinationCtrl->GetString(lineOfCtrls->destinationCtrl->GetSelection()));
|
||||
}
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
ddSelectFkKindLine::ddSelectFkKindLine(wxWindow *parent, wxString sourceColumn, wxArrayString possibleTargets, wxWindowID eventId)
|
||||
{
|
||||
sourceCtrl = new wxStaticText(parent, wxID_STATIC, sourceColumn, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
destinationCtrl = new wxChoice(parent, eventId, wxDefaultPosition, wxDefaultSize, possibleTargets);
|
||||
destinationCtrl->SetStringSelection(_("Automatically Generated"));
|
||||
}
|
||||
|
||||
ddSelectFkKindLine::ddSelectFkKindLine()
|
||||
{
|
||||
sourceCtrl = NULL;
|
||||
destinationCtrl = NULL;
|
||||
}
|
||||
|
||||
ddSelectFkKindLine::~ddSelectFkKindLine()
|
||||
{
|
||||
delete sourceCtrl;
|
||||
delete destinationCtrl;
|
||||
}
|
||||
78
dd/dditems/utilities/ddTableNameDialog.cpp
Normal file
78
dd/dditems/utilities/ddTableNameDialog.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddTableNameDialog.cpp - Utility dialog class to allow user input of table name and short name
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/statline.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/dditems/utilities/ddTableNameDialog.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
|
||||
#define txtUsualTableName CTRL_TEXT("txtUsualTableName")
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ddTableNameDialog, pgDialog)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
ddTableNameDialog::ddTableNameDialog( wxWindow *parent,
|
||||
const wxString &defaultValue1,
|
||||
const wxString &defaultValue2,
|
||||
ddTextTableItemFigure *tableItem
|
||||
) :
|
||||
pgDialog()
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
LoadResource(parent, wxT("ddTableNameDialog"));
|
||||
RestorePosition();
|
||||
Init();
|
||||
tabItem = tableItem;
|
||||
checkGenerate = false;
|
||||
|
||||
SetValue1(defaultValue1);
|
||||
SetValue2(defaultValue2);
|
||||
|
||||
txtUsualTableName->SetFocus();
|
||||
}
|
||||
|
||||
ddTableNameDialog::~ddTableNameDialog()
|
||||
{
|
||||
}
|
||||
|
||||
ddTableNameDialog::ddTableNameDialog() :
|
||||
pgDialog()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void ddTableNameDialog::Init( )
|
||||
{
|
||||
m_value1 = wxEmptyString;
|
||||
m_value2 = wxEmptyString;
|
||||
}
|
||||
|
||||
//Transfer data to the window
|
||||
bool ddTableNameDialog::TransferDataToWindow()
|
||||
{
|
||||
txtUsualTableName->SetValue(m_value1);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Transfer data from the window
|
||||
bool ddTableNameDialog::TransferDataFromWindow()
|
||||
{
|
||||
m_value1 = txtUsualTableName->GetValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
17
dd/dditems/utilities/module.mk
Normal file
17
dd/dditems/utilities/module.mk
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/dditems/utilities/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/dditems/utilities/ddPrecisionScaleDialog.cpp \
|
||||
dd/dditems/utilities/ddSelectKindFksDialog.cpp \
|
||||
dd/dditems/utilities/ddTableNameDialog.cpp
|
||||
EXTRA_DIST += \
|
||||
dd/dditems/utilities/module.mk
|
||||
42
dd/ddmodel/ddBrowserDataContainer.cpp
Normal file
42
dd/ddmodel/ddBrowserDataContainer.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddBrowserDataContainer.cpp - Item to contain data for each treview child.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/ddmodel/ddBrowserDataContainer.h"
|
||||
#include "hotdraw/figures/hdIFigure.h"
|
||||
|
||||
|
||||
ddBrowserDataContainer::ddBrowserDataContainer(hdIFigure *data)
|
||||
{
|
||||
figure = data;
|
||||
}
|
||||
|
||||
|
||||
// Destructor
|
||||
ddBrowserDataContainer::~ddBrowserDataContainer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int ddBrowserDataContainer::getFigureKindId()
|
||||
{
|
||||
return figure->getKindId();
|
||||
}
|
||||
|
||||
hdIFigure *ddBrowserDataContainer::getFigure()
|
||||
{
|
||||
return figure;
|
||||
}
|
||||
2087
dd/ddmodel/ddDBReverseEnginering.cpp
Normal file
2087
dd/ddmodel/ddDBReverseEnginering.cpp
Normal file
File diff suppressed because it is too large
Load diff
710
dd/ddmodel/ddDatabaseDesign.cpp
Normal file
710
dd/ddmodel/ddDatabaseDesign.cpp
Normal file
|
|
@ -0,0 +1,710 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddDatabaseDesign.cpp - Manages all design related info and contains all model(s) and tables.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// libxml2 headers
|
||||
#include <libxml/xmlwriter.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
#include "hotdraw/tools/hdSelectionTool.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "dd/ddmodel/ddDrawingEditor.h"
|
||||
#include "dd/dditems/figures/xml/ddXmlStorage.h"
|
||||
#include "dd/ddmodel/ddModelBrowser.h"
|
||||
|
||||
|
||||
ddDatabaseDesign::ddDatabaseDesign(wxWindow *parent, wxWindow *frmOwner)
|
||||
{
|
||||
editor = new ddDrawingEditor(parent, frmOwner, this);
|
||||
attachedBrowser = NULL;
|
||||
}
|
||||
|
||||
ddDatabaseDesign::~ddDatabaseDesign()
|
||||
{
|
||||
if(editor)
|
||||
delete editor;
|
||||
}
|
||||
|
||||
ddDrawingEditor *ddDatabaseDesign::getEditor()
|
||||
{
|
||||
return editor;
|
||||
}
|
||||
|
||||
hdDrawingView *ddDatabaseDesign::getView(int diagramIndex)
|
||||
{
|
||||
return editor->getExistingView(diagramIndex);
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::registerBrowser(ddModelBrowser *browser)
|
||||
{
|
||||
attachedBrowser = browser;
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::addTableToModel(hdIFigure *figure)
|
||||
{
|
||||
editor->addModelFigure(figure);
|
||||
if(attachedBrowser)
|
||||
{
|
||||
attachedBrowser->refreshFromModel();
|
||||
}
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::addTableToView(int diagramIndex, hdIFigure *figure)
|
||||
{
|
||||
editor->addDiagramFigure(diagramIndex, figure);
|
||||
if(attachedBrowser)
|
||||
{
|
||||
attachedBrowser->refreshFromModel();
|
||||
}
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::refreshBrowser()
|
||||
{
|
||||
if(attachedBrowser)
|
||||
{
|
||||
attachedBrowser->refreshFromModel();
|
||||
}
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::removeTable(int diagramIndex, hdIFigure *figure)
|
||||
{
|
||||
editor->removeDiagramFigure(diagramIndex, figure);
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::refreshDraw(int diagramIndex)
|
||||
{
|
||||
editor->getExistingView(diagramIndex)->Refresh();
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::eraseDiagram(int diagramIndex)
|
||||
{
|
||||
editor->getExistingDiagram(diagramIndex)->removeAllFigures();
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::emptyModel()
|
||||
{
|
||||
editor->deleteAllModelFigures();
|
||||
if(attachedBrowser)
|
||||
{
|
||||
attachedBrowser->refreshFromModel();
|
||||
}
|
||||
}
|
||||
|
||||
bool ddDatabaseDesign::validateModel(wxString &errors)
|
||||
{
|
||||
bool out = true;
|
||||
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmpFigure;
|
||||
ddTableFigure *table;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmpFigure = (hdIFigure *)iterator->Next();
|
||||
if(tmpFigure->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmpFigure;
|
||||
if(!table->validateTable(errors))
|
||||
{
|
||||
out = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::generateList(wxArrayString tables, wxArrayInt options, pgConn *connection, wxString schemaName)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Validate
|
||||
if(tables.Count() != options.Count())
|
||||
{
|
||||
// shouldn't it be a WXASSERT?
|
||||
wxMessageBox(_("Invalid number of arguments in call of function generate tables of list"), _("Error at generation process"), wxICON_ERROR | wxOK);
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
int tablesCount = tables.Count();
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
if(table == NULL)
|
||||
{
|
||||
// shouldn't it be a WXASSERT?
|
||||
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error at generation process"), wxICON_ERROR | wxOK);
|
||||
return wxEmptyString;
|
||||
}
|
||||
}
|
||||
|
||||
// Start building of CREATE + ALTER PK(s) + ALTER UK(s) + ALTER FK(s)
|
||||
wxString out;
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Create sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
if(options[i] == DDGENDROPCRE)
|
||||
{
|
||||
out += wxT(" \n");
|
||||
out += wxT("DROP TABLE \"") + table->getTableName() + wxT("\";");
|
||||
out += wxT(" \n");
|
||||
}
|
||||
out += wxT(" \n");
|
||||
out += table->generateSQLCreate(schemaName);
|
||||
out += wxT(" \n");
|
||||
}
|
||||
}
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Pk sentence for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
out += table->generateSQLAlterPks(schemaName);
|
||||
}
|
||||
}
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Uk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
out += table->generateSQLAlterUks(schemaName);
|
||||
}
|
||||
}
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Fk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENCREATE || options[i] == DDGENDROPCRE)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
out += table->generateSQLAlterFks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
//Start generation of alter table instead of create
|
||||
//Check there is some
|
||||
int countAlter = 0;
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENALTER)
|
||||
{
|
||||
countAlter++;
|
||||
}
|
||||
}
|
||||
|
||||
if(countAlter > 0 && connection == NULL)
|
||||
{
|
||||
wxMessageBox(_("No connection found when building ALTER objects DDL."), _("Error at generation process"), wxICON_ERROR | wxOK);
|
||||
return out;
|
||||
}
|
||||
else if(countAlter > 0 && connection != NULL)
|
||||
{
|
||||
if(schemaName.IsEmpty())
|
||||
{
|
||||
wxMessageBox(_("Schema defined when building ALTER TABLE DDL"), _("Error at generation process"), wxICON_ERROR | wxOK);
|
||||
return out;
|
||||
}
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Alter table sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
if(options[i] == DDGENALTER)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
out += table->generateAltersTable(connection, schemaName, this);
|
||||
out += wxT(" \n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
wxArrayString ddDatabaseDesign::getModelTables()
|
||||
{
|
||||
wxArrayString out;
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out.Add(table->getTableName());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::generateModel(wxString schemaName)
|
||||
{
|
||||
wxString out;
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Create sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
out += wxT(" \n");
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLCreate(schemaName);
|
||||
out += wxT(" \n");
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Pk sentence for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterPks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Uk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterUks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Fk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterFks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
delete iterator;
|
||||
return out;
|
||||
}
|
||||
|
||||
wxArrayString ddDatabaseDesign::getDiagramTables(int diagramIndex)
|
||||
{
|
||||
wxArrayString out;
|
||||
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out.Add(table->getTableName());
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::generateDiagram(int diagramIndex, wxString schemaName)
|
||||
{
|
||||
wxString out;
|
||||
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Create sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
out += wxT(" \n");
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLCreate(schemaName);
|
||||
out += wxT(" \n");
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Pk sentence for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterPks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Uk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterUks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
out += wxT("--\n-- ");
|
||||
out += _("Generating Fk sentence(s) for table(s) ");
|
||||
out += wxT(" \n--\n");
|
||||
out += wxT(" \n");
|
||||
out += wxT(" \n");
|
||||
iterator->ResetIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
out += table->generateSQLAlterFks(schemaName);
|
||||
}
|
||||
}
|
||||
|
||||
delete iterator;
|
||||
return out;
|
||||
}
|
||||
|
||||
ddTableFigure *ddDatabaseDesign::getSelectedTable(int diagramIndex)
|
||||
{
|
||||
hdIteratorBase *iterator = editor->getExistingDiagram(diagramIndex)->figuresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table = NULL;
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if (tmp->isSelected(diagramIndex) && tmp->getKindId() == DDTABLEFIGURE)
|
||||
table = (ddTableFigure *)tmp;
|
||||
}
|
||||
delete iterator;
|
||||
return table;
|
||||
}
|
||||
|
||||
ddTableFigure *ddDatabaseDesign::getTable(wxString tableName)
|
||||
{
|
||||
ddTableFigure *out = NULL;
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
if(table->getTableName().IsSameAs(tableName, false))
|
||||
{
|
||||
out = table;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
return out;
|
||||
}
|
||||
|
||||
#define XML_FROM_WXSTRING(s) ((xmlChar *)(const char *)s.mb_str(wxConvUTF8))
|
||||
#define WXSTRING_FROM_XML(s) wxString((char *)s, wxConvUTF8)
|
||||
|
||||
bool ddDatabaseDesign::writeXmlModel(wxString file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
xmlWriter = xmlNewTextWriterFilename(file.mb_str(wxConvUTF8), 0);
|
||||
if (xmlWriter == NULL)
|
||||
{
|
||||
wxMessageBox(_("Failed to write the model file!"), _("Error"), wxICON_ERROR | wxOK);
|
||||
return false;
|
||||
}
|
||||
rc = xmlTextWriterStartDocument(xmlWriter, NULL, "UTF-8" , NULL);
|
||||
if(rc < 0)
|
||||
{
|
||||
wxMessageBox(_("Failed to write the model file!"), _("Error"), wxICON_ERROR | wxOK);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ddXmlStorage::StartModel(xmlWriter, this);
|
||||
//initialize IDs of tables
|
||||
mappingNameToId.clear();
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
int nextID = 10;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
mappingNameToId[table->getTableName()] = wxString::Format(wxT("TID%d"), nextID);
|
||||
nextID += 10;
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
|
||||
//Create table xml info
|
||||
iterator = editor->modelFiguresEnumerator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
ddXmlStorage::Write(xmlWriter, table);
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
|
||||
//Create relationships xml info
|
||||
ddRelationshipFigure *relationship;
|
||||
iterator = editor->modelFiguresEnumerator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
relationship = (ddRelationshipFigure *)tmp;
|
||||
ddXmlStorage::Write(xmlWriter, relationship);
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
//Create Diagrams xml info
|
||||
|
||||
ddXmlStorage::StarDiagrams(xmlWriter);
|
||||
|
||||
iterator = editor->diagramsEnumerator();
|
||||
hdDrawing *tmpDiagram;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmpDiagram = (hdDrawing *)iterator->Next();
|
||||
ddXmlStorage::WriteLocal(xmlWriter, tmpDiagram);
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
ddXmlStorage::EndDiagrams(xmlWriter);
|
||||
|
||||
//End model xml info
|
||||
ddXmlStorage::EndModel(xmlWriter);
|
||||
xmlTextWriterEndDocument(xmlWriter);
|
||||
xmlFreeTextWriter(xmlWriter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ddDatabaseDesign::readXmlModel(wxString file, ctlAuiNotebook *notebook)
|
||||
{
|
||||
emptyModel();
|
||||
|
||||
mappingIdToName.clear();
|
||||
//Initial Parse Model
|
||||
xmlTextReaderPtr reader = xmlReaderForFile(file.mb_str(wxConvUTF8), NULL, 0);
|
||||
ddXmlStorage::setModel(this);
|
||||
ddXmlStorage::setModel(this);
|
||||
ddXmlStorage::initialModelParse(reader);
|
||||
|
||||
//Parse Model
|
||||
xmlReaderNewFile(reader, file.mb_str(wxConvUTF8), NULL, 0);
|
||||
ddXmlStorage::setModel(this);
|
||||
ddXmlStorage::setNotebook(notebook);
|
||||
|
||||
if(!ddXmlStorage::Read(reader))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
xmlFreeTextReader(reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::getTableId(wxString tableName)
|
||||
{
|
||||
return mappingNameToId[tableName];
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::addTableToMapping(wxString IdKey, wxString tableName)
|
||||
{
|
||||
mappingIdToName[IdKey] = tableName;
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::getTableName(wxString Id)
|
||||
{
|
||||
wxString tableName = wxEmptyString;
|
||||
|
||||
tablesMappingHashMap::iterator it;
|
||||
for (it = mappingIdToName.begin(); it != mappingIdToName.end(); ++it)
|
||||
{
|
||||
wxString key = it->first;
|
||||
if(key.IsSameAs(Id, false))
|
||||
{
|
||||
tableName = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
hdDrawing *ddDatabaseDesign::createDiagram(wxWindow *owner, wxString name, bool fromXml)
|
||||
{
|
||||
hdDrawing *drawing = editor->createDiagram(owner, fromXml);
|
||||
drawing->setName(name);
|
||||
return drawing;
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::deleteDiagram(int diagramIndex, bool deleteView)
|
||||
{
|
||||
editor->deleteDiagram(diagramIndex, deleteView);
|
||||
}
|
||||
|
||||
wxString ddDatabaseDesign::getVersionXML()
|
||||
{
|
||||
return wxString(_("1.0"));
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::markSchemaOn(wxArrayString tables)
|
||||
{
|
||||
|
||||
int i, tablesCount = tables.Count();
|
||||
for(i = 0; i < tablesCount; i++)
|
||||
{
|
||||
ddTableFigure *table = getTable(tables[i]);
|
||||
if(table != NULL) //mark on if table exists in other case do nothing
|
||||
{
|
||||
table->setBelongsToSchema(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ddDatabaseDesign::unMarkSchemaOnAll()
|
||||
{
|
||||
hdIteratorBase *iterator = editor->modelFiguresEnumerator();
|
||||
hdIFigure *tmpFigure;
|
||||
ddTableFigure *table;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmpFigure = (hdIFigure *)iterator->Next();
|
||||
if(tmpFigure->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmpFigure;
|
||||
table->setBelongsToSchema(false);
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
}
|
||||
300
dd/ddmodel/ddDrawingEditor.cpp
Normal file
300
dd/ddmodel/ddDrawingEditor.cpp
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// hdDrawingEditor.cpp - Main class that manages all other classes
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/ddmodel/ddDrawingEditor.h"
|
||||
#include "dd/dditems/utilities/ddTableNameDialog.h"
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
#include "dd/ddmodel/ddDrawingView.h"
|
||||
#include "hotdraw/utilities/hdRemoveDeleteDialog.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "frm/frmDatabaseDesigner.h"
|
||||
|
||||
|
||||
ddDrawingEditor::ddDrawingEditor(wxWindow *owner, wxWindow *frmOwner, ddDatabaseDesign *design)
|
||||
: hdDrawingEditor(owner, true)
|
||||
{
|
||||
databaseDesign = design;
|
||||
frm = (frmDatabaseDesigner *) frmOwner;
|
||||
}
|
||||
|
||||
hdDrawing *ddDrawingEditor::createDiagram(wxWindow *owner, bool fromXml)
|
||||
{
|
||||
|
||||
hdDrawing *_tmpModel = new hdDrawing(this);
|
||||
|
||||
ddDrawingView *_viewTmp = new ddDrawingView(_diagrams->count(), owner, this, wxSize(1200, 1200), _tmpModel);
|
||||
|
||||
// Set Scroll Bar & split
|
||||
_viewTmp->SetScrollbars( 10, 10, 127, 80 );
|
||||
_viewTmp->EnableScrolling(true, true);
|
||||
_viewTmp->AdjustScrollbars();
|
||||
|
||||
_tmpModel->registerView(_viewTmp);
|
||||
|
||||
_viewTmp->SetDropTarget(new ddDropTarget(databaseDesign, _tmpModel));
|
||||
|
||||
if(!fromXml)
|
||||
{
|
||||
//Add a new position inside each figure to allow use of this new diagram existing figures.
|
||||
int i;
|
||||
hdIFigure *tmp;
|
||||
for(i = 0; i < _model->count(); i++)
|
||||
{
|
||||
tmp = (hdIFigure *) _model->getItemAt(i);
|
||||
tmp->AddPosForNewDiagram();
|
||||
}
|
||||
}
|
||||
//Add Diagram
|
||||
_diagrams->addItem((hdObject *) _tmpModel);
|
||||
modelChanged = true;
|
||||
return _tmpModel;
|
||||
}
|
||||
|
||||
|
||||
void ddDrawingEditor::remOrDelSelFigures(int diagramIndex)
|
||||
{
|
||||
hdIFigure *tmp;
|
||||
ddTableFigure *table;
|
||||
ddRelationshipFigure *relation;
|
||||
hdIteratorBase *iterator;
|
||||
hdCollection *tmpSelection;
|
||||
int answer;
|
||||
int numbTables = 0;
|
||||
int numbRelationships = 0;
|
||||
|
||||
if (getExistingDiagram(diagramIndex)->countSelectedFigures() == 1)
|
||||
{
|
||||
tmp = (hdIFigure *) getExistingDiagram(diagramIndex)->selectedFigures()->getItemAt(0);
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
numbTables = 1;
|
||||
table = (ddTableFigure *)tmp;
|
||||
hdRemoveDeleteDialog dialog(_("Are you sure you wish to delete table ") + table->getTableName() + wxT("?"), _("Delete table?"), getExistingView(diagramIndex));
|
||||
answer = dialog.ShowModal();
|
||||
}
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
numbRelationships = 1;
|
||||
relation = (ddRelationshipFigure *)tmp;
|
||||
//Relationship can be delete only NOT REMOVED
|
||||
hdRemoveDeleteDialog dialog2(_("Are you sure you wish to delete relationship ") + relation->getConstraintName() + wxT("?"), _("Delete relationship?"), getExistingView(diagramIndex), false);
|
||||
answer = dialog2.ShowModal();
|
||||
}
|
||||
}
|
||||
else if (getExistingDiagram(diagramIndex)->countSelectedFigures() > 1)
|
||||
{
|
||||
numbTables = 0;
|
||||
iterator = getExistingDiagram(diagramIndex)->selectionFigures();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
numbTables++;
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
|
||||
//Improve messages to display about relationships and tables and only relationship
|
||||
hdRemoveDeleteDialog dialog3(
|
||||
wxString::Format(_("Are you sure you wish to delete %d tables, removing from model related relationships?"), numbTables),
|
||||
_("Delete tables?"), getExistingView(diagramIndex));
|
||||
answer = dialog3.ShowModal();
|
||||
}
|
||||
|
||||
if (answer == DD_DELETE || answer == DD_REMOVE)
|
||||
{
|
||||
modelChanged = true;
|
||||
tmpSelection = new hdCollection(new hdArrayCollection());
|
||||
|
||||
//Preprocess relationships counting and storing at temporary collection
|
||||
numbRelationships = 0;
|
||||
iterator = getExistingDiagram(diagramIndex)->selectionFigures();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
numbRelationships++;
|
||||
tmpSelection->addItem(tmp);
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
while(numbTables > 0)
|
||||
{
|
||||
tmp = (hdIFigure *) getExistingDiagram(diagramIndex)->selectedFigures()->getItemAt(0);
|
||||
if(tmp->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmp;
|
||||
if(table && answer == DD_REMOVE)
|
||||
{
|
||||
getExistingDiagram(diagramIndex)->removeFromSelection(table);
|
||||
getExistingDiagram(diagramIndex)->remove(table);
|
||||
}
|
||||
//if table is going to be delete all others diagrams should be alerted about it
|
||||
if(table && answer == DD_DELETE)
|
||||
{
|
||||
removeFromAllSelections(table);
|
||||
table->processDeleteAlert(getExistingDiagram(diagramIndex));
|
||||
deleteModelFigure(table);
|
||||
databaseDesign->refreshBrowser();
|
||||
}
|
||||
numbTables--;
|
||||
}
|
||||
else if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
getExistingDiagram(diagramIndex)->removeFromSelection(tmp); //isn't a tables is probably a relationship
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting tables"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
|
||||
}
|
||||
}
|
||||
|
||||
if( numbRelationships > 0 )
|
||||
{
|
||||
|
||||
//check again: Are there relationships selected that wasn't delete (only selected relationship not source/destination table)
|
||||
numbRelationships = 0;
|
||||
iterator = tmpSelection->createIterator(); //getExistingDiagram(diagramIndex)->selectionFigures();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
//only way a relationship don't exists at diagram is
|
||||
//it had been deleted before by deleting source/destination table that owns it
|
||||
if(getExistingDiagram(diagramIndex)->includes(tmp))
|
||||
{
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
numbRelationships++;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting table"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpSelection->removeItem(tmp);
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
while(numbRelationships > 0 && tmpSelection->count() == numbRelationships)
|
||||
{
|
||||
tmp = (hdIFigure *) tmpSelection->getItemAt(0);
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
relation = (ddRelationshipFigure *)tmp;
|
||||
if(relation && answer == DD_REMOVE)
|
||||
{
|
||||
getExistingDiagram(diagramIndex)->removeFromSelection(relation);
|
||||
getExistingDiagram(diagramIndex)->remove(relation);
|
||||
}
|
||||
//if relation is going to be delete all others diagrams should be alerted about it
|
||||
if(relation && answer == DD_DELETE)
|
||||
{
|
||||
removeFromAllSelections(relation);
|
||||
relation->removeForeignKeys();
|
||||
relation->disconnectEnd();
|
||||
relation->disconnectStart();
|
||||
deleteModelFigure(relation);
|
||||
databaseDesign->refreshBrowser();
|
||||
}
|
||||
numbRelationships--;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(_("Invalid figure kind found"), _("Error while deleting realtionships"), wxCENTRE | wxICON_ERROR, getExistingView(diagramIndex));
|
||||
// getExistingDiagram(diagramIndex)->removeFromSelection(tmp); //isn't neither a table or relationship
|
||||
}
|
||||
}
|
||||
}
|
||||
getExistingDiagram(diagramIndex)->clearSelection(); //after delete all items all relationships remains at selection and should be removed
|
||||
tmpSelection->removeAll();
|
||||
delete tmpSelection;
|
||||
}
|
||||
}
|
||||
|
||||
void ddDrawingEditor::checkRelationshipsConsistency(int diagramIndex)
|
||||
{
|
||||
hdIFigure *tmp;
|
||||
ddRelationshipFigure *relation;
|
||||
hdDrawing *diagram = getExistingDiagram(diagramIndex);
|
||||
|
||||
// First Step Removel all orphan [relations without source or destination] relationships
|
||||
// from DIAGRAM but NOT from MODEL
|
||||
hdIteratorBase *iterator = diagram->figuresEnumerator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
relation = (ddRelationshipFigure *)tmp;
|
||||
//test if all tables of a relationship are included if this is not the case then remove relationship from this diagram
|
||||
bool sourceExists = diagram->getFiguresCollection()->existsObject(relation->getStartTable());
|
||||
bool destinationExists = diagram->getFiguresCollection()->existsObject(relation->getEndTable());
|
||||
if(!sourceExists || !destinationExists)
|
||||
{
|
||||
diagram->remove(relation);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
// Now add all existing relationships in MODEL to DIAGRAM if both source and destination
|
||||
// tables exists at DIAGRAM
|
||||
iterator = _model->createIterator();
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmp = (hdIFigure *)iterator->Next();
|
||||
if(tmp->getKindId() == DDRELATIONSHIPFIGURE)
|
||||
{
|
||||
relation = (ddRelationshipFigure *)tmp;
|
||||
|
||||
//test if all tables of a relationship are included if this is the case then include relationship at this diagram
|
||||
bool sourceExists = diagram->getFiguresCollection()->existsObject(relation->getStartTable());
|
||||
bool destinationExists = diagram->getFiguresCollection()->existsObject(relation->getEndTable());
|
||||
bool relationExists = diagram->getFiguresCollection()->existsObject(relation);
|
||||
if(sourceExists && destinationExists && !relationExists)
|
||||
{
|
||||
diagram->add(relation);
|
||||
relation->updateConnection(diagramIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
}
|
||||
|
||||
void ddDrawingEditor::checkAllDigramsRelConsistency()
|
||||
{
|
||||
int i, size = modelCount();
|
||||
|
||||
for(i = 0; i < size ; i++)
|
||||
{
|
||||
checkRelationshipsConsistency(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ddDrawingEditor::notifyChanged()
|
||||
{
|
||||
if(frm)
|
||||
frm->setModelChanged(true);
|
||||
modelChanged = true;
|
||||
}
|
||||
90
dd/ddmodel/ddDrawingView.cpp
Normal file
90
dd/ddmodel/ddDrawingView.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddDrawingView.cpp - Main canvas where all tables and relationships are drawn
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/ddmodel/ddDrawingView.h"
|
||||
#include "hotdraw/utilities/hdArrayCollection.h"
|
||||
#include "dd/dditems/figures/ddTableFigure.h"
|
||||
#include "dd/dditems/figures/ddRelationshipFigure.h"
|
||||
#include "dd/dditems/utilities/ddDataType.h"
|
||||
#include "dd/dditems/utilities/ddTableNameDialog.h"
|
||||
|
||||
ddDrawingView::ddDrawingView(int diagram, wxWindow *ddParent, ddDrawingEditor *editor , wxSize size, hdDrawing *drawing)
|
||||
: hdDrawingView(diagram, ddParent, editor, size, drawing)
|
||||
{
|
||||
}
|
||||
|
||||
void ddDrawingView::createViewMenu(wxMenu &mnu)
|
||||
{
|
||||
mnu.Append(MNU_NEWTABLE, _("Add new Table"));
|
||||
}
|
||||
|
||||
void ddDrawingView::OnGenericViewPopupClick(wxCommandEvent &event)
|
||||
{
|
||||
ddDrawingEditor *ed = (ddDrawingEditor *) editor();
|
||||
switch(event.GetId())
|
||||
{
|
||||
case MNU_NEWTABLE:
|
||||
ddTableNameDialog *newTableDialog = new ddTableNameDialog(
|
||||
this,
|
||||
wxEmptyString,
|
||||
wxEmptyString,
|
||||
NULL
|
||||
);
|
||||
int answer = newTableDialog->ShowModal();
|
||||
if (answer == wxID_OK && !newTableDialog->GetValue1().IsEmpty())
|
||||
{
|
||||
ddTableFigure *newTable = new ddTableFigure(newTableDialog->GetValue1(),
|
||||
rand() % 90 + 200,
|
||||
rand() % 90 + 140);
|
||||
ed->getDesign()->addTableToView(this->getIdx(), newTable);
|
||||
ed->getDesign()->refreshDraw(this->getIdx());
|
||||
}
|
||||
delete newTableDialog;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ddDropTarget::ddDropTarget(ddDatabaseDesign *sourceDesign, hdDrawing *targetDrawing)
|
||||
{
|
||||
target = targetDrawing;
|
||||
source = sourceDesign;
|
||||
|
||||
}
|
||||
|
||||
bool ddDropTarget::OnDropText(wxCoord x, wxCoord y, const wxString &text)
|
||||
{
|
||||
ddTableFigure *t = source->getTable(text);
|
||||
if(t != NULL && !target->includes(t))
|
||||
{
|
||||
target->add(t);
|
||||
t->syncInternalsPosAt(target->getView()->getIdx(), x, y);
|
||||
source->getEditor()->checkRelationshipsConsistency(target->getView()->getIdx());
|
||||
target->getView()->Refresh();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(target->includes(t))
|
||||
{
|
||||
wxMessageBox(_("Table exists already at this diagram"), _("Drag and drop warning"), wxICON_EXCLAMATION | wxOK);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
739
dd/ddmodel/ddGenerationWizard.cpp
Normal file
739
dd/ddmodel/ddGenerationWizard.cpp
Normal file
|
|
@ -0,0 +1,739 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddGenerationWizard.cpp - Wizard to allow generation of tables.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/regex.h>
|
||||
|
||||
// App headers
|
||||
#include "schema/pgSchema.h"
|
||||
#include "dd/ddmodel/ddGenerationWizard.h"
|
||||
#include "dd/ddmodel/ddDBReverseEngineering.h"
|
||||
#include "gqb/gqbGridRestTable.h"
|
||||
#include "images/gqbOrderAddAll.pngc"
|
||||
#include "images/gqbOrderRemoveAll.pngc"
|
||||
#include "images/gqbOrderRemove.pngc"
|
||||
#include "images/gqbOrderAdd.pngc"
|
||||
|
||||
|
||||
#include "images/continue.pngc"
|
||||
|
||||
const wxColour LIGHT_YELLOW(0xff, 0xff, 0x80);
|
||||
|
||||
BEGIN_EVENT_TABLE(ddGenerationWizard, wxWizard)
|
||||
EVT_WIZARD_FINISHED(wxID_ANY, ddGenerationWizard::OnFinishPressed)
|
||||
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, ddGenerationWizard::OnWizardPageChanging)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
BEGIN_EVENT_TABLE(SelGenTablesPage, wxWizardPage)
|
||||
EVT_BUTTON(DDREMOVE, SelGenTablesPage::OnButtonRemove)
|
||||
EVT_BUTTON(DDREMOVEALL, SelGenTablesPage::OnButtonRemoveAll)
|
||||
EVT_BUTTON(DDADD, SelGenTablesPage::OnButtonAdd)
|
||||
EVT_BUTTON(DDADDALL, SelGenTablesPage::OnButtonAddAll)
|
||||
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, SelGenTablesPage::OnWizardPageChanging)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
BEGIN_EVENT_TABLE(SelGenSchemaPage, wxWizardPage)
|
||||
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, SelGenSchemaPage::OnWizardPageChanging)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
BEGIN_EVENT_TABLE(ReportGridPage, wxWizardPage)
|
||||
EVT_WIZARD_PAGE_CHANGING(wxID_ANY, ReportGridPage::OnWizardPageChanging)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ddGenerationWizard::ddGenerationWizard(wxFrame *frame, ddDatabaseDesign *design, pgConn *connection, bool useSizer)
|
||||
: wxWizard(frame, wxID_ANY, wxT("Generate DDL from model"),
|
||||
wxBitmap(*continue_png_bmp), wxDefaultPosition,
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
conn = connection;
|
||||
figuresDesign = design;
|
||||
|
||||
// a wizard page may be either an object of predefined class
|
||||
initialPage = new wxWizardPageSimple(this);
|
||||
|
||||
frontText = new wxStaticText(initialPage, wxID_ANY,
|
||||
wxT("Build DDL from model tables.\n")
|
||||
wxT("\n")
|
||||
wxT("The next pages will allow the built of DDL of the current database designer model.")
|
||||
wxT("\n\n")
|
||||
wxT("Restrictions apply when using experimental function ALTER TABLE instead of DROP/CREATE:\n\n")
|
||||
wxT("1. Database connection is required to check differences with existing tables.\n\n")
|
||||
wxT("2. Only changes done in the design will be applied to the db, not otherwise.\n\n")
|
||||
wxT("3. Rename of columns or tables is not (yet) supported.\n\n")
|
||||
wxT("4. Rename of constraints generate a drop and create, except for primary key constraints.\n\n")
|
||||
wxT("5. All constraints should have a defined name.\n\n")
|
||||
, wxPoint(5, 5)
|
||||
);
|
||||
|
||||
page2 = new SelGenTablesPage(this, initialPage);
|
||||
initialPage->SetNext(page2);
|
||||
page3 = new SelGenSchemaPage(this, page2);
|
||||
page2->SetNext(page3);
|
||||
page4 = new ReportGridPage(this, page3);
|
||||
page3->SetNext(page4);
|
||||
|
||||
if (useSizer)
|
||||
{
|
||||
// allow the wizard to size itself around the pages
|
||||
GetPageAreaSizer()->Add(initialPage);
|
||||
}
|
||||
}
|
||||
|
||||
ddGenerationWizard::~ddGenerationWizard()
|
||||
{
|
||||
}
|
||||
|
||||
void ddGenerationWizard::OnFinishPressed(wxWizardEvent &event)
|
||||
{
|
||||
wxString strChoicesSimple[1] = {_("Create table")};
|
||||
wxString strChoicesAlter[4] = {_("Alter table"), _("Drop, then create"), _("Create table [conflict]"), wxT("No action")};
|
||||
wxArrayString tables;
|
||||
wxArrayInt options;
|
||||
if(page4->getGrid()->GetRows() > 0)
|
||||
{
|
||||
int i;
|
||||
for(i = page4->getGrid()->GetRows() - 1; i >= 0; i--)
|
||||
{
|
||||
tables.Add(page4->getGrid()->GetCellValue(i, 0));
|
||||
wxString value = page4->getGrid()->GetCellValue(i, 1);
|
||||
if(value.IsSameAs(strChoicesAlter[0]))
|
||||
{
|
||||
options.Add(DDGENALTER);
|
||||
}
|
||||
else if(value.IsSameAs(strChoicesAlter[1]))
|
||||
{
|
||||
options.Add(DDGENDROPCRE);
|
||||
}
|
||||
else if(value.IsSameAs(strChoicesAlter[2]) || value.IsSameAs(strChoicesSimple[0]))
|
||||
{
|
||||
options.Add(DDGENCREATE);
|
||||
}
|
||||
else if(value.IsSameAs(strChoicesAlter[3]))
|
||||
{
|
||||
options.Add(DDGENNOTHING);
|
||||
}
|
||||
}
|
||||
}
|
||||
DDL = getDesign()->generateList(tables, options, conn, schemaName);
|
||||
}
|
||||
|
||||
void ddGenerationWizard::OnWizardPageChanging(wxWizardEvent &event)
|
||||
{
|
||||
if(event.GetDirection())
|
||||
{
|
||||
page2->RefreshTablesList();
|
||||
}
|
||||
}
|
||||
|
||||
// ----- SelGenTablesPage Implementation
|
||||
|
||||
SelGenTablesPage::SelGenTablesPage(wxWizard *parent, wxWizardPage *prev)
|
||||
: wxWizardPage(parent)
|
||||
{
|
||||
wparent = (ddGenerationWizard *) parent;
|
||||
m_prev = prev;
|
||||
m_next = NULL;
|
||||
|
||||
wxFlexGridSizer *mainSizer = new wxFlexGridSizer(2, 3, 0, 0);
|
||||
this->SetSizer(mainSizer);
|
||||
|
||||
leftText = new wxStaticText(this, wxID_STATIC, _("Table(s) from selected schema"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
mainSizer->Add(leftText);
|
||||
centerText = new wxStaticText(this, wxID_STATIC, _(" "), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
mainSizer->Add(centerText);
|
||||
|
||||
rightText = new wxStaticText(this, wxID_STATIC, _("Tables(s) to be generated"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
mainSizer->Add(rightText, wxALIGN_LEFT);
|
||||
|
||||
// Left listbox with all tables
|
||||
m_allTables = new wxListBox( this, DDALLTABS, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED | wxLB_ALWAYS_SB | wxLB_SORT);
|
||||
mainSizer->AddGrowableRow(1);
|
||||
mainSizer->AddGrowableCol(0);
|
||||
mainSizer->Add(m_allTables , 1, wxEXPAND);
|
||||
|
||||
addBitmap = *gqbOrderAdd_png_bmp;
|
||||
addAllBitmap = *gqbOrderAddAll_png_bmp;
|
||||
removeBitmap = *gqbOrderRemove_png_bmp;
|
||||
removeAllBitmap = *gqbOrderRemoveAll_png_bmp;
|
||||
|
||||
buttonAdd = new wxBitmapButton( this, DDADD, addBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Add Column") );
|
||||
buttonAdd->SetToolTip(_("Add the selected table(s)"));
|
||||
buttonAddAll = new wxBitmapButton( this, DDADDALL, addAllBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Add All Columns") );
|
||||
buttonAddAll->SetToolTip(_("Add all tables"));
|
||||
buttonRemove = new wxBitmapButton( this, DDREMOVE, removeBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Remove Column") );
|
||||
buttonRemove->SetToolTip(_("Remove the selected table(s)"));
|
||||
buttonRemoveAll = new wxBitmapButton( this, DDREMOVEALL, removeAllBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, wxT("Remove All Columns") );
|
||||
buttonRemoveAll->SetToolTip(_("Remove all tables"));
|
||||
|
||||
wxBoxSizer *buttonsSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
buttonsSizer->Add(
|
||||
this->buttonAdd,
|
||||
0, // make horizontally unstretchable
|
||||
wxALL, // make border all around (implicit top alignment)
|
||||
3 ); // set border width to 3
|
||||
|
||||
buttonsSizer->Add(
|
||||
this->buttonAddAll,
|
||||
0, // make horizontally unstretchable
|
||||
wxALL, // make border all around (implicit top alignment)
|
||||
3 ); // set border width to 3
|
||||
|
||||
buttonsSizer->Add(
|
||||
this->buttonRemove,
|
||||
0, // make horizontally unstretchable
|
||||
wxALL, // make border all around (implicit top alignment)
|
||||
3 ); // set border width to 3
|
||||
|
||||
buttonsSizer->Add(
|
||||
this->buttonRemoveAll,
|
||||
0, // make horizontally unstretchable
|
||||
wxALL, // make border all around (implicit top alignment)
|
||||
3 ); // set border width to 3
|
||||
|
||||
mainSizer->Add(
|
||||
buttonsSizer,
|
||||
0, // make vertically unstretchable
|
||||
wxALIGN_CENTER ); // no border and centre horizontally
|
||||
|
||||
//right listbox with selected tables from schema to be imported.
|
||||
m_selTables = new wxListBox( this, DDSELTABS, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED | wxLB_ALWAYS_SB | wxLB_SORT);
|
||||
mainSizer->AddGrowableCol(2);
|
||||
mainSizer->Add(m_selTables , 1, wxEXPAND);
|
||||
mainSizer->Fit(this);
|
||||
}
|
||||
|
||||
SelGenTablesPage::~SelGenTablesPage()
|
||||
{
|
||||
if(rightText)
|
||||
delete rightText;
|
||||
if(centerText)
|
||||
delete centerText;
|
||||
if(leftText)
|
||||
delete leftText;
|
||||
if(m_allTables)
|
||||
delete m_allTables;
|
||||
if(m_selTables)
|
||||
delete m_selTables;
|
||||
if(buttonAdd)
|
||||
delete buttonAdd;
|
||||
if(buttonAddAll)
|
||||
delete buttonAddAll;
|
||||
if(buttonRemove)
|
||||
delete buttonRemove;
|
||||
if(buttonRemoveAll)
|
||||
delete buttonRemoveAll;
|
||||
}
|
||||
|
||||
void SelGenTablesPage::OnButtonAdd(wxCommandEvent &)
|
||||
{
|
||||
wxArrayInt positions;
|
||||
if(m_allTables->GetSelections(positions) > 0)
|
||||
{
|
||||
int i;
|
||||
int size = positions.Count();
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
m_selTables->Append(m_allTables->GetString(positions[i]));
|
||||
m_allTables->Deselect(positions[i]);
|
||||
}
|
||||
|
||||
for(i = (size - 1); i >= 0 ; i--)
|
||||
{
|
||||
m_allTables->Delete(positions[i]);
|
||||
}
|
||||
|
||||
if(m_allTables->GetCount() > 0)
|
||||
m_allTables->Select(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SelGenTablesPage::OnButtonAddAll(wxCommandEvent &)
|
||||
{
|
||||
int itemsCount = m_allTables->GetCount();
|
||||
if( itemsCount > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
m_allTables->Deselect(0);
|
||||
m_selTables->Append(m_allTables->GetString(0));
|
||||
m_allTables->Delete(0);
|
||||
itemsCount--;
|
||||
}
|
||||
while(itemsCount > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SelGenTablesPage::OnButtonRemove(wxCommandEvent &)
|
||||
{
|
||||
wxArrayInt positions;
|
||||
if(m_selTables->GetSelections(positions) > 0)
|
||||
{
|
||||
int i;
|
||||
int size = positions.Count(); // Warning about conversion should be ignored
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
m_allTables->Append(m_selTables->GetString(positions[i]));
|
||||
m_selTables->Deselect(positions[i]);
|
||||
}
|
||||
|
||||
for(i = (size - 1); i >= 0 ; i--)
|
||||
{
|
||||
m_selTables->Delete(positions[i]);
|
||||
}
|
||||
|
||||
if(m_selTables->GetCount() > 0)
|
||||
m_selTables->Select(0);
|
||||
}
|
||||
}
|
||||
|
||||
void SelGenTablesPage::OnButtonRemoveAll(wxCommandEvent &)
|
||||
{
|
||||
int itemsCount = m_selTables->GetCount();
|
||||
if( itemsCount > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
m_selTables->Deselect(0);
|
||||
m_allTables->Append(m_selTables->GetString(0));
|
||||
m_selTables->Delete(0);
|
||||
itemsCount--;
|
||||
}
|
||||
while(itemsCount > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SelGenTablesPage::moveToSelectList(wxString tableName)
|
||||
{
|
||||
int position = m_allTables->FindString(tableName);
|
||||
if(position != wxNOT_FOUND)
|
||||
{
|
||||
m_selTables->Append(m_allTables->GetString(position));
|
||||
m_allTables->Delete(position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SelGenTablesPage::RefreshTablesList()
|
||||
{
|
||||
wxArrayString tablesList;
|
||||
m_allTables->Clear();
|
||||
m_selTables->Clear();
|
||||
|
||||
hdIteratorBase *iterator = wparent->getDesign()->getEditor()->modelFiguresEnumerator();
|
||||
hdIFigure *tmpFigure;
|
||||
ddTableFigure *table;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmpFigure = (hdIFigure *)iterator->Next();
|
||||
if(tmpFigure->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmpFigure;
|
||||
tablesList.Add(table->getTableName());
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
m_allTables->Set(tablesList, (void **)NULL);
|
||||
|
||||
int max = wparent->preSelTables.Count();
|
||||
if(max > 0)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < max; i++)
|
||||
{
|
||||
moveToSelectList(wparent->preSelTables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SelGenTablesPage::OnWizardPageChanging(wxWizardEvent &event)
|
||||
{
|
||||
if(event.GetDirection() && m_selTables->GetCount() <= 0)
|
||||
{
|
||||
wxMessageBox(_("Please select at least one table to move to next step."), _("No tables selected"), wxICON_WARNING | wxOK, this);
|
||||
event.Veto();
|
||||
}
|
||||
}
|
||||
|
||||
// ----- SelGenSchemaPage Implementation
|
||||
|
||||
SelGenSchemaPage::SelGenSchemaPage(wxWizard *parent, wxWizardPage *prev)
|
||||
: wxWizardPage(parent)
|
||||
{
|
||||
wparent = (ddGenerationWizard *) parent;
|
||||
m_prev = prev;
|
||||
m_next = NULL;
|
||||
|
||||
// A top-level sizer
|
||||
wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL );
|
||||
this->SetSizer(topSizer);
|
||||
|
||||
//Add a message
|
||||
message = new wxStaticText(this, wxID_STATIC, _("Please select a schema to use as check target.\n\nThis affects the choice between CREATE/ALTER table(s) matching for the DDL generated depending on whether or not the table exists in that schema."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
topSizer->Add(message);
|
||||
topSizer->AddSpacer(10);
|
||||
|
||||
//Get Schemas info
|
||||
if(wparent && wparent->getConnection())
|
||||
refreshSchemas(wparent->getConnection());
|
||||
else
|
||||
schemasNames.Add(_("No schema"));
|
||||
|
||||
//Add a listbox with schemas
|
||||
m_allSchemas = new wxListBox(this, DDALLSCHEMAS, wxDefaultPosition, wxDefaultSize, schemasNames, wxLB_SORT | wxLB_ALWAYS_SB | wxLB_SINGLE);
|
||||
topSizer->Add(m_allSchemas, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
SelGenSchemaPage::~SelGenSchemaPage()
|
||||
{
|
||||
if(m_allSchemas)
|
||||
delete m_allSchemas;
|
||||
if(message)
|
||||
delete message;
|
||||
}
|
||||
|
||||
void SelGenSchemaPage::OnWizardPageChanging(wxWizardEvent &event)
|
||||
{
|
||||
if(event.GetDirection() && m_allSchemas->GetSelection() == wxNOT_FOUND)
|
||||
{
|
||||
wxMessageBox(_("Please select an item to move to next step."), _("No choice made"), wxICON_WARNING | wxOK, this);
|
||||
event.Veto();
|
||||
}
|
||||
else if(event.GetDirection())
|
||||
{
|
||||
if(m_allSchemas->GetSelection() > 0)
|
||||
{
|
||||
wparent->OIDSelectedSchema = schemasHM[schemasNames[m_allSchemas->GetSelection()]];
|
||||
wparent->schemaName = schemasNames[m_allSchemas->GetSelection()];
|
||||
wxArrayString tables = ddImportDBUtils::getTablesNames(wparent->getConnection(), wparent->schemaName);
|
||||
wparent->getDesign()->unMarkSchemaOnAll();
|
||||
wparent->getDesign()->markSchemaOn(tables);
|
||||
}
|
||||
else
|
||||
{
|
||||
wparent->OIDSelectedSchema = -1;
|
||||
wparent->schemaName = _("");
|
||||
}
|
||||
wparent->page4->populateGrid();
|
||||
}
|
||||
}
|
||||
|
||||
void SelGenSchemaPage::refreshSchemas(pgConn *connection)
|
||||
{
|
||||
|
||||
schemasHM.clear();
|
||||
schemasNames.Clear();
|
||||
schemasNames.Add(wxT("Not schema selected"));
|
||||
|
||||
// Search schemas and insert them
|
||||
wxString restr = wxT(" WHERE ")
|
||||
wxT("NOT ")
|
||||
wxT("((nspname = 'pg_catalog' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'pg_class' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
|
||||
wxT("(nspname = 'pgagent' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'pga_job' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
|
||||
wxT("(nspname = 'information_schema' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
|
||||
wxT("(nspname LIKE '_%' AND EXISTS (SELECT 1 FROM pg_proc WHERE proname='slonyversion' AND pronamespace = nsp.oid LIMIT 1)) OR\n")
|
||||
wxT("(nspname = 'dbo' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'systables' AND relnamespace = nsp.oid LIMIT 1)) OR\n")
|
||||
wxT("(nspname = 'sys' AND EXISTS (SELECT 1 FROM pg_class WHERE relname = 'all_tables' AND relnamespace = nsp.oid LIMIT 1)))\n");
|
||||
|
||||
if (connection->EdbMinimumVersion(8, 2))
|
||||
{
|
||||
restr += wxT(" AND nsp.nspparent = 0\n");
|
||||
// Do not show dbms_job_procedure in schemas
|
||||
if (!settings->GetShowSystemObjects())
|
||||
restr += wxT("AND NOT (nspname = 'dbms_job_procedure' AND EXISTS(SELECT 1 FROM pg_proc WHERE pronamespace = nsp.oid and proname = 'run_job' LIMIT 1))\n");
|
||||
}
|
||||
|
||||
wxString sql;
|
||||
|
||||
if (connection->BackendMinimumVersion(8, 1))
|
||||
{
|
||||
sql = wxT("SELECT CASE WHEN nspname LIKE E'pg\\\\_temp\\\\_%' THEN 1\n")
|
||||
wxT(" WHEN (nspname LIKE E'pg\\\\_%') THEN 0\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = wxT("SELECT CASE WHEN nspname LIKE 'pg\\\\_temp\\\\_%' THEN 1\n")
|
||||
wxT(" WHEN (nspname LIKE 'pg\\\\_%') THEN 0\n");
|
||||
}
|
||||
sql += wxT(" ELSE 3 END AS nsptyp, nspname, nsp.oid\n")
|
||||
wxT(" FROM pg_namespace nsp\n")
|
||||
+ restr +
|
||||
wxT(" ORDER BY 1, nspname");
|
||||
|
||||
pgSet *schemas = connection->ExecuteSet(sql);
|
||||
wxTreeItemId parent;
|
||||
|
||||
if (schemas)
|
||||
{
|
||||
while (!schemas->Eof())
|
||||
{
|
||||
wxString name = schemas->GetVal(wxT("nspname"));
|
||||
long nsptyp = schemas->GetLong(wxT("nsptyp"));
|
||||
|
||||
wxStringTokenizer tokens(settings->GetSystemSchemas(), wxT(","));
|
||||
while (tokens.HasMoreTokens())
|
||||
{
|
||||
wxRegEx regex(tokens.GetNextToken());
|
||||
if (regex.Matches(name))
|
||||
{
|
||||
nsptyp = SCHEMATYP_USERSYS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nsptyp <= SCHEMATYP_USERSYS && !settings->GetShowSystemObjects())
|
||||
{
|
||||
schemas->MoveNext();
|
||||
continue;
|
||||
}
|
||||
schemasNames.Add(name);
|
||||
schemasHM[name] = schemas->GetOid(wxT("oid"));
|
||||
schemas->MoveNext();
|
||||
}
|
||||
|
||||
delete schemas;
|
||||
}
|
||||
}
|
||||
|
||||
// ----- SelGenSchemaPage Implementation
|
||||
|
||||
ReportGridPage::ReportGridPage(wxWizard *parent, wxWizardPage *prev)
|
||||
: wxWizardPage(parent)
|
||||
{
|
||||
wparent = (ddGenerationWizard *) parent;
|
||||
m_prev = prev;
|
||||
m_next = NULL;
|
||||
|
||||
// A top-level sizer
|
||||
wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL );
|
||||
this->SetSizer(topSizer);
|
||||
|
||||
// Add a message
|
||||
message = new wxStaticText(this, wxID_STATIC, _("Please check the choice for each table:"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
topSizer->Add(message);
|
||||
topSizer->AddSpacer(10);
|
||||
|
||||
reportGrid = new wxDDGrid(this, DDTABSGRID);
|
||||
reportGrid->SetRowLabelSize(25);
|
||||
reportGrid->CreateGrid(wparent->page2->countSelTables(), 2);
|
||||
populateGrid();
|
||||
topSizer->Add(reportGrid, 1, wxEXPAND);
|
||||
|
||||
// Add Event
|
||||
this->Connect(DDTABSGRID, wxEVT_GRID_CELL_LEFT_CLICK, (wxObjectEventFunction) (wxEventFunction) (wxGridEventFunction) &ReportGridPage::OnCellLeftClick);
|
||||
}
|
||||
|
||||
ReportGridPage::~ReportGridPage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ReportGridPage::populateGrid()
|
||||
{
|
||||
reportGrid->ClearGrid();
|
||||
if(reportGrid->GetNumberRows() > 0)
|
||||
reportGrid->DeleteRows(0, reportGrid->GetNumberRows());
|
||||
int row , max = wparent->page2->countSelTables();
|
||||
reportGrid->InsertRows(0, max);
|
||||
reportGrid->SetColLabelValue(0, _("Tables"));
|
||||
reportGrid->SetColLabelValue(1, _("Select an action"));
|
||||
wxString strChoicesSimple[1] = {_("Create table")};
|
||||
wxString strChoicesAlter[4] = {_("Alter table"), _("Drop, then create"), _("Create table [conflict]"), wxT("No action")};
|
||||
|
||||
//Two Steps process first for tables that belongs to a schema (need alter table)
|
||||
//Later for normal tables (just create table)
|
||||
int useRow = 0;
|
||||
for(row = 0; row < max; row++)
|
||||
{
|
||||
ddTableFigure *table = wparent->getDesign()->getTable(wparent->page2->getSelTableName(row));
|
||||
if(table != NULL && table->getBelongsToSchema())
|
||||
{
|
||||
wxString labelString = wxString::Format(wxT("%i"), (row + 1));
|
||||
reportGrid->SetRowLabelValue(row, labelString);
|
||||
reportGrid->SetReadOnly( row, 0, true);
|
||||
reportGrid->SetCellValue( row, 0, wparent->page2->getSelTableName(row));
|
||||
reportGrid->SetCellRenderer(row, 1, new wxGridCellComboBoxRenderer);
|
||||
reportGrid->SetCellEditor(row, 1, new dxGridCellSizedChoiceEditor(WXSIZEOF(strChoicesAlter), strChoicesAlter));
|
||||
reportGrid->SetCellValue( row, 1, _("Alter table"));
|
||||
reportGrid->SetCellBackgroundColour(row, 0, LIGHT_YELLOW);
|
||||
reportGrid->SetCellBackgroundColour(row, 1, LIGHT_YELLOW);
|
||||
useRow++;
|
||||
}
|
||||
else if(table == NULL)
|
||||
{
|
||||
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error at generation process"), wxICON_ERROR | wxOK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal tables (just create table)
|
||||
for(row = 0; row < max; row++)
|
||||
{
|
||||
ddTableFigure *table = wparent->getDesign()->getTable(wparent->page2->getSelTableName(row));
|
||||
if(table != NULL && !table->getBelongsToSchema())
|
||||
{
|
||||
reportGrid->SetCellValue( row, 0, wparent->page2->getSelTableName(row));
|
||||
reportGrid->SetCellRenderer(row, 1, new wxGridCellComboBoxRenderer);
|
||||
reportGrid->SetCellEditor(row, 1, new dxGridCellSizedChoiceEditor(WXSIZEOF(strChoicesSimple), strChoicesSimple));
|
||||
reportGrid->SetCellValue( row, 1, _("Create table"));
|
||||
useRow++;
|
||||
}
|
||||
else if(table == NULL)
|
||||
{
|
||||
wxMessageBox(_("Metadata of table to be generated not found at database designer model"), _("Error importing at generation process"), wxICON_ERROR | wxOK);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reportGrid->AutoSizeColumns();
|
||||
reportGrid->Fit();
|
||||
|
||||
}
|
||||
|
||||
void ReportGridPage::OnWizardPageChanging(wxWizardEvent &event)
|
||||
{
|
||||
if(!event.GetDirection())
|
||||
{
|
||||
//Reset tables after a warning
|
||||
int answer = wxMessageBox(_("Going back to \"Select schema\" page will reinitialize all selections.\nDo you want to continue?"), _("Going back?"), wxYES_NO | wxCANCEL, this);
|
||||
if (answer != wxYES)
|
||||
{
|
||||
event.Veto();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReportGridPage::OnCellLeftClick(wxGridEvent &event)
|
||||
{
|
||||
// Only show editor y case of column 1
|
||||
if(event.GetCol() == 1)
|
||||
{
|
||||
reportGrid->ComboBoxEvent(event);
|
||||
}
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// -- Special version of wxGrid to allow use of fast comboboxes and grid columns auto fit
|
||||
//
|
||||
wxDDGrid::wxDDGrid(wxWindow *parent, wxWindowID id):
|
||||
wxGrid(parent, id, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_BESTWRAP, wxT("")),
|
||||
m_selTemp(NULL), keepFit(1)
|
||||
{
|
||||
// Initially all columns have s-factor=1
|
||||
for( unsigned i = 0; i < 10; ++i ) sf[i] = 1;
|
||||
|
||||
Connect( wxEVT_SIZE, wxSizeEventHandler( wxDDGrid::OnSizeEvt) );
|
||||
// Adjust the default row height to be more compact
|
||||
wxFont font = GetLabelFont();
|
||||
int nWidth = 0;
|
||||
int nHeight = 18;
|
||||
GetTextExtent(wxT("W"), &nWidth, &nHeight, NULL, NULL, &font);
|
||||
SetColLabelSize(nHeight + 6);
|
||||
SetRowLabelSize(35);
|
||||
#ifdef __WXGTK__
|
||||
SetDefaultRowSize(nHeight + 8, TRUE);
|
||||
#else
|
||||
SetDefaultRowSize(nHeight + 4, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void wxDDGrid::ComboBoxEvent(wxGridEvent &event)
|
||||
{
|
||||
|
||||
// This forces the cell to go into edit mode directly
|
||||
this->m_waitForSlowClick = TRUE;
|
||||
int row = event.GetRow();
|
||||
int col = event.GetCol();
|
||||
|
||||
this->SetGridCursor(row, col);
|
||||
|
||||
// Store the click co-ordinates in the editor if possible
|
||||
// if an editor has created a ClientData area, we presume it's
|
||||
// a wxPoint and we store the click co-ordinates
|
||||
wxGridCellEditor *pEditor = this->GetCellEditor(event.GetRow(), event.GetCol());
|
||||
wxPoint *pClickPoint = (wxPoint *)pEditor->GetClientData();
|
||||
if (pClickPoint)
|
||||
{
|
||||
*pClickPoint = this->ClientToScreen(event.GetPosition());
|
||||
#ifndef __WINDOWS__
|
||||
EnableCellEditControl(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
// hack to prevent selection from being lost when click combobox
|
||||
if (this->IsInSelection(event.GetRow(), event.GetCol()))
|
||||
{
|
||||
this->m_selTemp = this->m_selection;
|
||||
this->m_selection = NULL;
|
||||
}
|
||||
pEditor->DecRef();
|
||||
}
|
||||
|
||||
void wxDDGrid::RevertSel()
|
||||
{
|
||||
if (m_selTemp)
|
||||
{
|
||||
wxASSERT(m_selection == NULL);
|
||||
m_selection = m_selTemp;
|
||||
m_selTemp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void wxDDGrid::OnSizeEvt( wxSizeEvent &ev )
|
||||
{
|
||||
if( !StretchIt() ) ev.Skip();
|
||||
}
|
||||
|
||||
int wxDDGrid::StretchIt()
|
||||
{
|
||||
int new_width = GetClientSize().GetWidth() - GetRowLabelSize() - 10;
|
||||
int fixedWidth = 0, numStretches = 0, numStretched = 0;
|
||||
|
||||
for( int i = 0; i < GetNumberCols(); ++i )
|
||||
{
|
||||
if( sf[i] == 0 ) fixedWidth += GetColSize(i);
|
||||
else if( sf[i] < 0 )
|
||||
{
|
||||
AutoSizeColumn(i, false);
|
||||
fixedWidth += GetColSize(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
numStretches += sf[i];
|
||||
numStretched += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now either we have space for normal layout or resort to wxGrid default behaviour
|
||||
if( numStretched && ((fixedWidth + numStretched * 10) < new_width) )
|
||||
{
|
||||
int stretchSpace = (new_width - fixedWidth) / numStretches;
|
||||
//BeginBatch();
|
||||
int i, max = GetNumberCols();
|
||||
for(i = 0; i < max; ++i )
|
||||
if( sf[i] > 0 )
|
||||
SetColSize(i, stretchSpace * sf[i]);
|
||||
//EndBatch();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
114
dd/ddmodel/ddModelBrowser.cpp
Normal file
114
dd/ddmodel/ddModelBrowser.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ddModelBrowser.cpp - Tables Tree of Database Designer.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/regex.h>
|
||||
#include <wx/imaglist.h>
|
||||
|
||||
// App headers
|
||||
#include "dd/ddmodel/ddModelBrowser.h"
|
||||
#include "dd/ddmodel/ddDatabaseDesign.h"
|
||||
#include "dd/ddmodel/ddBrowserDataContainer.h"
|
||||
|
||||
// Images
|
||||
#include "images/table-sm.pngc"
|
||||
#include "images/database-sm.pngc"
|
||||
|
||||
|
||||
BEGIN_EVENT_TABLE(ddModelBrowser, wxTreeCtrl)
|
||||
EVT_TREE_ITEM_ACTIVATED(DD_BROWSER, ddModelBrowser::OnItemActivated)
|
||||
EVT_TREE_BEGIN_DRAG(DD_BROWSER, ddModelBrowser::OnBeginDrag)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
ddModelBrowser::ddModelBrowser(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, ddDatabaseDesign *design)
|
||||
: wxTreeCtrl(parent, id, pos, size, style)
|
||||
{
|
||||
ownerDesign = design;
|
||||
rootNode = (wxTreeItemId *)NULL;
|
||||
createRoot(_("Database Design"));
|
||||
|
||||
// Create normal images list of browser
|
||||
// Remember to update enum gqbImages in ddModelBrowser.h if changing the images!!
|
||||
imageList = new wxImageList(16, 16);
|
||||
imageList->Add(*database_sm_png_ico);
|
||||
imageList->Add(*table_sm_png_ico);
|
||||
this->AssignImageList(imageList);
|
||||
}
|
||||
|
||||
|
||||
// Destructor
|
||||
ddModelBrowser::~ddModelBrowser()
|
||||
{
|
||||
this->DeleteAllItems(); // This remove and delete data inside tree's node
|
||||
}
|
||||
|
||||
|
||||
// Create root node
|
||||
wxTreeItemId &ddModelBrowser::createRoot(wxString Name)
|
||||
{
|
||||
rootNode = this->AddRoot(Name, DD_IMG_FIG_DATABASE, DD_IMG_FIG_DATABASE);
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
|
||||
void ddModelBrowser::refreshFromModel()
|
||||
{
|
||||
|
||||
this->DeleteAllItems();
|
||||
createRoot(_("Database Design"));
|
||||
|
||||
hdIteratorBase *iterator = ownerDesign->getEditor()->modelFiguresEnumerator();
|
||||
hdIFigure *tmpFigure;
|
||||
ddTableFigure *table;
|
||||
|
||||
while(iterator->HasNext())
|
||||
{
|
||||
tmpFigure = (hdIFigure *)iterator->Next();
|
||||
if(tmpFigure->getKindId() == DDTABLEFIGURE)
|
||||
{
|
||||
table = (ddTableFigure *)tmpFigure;
|
||||
this->AppendItem(rootNode, table->getTableName(), DD_IMG_FIG_TABLE, DD_IMG_FIG_TABLE, new ddBrowserDataContainer(table));
|
||||
}
|
||||
}
|
||||
delete iterator;
|
||||
|
||||
this->Expand(rootNode);
|
||||
this->SortChildren(rootNode);
|
||||
}
|
||||
|
||||
void ddModelBrowser::OnItemActivated(wxTreeEvent &event)
|
||||
{
|
||||
}
|
||||
|
||||
void ddModelBrowser::OnBeginDrag(wxTreeEvent &event)
|
||||
{
|
||||
wxTreeItemId itemId = event.GetItem();
|
||||
|
||||
// Simplest solution, simulate DnD but actually don't do it
|
||||
ddBrowserDataContainer *object = (ddBrowserDataContainer *) GetItemData(itemId);
|
||||
|
||||
if(object != NULL && (object->getFigureKindId() == DDTABLEFIGURE))
|
||||
{
|
||||
ddTableFigure *item = (ddTableFigure *) object->getFigure();
|
||||
wxString tableName = item->getTableName();
|
||||
wxTextDataObject textData(tableName);
|
||||
wxDropSource dragSource(this);
|
||||
dragSource.SetData(textData);
|
||||
wxDragResult result = dragSource.DoDragDrop(wxDrag_CopyOnly);
|
||||
if(result != wxDragCopy)
|
||||
{
|
||||
wxMessageBox(wxT("Invalid kind of data during drag and drop operation"), wxT("Drag and drop error"), wxICON_ERROR | wxOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
dd/ddmodel/module.mk
Normal file
22
dd/ddmodel/module.mk
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/ddmodel/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
pgadmin3_SOURCES += \
|
||||
dd/ddmodel/ddDatabaseDesign.cpp \
|
||||
dd/ddmodel/ddDrawingEditor.cpp \
|
||||
dd/ddmodel/ddDBReverseEnginering.cpp \
|
||||
dd/ddmodel/ddModelBrowser.cpp \
|
||||
dd/ddmodel/ddGenerationWizard.cpp \
|
||||
dd/ddmodel/ddDrawingView.cpp \
|
||||
dd/ddmodel/ddBrowserDataContainer.cpp
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/ddmodel/module.mk
|
||||
17
dd/module.mk
Normal file
17
dd/module.mk
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#######################################################################
|
||||
#
|
||||
# pgAdmin III - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
# module.mk - pgadmin/dd/ Makefile fragment
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
include dd/dditems/module.mk
|
||||
include dd/ddmodel/module.mk
|
||||
|
||||
|
||||
EXTRA_DIST += \
|
||||
dd/module.mk
|
||||
79
debugger/ctlMessageWindow.cpp
Normal file
79
debugger/ctlMessageWindow.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlMessageWindow.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "debugger/ctlMessageWindow.h"
|
||||
|
||||
IMPLEMENT_CLASS(ctlMessageWindow, wxTextCtrl)
|
||||
|
||||
BEGIN_EVENT_TABLE(ctlMessageWindow, wxTextCtrl)
|
||||
EVT_TIMER(wxID_ANY, ctlMessageWindow::OnTimer)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ctlMessageWindow constructor
|
||||
//
|
||||
// Initialize the grid control and clear it out....
|
||||
//
|
||||
|
||||
ctlMessageWindow::ctlMessageWindow(wxWindow *parent, wxWindowID id)
|
||||
: wxTextCtrl(parent, wxID_ANY, wxT(""), wxPoint(0, 0), wxSize(0, 0),
|
||||
wxTE_MULTILINE | wxTE_READONLY)
|
||||
{
|
||||
SetFont(settings->GetSQLFont());
|
||||
m_timer.SetOwner(this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AddMessage()
|
||||
//
|
||||
// Adds the message in the 'DBMS Messages' window.
|
||||
//
|
||||
|
||||
void ctlMessageWindow::AddMessage(wxString message)
|
||||
{
|
||||
m_mutex.Lock();
|
||||
m_currMsg += message + wxT("\n");
|
||||
m_mutex.Unlock();
|
||||
|
||||
if (!m_timer.IsRunning())
|
||||
m_timer.Start(100, true);
|
||||
}
|
||||
|
||||
void ctlMessageWindow::OnTimer(wxTimerEvent &ev)
|
||||
{
|
||||
m_mutex.Lock();
|
||||
AppendText(m_currMsg);
|
||||
m_currMsg = wxEmptyString;
|
||||
m_mutex.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// delMessage()
|
||||
//
|
||||
// Removes the given message from the 'DBMS Messages' window.
|
||||
//
|
||||
|
||||
void ctlMessageWindow::DelMessage(const char *name)
|
||||
{
|
||||
SetValue(wxT(""));
|
||||
}
|
||||
|
||||
|
||||
wxString ctlMessageWindow::GetMessage(int row)
|
||||
{
|
||||
return(GetValue());
|
||||
}
|
||||
100
debugger/ctlResultGrid.cpp
Normal file
100
debugger/ctlResultGrid.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlResultGrid.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "debugger/ctlResultGrid.h"
|
||||
|
||||
IMPLEMENT_CLASS( ctlResultGrid, wxGrid )
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ctlResultGrid constructor
|
||||
//
|
||||
// We use a ctlResultGrid to display the result set from a query. This class
|
||||
// is a minor extension of the wxGrid class.
|
||||
|
||||
ctlResultGrid::ctlResultGrid( wxWindow *parent, wxWindowID id )
|
||||
: wxGrid( parent, id )
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
|
||||
CreateGrid( 0, 0 );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// fillGrid()
|
||||
//
|
||||
// Given a result set handle, this function copies the values in that result
|
||||
// set into the grid.
|
||||
|
||||
void ctlResultGrid::FillResult(pgSet *set)
|
||||
{
|
||||
// Clear out the old results (if any) and resize
|
||||
// grid to match the result set
|
||||
if( GetNumberRows())
|
||||
DeleteRows( 0, GetNumberRows());
|
||||
if( GetNumberCols())
|
||||
DeleteCols( 0, GetNumberCols());
|
||||
|
||||
if (!set)
|
||||
return;
|
||||
|
||||
int rowCount = set->NumRows();
|
||||
int colCount = set->NumCols();
|
||||
|
||||
// If this PGresult represents a non-query command
|
||||
// (like an INSERT), there won't be any columns in
|
||||
// the result set - just return
|
||||
if( colCount == 0 )
|
||||
return;
|
||||
|
||||
// Disable repaints to we don't flicker too much
|
||||
|
||||
BeginBatch();
|
||||
|
||||
AppendRows(rowCount);
|
||||
AppendCols(colCount);
|
||||
|
||||
EnableEditing(false);
|
||||
|
||||
// Copy the column names from the result set into the column headers
|
||||
int row = 0,
|
||||
col;
|
||||
for(col = 0; col < colCount; ++col)
|
||||
SetColLabelValue(col, set->ColName(col));
|
||||
wxGridCellAttr* pAttr = new wxGridCellAttr;
|
||||
pAttr->SetBackgroundColour(wxColour(128,128,128));
|
||||
//SetDefaultCellBackgroundColour(wxColour(128,128,128));
|
||||
|
||||
// Now copy each value from the result set into the grid
|
||||
while(!set->Eof())
|
||||
{
|
||||
for(col = 0; col < colCount; ++col)
|
||||
{
|
||||
if(set->IsNull(col))
|
||||
SetCellValue(row, col, wxT(""));
|
||||
else
|
||||
SetCellValue(row, col, set->GetVal(col));
|
||||
}
|
||||
row++;
|
||||
set->MoveNext();
|
||||
}
|
||||
|
||||
// Resize each column to fit its content
|
||||
AutoSizeColumns(false);
|
||||
|
||||
// Enable repaints
|
||||
EndBatch();
|
||||
}
|
||||
88
debugger/ctlStackWindow.cpp
Normal file
88
debugger/ctlStackWindow.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlStackWindow.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/listimpl.cpp>
|
||||
|
||||
// App headers
|
||||
#include "debugger/ctlStackWindow.h"
|
||||
|
||||
WX_DEFINE_LIST(dbgStackFrameList);
|
||||
|
||||
IMPLEMENT_CLASS(ctlStackWindow, wxListBox)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ctlStackWindow constructor
|
||||
//
|
||||
// Initialize the grid control and clear it out....
|
||||
//
|
||||
|
||||
ctlStackWindow::ctlStackWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name )
|
||||
: wxListBox(parent , id, pos, size, 0, NULL, style | wxLB_HSCROLL | wxLB_NEEDED_SB )
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ClearStack()
|
||||
//
|
||||
// Remove all stack frames from the display
|
||||
//
|
||||
void ctlStackWindow::ClearStack()
|
||||
{
|
||||
Set(0, NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SetStack()
|
||||
//
|
||||
// Add an array of stack frames to the display
|
||||
//
|
||||
void ctlStackWindow::SetStack(const dbgStackFrameList &stacks, int selected)
|
||||
{
|
||||
Set(0, NULL);
|
||||
|
||||
for (dbgStackFrameList::Node *node = stacks.GetFirst(); node;
|
||||
node = node->GetNext())
|
||||
{
|
||||
dbgStackFrame *frame = node->GetData();
|
||||
Append(frame->GetDescription(), (wxClientData *)frame);
|
||||
}
|
||||
if (selected != -1)
|
||||
{
|
||||
SetSelection(selected);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SelectFrame()
|
||||
//
|
||||
// Select the frame based on the input function and package
|
||||
//
|
||||
void ctlStackWindow::SelectFrame(const wxString &pkg, const wxString &func)
|
||||
{
|
||||
int cnt = GetCount();
|
||||
|
||||
for (int idx = 0; idx < cnt; idx++)
|
||||
{
|
||||
dbgStackFrame *frame = (dbgStackFrame *)GetClientObject(idx);
|
||||
|
||||
if (frame && frame->GetFunction() == func && frame->GetPackage() == pkg)
|
||||
{
|
||||
SetSelection(idx);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
175
debugger/ctlTabWindow.cpp
Normal file
175
debugger/ctlTabWindow.cpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlTabWindow.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
// App headers
|
||||
#include "ctl/ctlAuiNotebook.h"
|
||||
#include "debugger/ctlTabWindow.h"
|
||||
#include "debugger/dbgConst.h"
|
||||
|
||||
|
||||
IMPLEMENT_CLASS(ctlTabWindow, wxWindow)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ctlTabWindow constructor
|
||||
//
|
||||
// This constructor creates a new notebook (a tab control) and clears out the
|
||||
// rest of the data members.
|
||||
//
|
||||
ctlTabWindow::ctlTabWindow(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
||||
const wxSize &size, long style, const wxString &name)
|
||||
: ctlAuiNotebook(parent, id, pos, size, style),
|
||||
m_resultWindow(NULL),
|
||||
m_varWindow(NULL),
|
||||
m_pkgVarWindow(NULL),
|
||||
m_stackWindow(NULL),
|
||||
m_paramWindow(NULL),
|
||||
m_messageWindow(NULL)
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
m_tabMap = new wsTabHash();
|
||||
}
|
||||
|
||||
void ctlTabWindow::SelectTab(wxWindowID id)
|
||||
{
|
||||
wsTabHash::iterator result = m_tabMap->find(id);
|
||||
|
||||
if (result != m_tabMap->end())
|
||||
{
|
||||
SetSelection(result->second);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetResultWindow()
|
||||
//
|
||||
// This function returns a pointer to our child result window (m_resultWindow)
|
||||
// and creates that window when we first need it.
|
||||
//
|
||||
ctlResultGrid *ctlTabWindow::GetResultWindow()
|
||||
{
|
||||
if (m_resultWindow == 0)
|
||||
{
|
||||
// We don't have a result window yet - go ahead and create one
|
||||
|
||||
m_resultWindow = new ctlResultGrid(this, -1);
|
||||
AddPage(m_resultWindow, _("Results"), true);
|
||||
}
|
||||
|
||||
return m_resultWindow;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetVarWindow()
|
||||
//
|
||||
// This function returns a pointer to our child 'local-variables' window
|
||||
// (m_varWindow) and creates that window when we first need it.
|
||||
//
|
||||
ctlVarWindow *ctlTabWindow::GetVarWindow(bool _create)
|
||||
{
|
||||
if ((m_varWindow == NULL) && _create)
|
||||
{
|
||||
// We don't have a variable window yet - go ahead and create one
|
||||
|
||||
(*m_tabMap)[ID_VARGRID] = GetPageCount();
|
||||
|
||||
m_varWindow = new ctlVarWindow(this, ID_VARGRID);
|
||||
AddPage(m_varWindow, _("Local Variables"), true);
|
||||
}
|
||||
|
||||
return m_varWindow;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetPkgVarWindow()
|
||||
//
|
||||
// This function returns a pointer to our child 'package-variables' window
|
||||
// (m_varWindow) and creates that window when we first need it.
|
||||
//
|
||||
ctlVarWindow *ctlTabWindow::GetPkgVarWindow(bool create)
|
||||
{
|
||||
if ((m_pkgVarWindow == NULL) && create)
|
||||
{
|
||||
// We don't have a variable window yet - go ahead and create one
|
||||
|
||||
(*m_tabMap)[ID_PKGVARGRID] = GetPageCount();
|
||||
|
||||
m_pkgVarWindow = new ctlVarWindow(this, ID_PKGVARGRID);
|
||||
AddPage(m_pkgVarWindow, _("Package Variables"), true);
|
||||
}
|
||||
|
||||
return m_pkgVarWindow;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetParamWindow()
|
||||
//
|
||||
// This function returns a pointer to our child 'parameters' window
|
||||
// (m_paramWindow) and creates that window when we first need it.
|
||||
//
|
||||
ctlVarWindow *ctlTabWindow::GetParamWindow(bool create)
|
||||
{
|
||||
if ((m_paramWindow == NULL) && create)
|
||||
{
|
||||
// We don't have a variable window yet - go ahead and create one
|
||||
|
||||
(*m_tabMap)[ID_PARAMGRID] = GetPageCount();
|
||||
|
||||
m_paramWindow = new ctlVarWindow(this, ID_PARAMGRID);
|
||||
AddPage(m_paramWindow, _("Parameters"), true);
|
||||
}
|
||||
|
||||
return m_paramWindow;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetMessageWindow()
|
||||
//
|
||||
// This function returns a pointer to our child 'messages' window
|
||||
// (m_messageWindow) and creates that window when we first need it.
|
||||
//
|
||||
ctlMessageWindow *ctlTabWindow::GetMessageWindow()
|
||||
{
|
||||
if (m_messageWindow == 0)
|
||||
{
|
||||
// We don't have a variable window yet - go ahead and create one
|
||||
|
||||
(*m_tabMap)[ID_MSG_PAGE] = GetPageCount();
|
||||
|
||||
m_messageWindow = new ctlMessageWindow(this, ID_MSG_PAGE);
|
||||
AddPage(m_messageWindow, _("DBMS Messages"), true);
|
||||
}
|
||||
return m_messageWindow;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GetStackWindow()
|
||||
//
|
||||
// This function returns a pointer to our child stack-trace window
|
||||
// (m_stackWindow) and creates that window when we first need it.
|
||||
//
|
||||
ctlStackWindow *ctlTabWindow::GetStackWindow()
|
||||
{
|
||||
if (m_stackWindow == 0)
|
||||
{
|
||||
// We don't have a stack-trace window yet - go ahead and create one
|
||||
m_stackWindow = new ctlStackWindow(this, -1);
|
||||
AddPage(m_stackWindow, _("Stack"), true);
|
||||
}
|
||||
|
||||
return m_stackWindow;
|
||||
}
|
||||
|
||||
182
debugger/ctlVarWindow.cpp
Normal file
182
debugger/ctlVarWindow.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// ctlVarWindow.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
// App headers
|
||||
#include "debugger/ctlVarWindow.h"
|
||||
|
||||
IMPLEMENT_CLASS(ctlVarWindow, wxGrid)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ctlVarWindow constructor
|
||||
//
|
||||
// Initialize the grid control and clear it out....
|
||||
//
|
||||
ctlVarWindow::ctlVarWindow(wxWindow *parent, wxWindowID id)
|
||||
: wxGrid(parent, id),
|
||||
m_cells(NULL),
|
||||
m_nameFont(GetDefaultCellFont())
|
||||
{
|
||||
SetFont(settings->GetSystemFont());
|
||||
|
||||
// Create the grid control
|
||||
CreateGrid(0, 0);
|
||||
SetRowLabelSize(0); // Turn off the row labels
|
||||
|
||||
// Set up three columns: name, value, and data type
|
||||
AppendCols(3);
|
||||
SetColLabelValue(COL_NAME, _("Name"));
|
||||
SetColLabelValue(COL_TYPE, _("Type"));
|
||||
SetColLabelValue(COL_VALUE, _("Value"));
|
||||
|
||||
EnableDragGridSize(true);
|
||||
|
||||
// EDB wants to hide certain PL variables. To do that, we
|
||||
// keep a hash of hidden names and a hash of hidden types...
|
||||
m_hiddenNames.insert(wxT("found"));
|
||||
m_hiddenNames.insert(wxT("rowcount"));
|
||||
m_hiddenNames.insert(wxT("sqlcode"));
|
||||
m_hiddenNames.insert(wxT("sqlerrm"));
|
||||
m_hiddenNames.insert(wxT("_found"));
|
||||
m_hiddenNames.insert(wxT("_rowcount"));
|
||||
m_hiddenNames.insert(wxT("sqlstate"));
|
||||
|
||||
m_hiddenTypes.insert(wxT("refcursor"));
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AddVar()
|
||||
//
|
||||
// Adds (or updates) the given variable in the 'local variables' window. If
|
||||
// we find a variabled named 'name' in the window, we simply update the value,
|
||||
// otherwise, we create a new entry in the grid
|
||||
//
|
||||
void ctlVarWindow::AddVar(wxString name, wxString value, wxString type, bool readOnly)
|
||||
{
|
||||
// If this is a 'hidden' variable, just ignore it
|
||||
|
||||
if (m_hiddenNames.find(name) != m_hiddenNames.end())
|
||||
return;
|
||||
|
||||
if (m_hiddenTypes.find(type) != m_hiddenTypes.end())
|
||||
return;
|
||||
|
||||
if (m_cells == NULL)
|
||||
{
|
||||
// This is the first variable we're adding to this grid,
|
||||
// layout the grid and set the column headers.
|
||||
|
||||
m_cells = new wsCellHash;
|
||||
}
|
||||
|
||||
// Try to find an existing grid cell for this variable...
|
||||
wxString key(name);
|
||||
|
||||
wsCellHash::iterator cell = m_cells->find(key);
|
||||
|
||||
if (cell == m_cells->end())
|
||||
{
|
||||
// Can't find this variable in the grid, go ahead and add it
|
||||
|
||||
gridCell newCell;
|
||||
|
||||
newCell.m_row = m_cells->size();
|
||||
newCell.m_type = type;
|
||||
newCell.m_value = value;
|
||||
|
||||
AppendRows(1);
|
||||
|
||||
SetRowLabelValue(newCell.m_row, key);
|
||||
|
||||
SetCellValue(newCell.m_row, COL_NAME, key);
|
||||
SetCellValue(newCell.m_row, COL_TYPE, type);
|
||||
SetCellValue(newCell.m_row, COL_VALUE, value);
|
||||
|
||||
SetCellFont(newCell.m_row, COL_NAME, m_nameFont);
|
||||
|
||||
SetReadOnly(newCell.m_row, COL_NAME, true);
|
||||
SetReadOnly(newCell.m_row, COL_TYPE, true);
|
||||
SetReadOnly(newCell.m_row, COL_VALUE, readOnly);
|
||||
|
||||
(*m_cells)[key] = newCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This variable is already in the grid, update the value
|
||||
// and hilite it so the user knows that it has changed.
|
||||
|
||||
cell->second.m_value = value;
|
||||
|
||||
if (GetCellValue(cell->second.m_row, COL_VALUE).IsSameAs(value))
|
||||
SetCellTextColour(cell->second.m_row, COL_VALUE, *wxBLACK);
|
||||
else
|
||||
SetCellTextColour(cell->second.m_row, COL_VALUE, *wxRED);
|
||||
|
||||
SetCellValue(cell->second.m_row, COL_VALUE, value);
|
||||
|
||||
// FIXME: why is this part conditional?
|
||||
// FIXME: why do we need this code? can the type ever change?
|
||||
|
||||
if (GetCellValue(cell->second.m_row, COL_TYPE) == wxT(""))
|
||||
{
|
||||
SetCellValue(cell->second.m_row, COL_TYPE, type);
|
||||
}
|
||||
}
|
||||
|
||||
// AutoSizeColumns(false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// delVar()
|
||||
//
|
||||
// Removes the given variable from the 'local variables' window.
|
||||
//
|
||||
|
||||
void ctlVarWindow::DelVar(wxString name)
|
||||
{
|
||||
if (name.IsEmpty())
|
||||
{
|
||||
delete m_cells;
|
||||
m_cells = NULL;
|
||||
|
||||
if (GetNumberRows())
|
||||
DeleteRows(0, GetNumberRows());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int row = 0; row < GetNumberRows(); row++)
|
||||
{
|
||||
if (GetCellValue(row, COL_NAME) == name)
|
||||
{
|
||||
DeleteRows(row, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxString ctlVarWindow::GetVarName(int row)
|
||||
{
|
||||
return(GetCellValue(row, COL_NAME));
|
||||
|
||||
}
|
||||
|
||||
wxString ctlVarWindow::GetVarValue(int row)
|
||||
{
|
||||
return(GetCellValue(row, COL_VALUE));
|
||||
}
|
||||
23
debugger/dbgBreakPoint.cpp
Normal file
23
debugger/dbgBreakPoint.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dbgBreakPoint.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/listimpl.cpp>
|
||||
|
||||
// App headers
|
||||
#include "debugger/dbgBreakPoint.h"
|
||||
|
||||
WX_DEFINE_LIST( dbgBreakPointList );
|
||||
|
||||
|
||||
913
debugger/dbgController.cpp
Normal file
913
debugger/dbgController.cpp
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dbgController.cpp - debugger controller
|
||||
// - Flow and data controller for the debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
#include "ctl/ctlAuiNotebook.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgQueryThread.h"
|
||||
#include "db/pgQueryResultEvent.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "schema/pgTrigger.h"
|
||||
#include "debugger/dbgConst.h"
|
||||
#include "debugger/dbgBreakPoint.h"
|
||||
#include "debugger/dbgController.h"
|
||||
#include "debugger/dbgModel.h"
|
||||
#include "debugger/ctlStackWindow.h"
|
||||
#include "debugger/ctlMessageWindow.h"
|
||||
#include "debugger/ctlTabWindow.h"
|
||||
#include "debugger/frmDebugger.h"
|
||||
#include "debugger/dlgDirectDbg.h"
|
||||
|
||||
#include "utils/pgDefs.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#define LOG_DBG_MUTEX_LOCKING 0
|
||||
|
||||
#if LOG_DBG_MUTEX_LOCKING
|
||||
|
||||
#define LOCKMUTEX(m) \
|
||||
wxPrintf(wxT("Mutex locking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
||||
m.Lock(); \
|
||||
wxPrintf(wxT("Mutex locked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
||||
|
||||
#define UNLOCKMUTEX(m) \
|
||||
wxPrintf(wxT("Mutex unlocking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
||||
m.Unlock(); \
|
||||
wxPrintf(wxT("Mutex unlocked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
||||
|
||||
#else
|
||||
|
||||
#define LOCKMUTEX(m) \
|
||||
m.Lock();
|
||||
|
||||
#define UNLOCKMUTEX(m) \
|
||||
m.Unlock();
|
||||
|
||||
#endif
|
||||
|
||||
const wxString dbgController::ms_cmdDebugSPLV1(
|
||||
wxT("SELECT edb_oid_debug(%lu, %lu)"));
|
||||
const wxString dbgController::ms_cmdDebugSPLV2(
|
||||
wxT("SELECT edb_oid_debug(%lu)"));
|
||||
|
||||
const wxString dbgController::ms_cmdDebugPLPGSQLV1(
|
||||
wxT("SELECT plpgsql_oid_debug(%lu, %lu)"));
|
||||
const wxString dbgController::ms_cmdDebugPLPGSQLV2(
|
||||
wxT("SELECT plpgsql_oid_debug(%lu)"));
|
||||
|
||||
const wxString dbgController::ms_cmdAttachToPort(
|
||||
wxT("SELECT * FROM pldbg_attach_to_port(%s)"));
|
||||
const wxString dbgController::ms_cmdWaitForBreakpointV1(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
|
||||
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
|
||||
wxT("FROM pldbg_wait_for_breakpoint($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdWaitForBreakpointV2(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.func AS func, p.targetName AS targetName, \n")
|
||||
wxT(" p.linenumber AS linenumber,\n")
|
||||
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func) AS args\n")
|
||||
wxT("FROM pldbg_wait_for_breakpoint($1::INTEGER) p"));
|
||||
|
||||
const wxString dbgController::ms_cmdGetVars(
|
||||
wxT("SELECT\n")
|
||||
wxT(" name, varClass, value,\n")
|
||||
wxT(" pg_catalog.format_type(dtype, NULL) as dtype, isconst\n")
|
||||
wxT("FROM pldbg_get_variables(%s) ORDER BY varClass"));
|
||||
const wxString dbgController::ms_cmdGetStack(
|
||||
wxT("SELECT * FROM pldbg_get_stack(%s) ORDER BY level"));
|
||||
const wxString dbgController::ms_cmdGetBreakpoints(
|
||||
wxT("SELECT * FROM pldbg_get_breakpoints(%s)"));
|
||||
|
||||
const wxString dbgController::ms_cmdStepOverV1(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
|
||||
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
|
||||
wxT("FROM pldbg_step_over($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdStepOverV2(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.func, p.targetName, p.linenumber,\n")
|
||||
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func) AS args\n")
|
||||
wxT("FROM pldbg_step_over($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdStepIntoV1(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
|
||||
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
|
||||
wxT("FROM pldbg_step_into($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdStepIntoV2(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.func, p.targetName, p.linenumber,\n")
|
||||
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func) AS args\n")
|
||||
wxT("FROM pldbg_step_into($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdContinueV1(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
|
||||
wxT(" p.linenumber AS linenumber, pldbg_get_source($1::INTEGER, p.pkg, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
|
||||
wxT("FROM pldbg_continue($1::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdContinueV2(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.func, p.targetName, p.linenumber,\n")
|
||||
wxT(" pldbg_get_source($1::INTEGER, p.func) AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func) AS args\n")
|
||||
wxT("FROM pldbg_continue($1::INTEGER) p"));
|
||||
|
||||
const wxString dbgController::ms_cmdSetBreakpointV1(
|
||||
wxT("SELECT * FROM pldbg_set_breakpoint(%s,%s,%s,%d)"));
|
||||
const wxString dbgController::ms_cmdSetBreakpointV2(
|
||||
wxT("SELECT * FROM pldbg_set_breakpoint(%s,%s,%d)"));
|
||||
|
||||
const wxString dbgController::ms_cmdClearBreakpointV1(
|
||||
wxT("SELECT * FROM pldbg_drop_breakpoint(%s,%s,%s,%d)"));
|
||||
const wxString dbgController::ms_cmdClearBreakpointV2(
|
||||
wxT("SELECT * FROM pldbg_drop_breakpoint(%s,%s,%d)"));
|
||||
|
||||
const wxString dbgController::ms_cmdSelectFrameV1(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.pkg AS pkg, p.func AS func, p.targetName AS targetName,\n")
|
||||
wxT(" p.linenumber AS linenumber,\n")
|
||||
wxT(" CASE WHEN p.func <> 0 THEN pldbg_get_source($1::INTEGER, p.func, p.pkg) ELSE '<No source available>' END AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func AND s.pkg = p.pkg) AS args\n")
|
||||
wxT("FROM pldbg_select_frame($1::INTEGER, $2::INTEGER) p"));
|
||||
const wxString dbgController::ms_cmdSelectFrameV2(
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.func AS func, p.targetName AS targetName, p.linenumber AS linenumber,\n")
|
||||
wxT(" CASE WHEN p.func <> 0 THEN pldbg_get_source($1::INTEGER, p.func) ELSE '<No source available>' END AS src,\n")
|
||||
wxT(" (SELECT\n")
|
||||
wxT(" s.args\n")
|
||||
wxT(" FROM pldbg_get_stack($1::INTEGER) s\n")
|
||||
wxT(" WHERE s.func = p.func) AS args\n")
|
||||
wxT("FROM pldbg_select_frame($1::INTEGER, $2::INTEGER) p"));
|
||||
|
||||
const wxString dbgController::ms_cmdDepositValue(
|
||||
wxT("SELECT * FROM pldbg_deposit_value(%s,%s,%d,%s)"));
|
||||
const wxString dbgController::ms_cmdAbortTarget(
|
||||
wxT("SELECT * FROM pldbg_abort_target(%s)"));
|
||||
|
||||
const wxString dbgController::ms_cmdAddBreakpointEDB(
|
||||
wxT("SELECT * FROM pldbg_set_global_breakpoint(%s, %s, %s, -1, %s)"));
|
||||
const wxString dbgController::ms_cmdAddBreakpointPG(
|
||||
wxT("SELECT * FROM pldbg_set_global_breakpoint(%s, %s, -1, %s)"));
|
||||
|
||||
const wxString dbgController::ms_cmdCreateListener(
|
||||
wxT("SELECT * from pldbg_create_listener()"));
|
||||
const wxString dbgController::ms_cmdWaitForTarget(
|
||||
wxT("SELECT * FROM pldbg_wait_for_target(%s)"));
|
||||
|
||||
const wxString dbgController::ms_cmdIsBackendRunning(
|
||||
wxT("SELECT COUNT(*) FROM (SELECT pg_catalog.pg_stat_get_backend_idset() AS bid) AS s WHERE pg_catalog.pg_stat_get_backend_pid(s.bid) = '%d'::integer;"));
|
||||
|
||||
dbgController::dbgController(frmMain *main, pgObject *_obj, bool _directDebugging)
|
||||
: m_ver(DEBUGGER_UNKNOWN_API), m_sessionType(DBG_SESSION_TYPE_UNKNOWN),
|
||||
m_lineOffset(1), m_terminated(false), m_frm(NULL), m_dbgConn(NULL),
|
||||
m_dbgThread(NULL), m_execConnThread(NULL), m_model(NULL),
|
||||
m_isStopping(false)
|
||||
{
|
||||
// Create the connection for listening the debugger port and doing the
|
||||
// debugging operations.
|
||||
// i.e.
|
||||
// - step in, step over, continue, fetch stack-frames, fetch variables, etc.
|
||||
m_dbgConn = _obj->GetConnection()->Duplicate(
|
||||
wxT("pgAdmin Debugger - Global Listener"));
|
||||
|
||||
if (!m_dbgConn->IsAlive())
|
||||
{
|
||||
wxLogError(_("Couldn't create a connection for the debugger"));
|
||||
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
|
||||
throw (std::runtime_error("Couldn't create a connection for the debugger"));
|
||||
}
|
||||
|
||||
m_dbgConn->ExecuteVoid(wxT("SET client_min_messages TO error"));
|
||||
|
||||
if (!m_dbgConn->BackendMinimumVersion(9, 1))
|
||||
m_lineOffset = 0;
|
||||
|
||||
OID target;
|
||||
if (_obj->GetMetaType() == PGM_TRIGGER)
|
||||
{
|
||||
target = ((pgTrigger *)_obj)->GetFunctionOid();
|
||||
}
|
||||
else
|
||||
{
|
||||
target = _obj->GetOid();
|
||||
}
|
||||
|
||||
// Fetch the target information
|
||||
try
|
||||
{
|
||||
m_model = new dbgModel((Oid)target, m_dbgConn);
|
||||
}
|
||||
catch (const std::runtime_error &error)
|
||||
{
|
||||
// Catching the runtime error thrown by dbgTargetInfo, so that we can
|
||||
// delete the created connection object and then rethrow it
|
||||
m_dbgConn->Close();
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
|
||||
// Rethrow the error
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (m_model->GetTarget()->HasVariadic() &&
|
||||
m_model->GetTarget()->GetLanguage() == wxT("edbspl"))
|
||||
{
|
||||
wxLogError(
|
||||
_("An 'edbspl' target with a variadic argument is not supported by this version of pgAdmin debugger!"));
|
||||
|
||||
throw (std::runtime_error(
|
||||
"Unsupported target specified!"));
|
||||
}
|
||||
|
||||
// Fetch the debugger version
|
||||
wxString strCntProxyInfo = m_dbgConn->ExecuteScalar(
|
||||
wxT("SELECT COUNT(*) FROM pg_catalog.pg_proc p\n")
|
||||
wxT(" LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid\n")
|
||||
wxT("WHERE n.nspname = ANY(current_schemas(false))\n")
|
||||
wxT(" AND p.proname = 'pldbg_get_proxy_info'"), false);
|
||||
|
||||
if(!strCntProxyInfo.IsEmpty() && m_dbgConn->GetLastResultStatus() == PGRES_TUPLES_OK)
|
||||
{
|
||||
if (strCntProxyInfo == wxT("0"))
|
||||
{
|
||||
m_ver = DEBUGGER_V1_API;
|
||||
}
|
||||
wxString strVer = m_dbgConn->ExecuteScalar(
|
||||
wxT("SELECT proxyapiver FROM pldbg_get_proxy_info()"), false);
|
||||
|
||||
if (!strVer.IsEmpty() && m_dbgConn->GetLastResultStatus() == PGRES_TUPLES_OK)
|
||||
{
|
||||
long ver;
|
||||
strVer.ToLong(&ver);
|
||||
|
||||
switch (ver)
|
||||
{
|
||||
case DEBUGGER_V3_API:
|
||||
case DEBUGGER_V2_API:
|
||||
m_ver = (DebuggerApiVersion)ver;
|
||||
break;
|
||||
default:
|
||||
wxLogError(
|
||||
wxString::Format(
|
||||
_("pgAdmin does not support this version of the debugger plugin (v:%ld)"),
|
||||
ver));
|
||||
|
||||
m_dbgConn->Close();
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
|
||||
delete m_model;
|
||||
m_model = NULL;
|
||||
|
||||
throw (std::runtime_error(
|
||||
"Unsupported version of debugger plugin installed"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(
|
||||
wxString::Format(
|
||||
_("Couldn't determine the debugger plugin version.\n%s"),
|
||||
m_dbgConn->GetLastError().c_str()));
|
||||
|
||||
m_dbgConn->Close();
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
|
||||
delete m_model;
|
||||
m_model = NULL;
|
||||
|
||||
throw (std::runtime_error(
|
||||
"Couldn't determine the debugger version (function execution error)."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(
|
||||
wxString::Format(
|
||||
_("Couldn't determine the debugger plugin version.\n%s"),
|
||||
m_dbgConn->GetLastError().c_str()));
|
||||
|
||||
m_dbgConn->Close();
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
|
||||
delete m_model;
|
||||
m_model = NULL;
|
||||
|
||||
throw (std::runtime_error(
|
||||
"Couldn't determine the debugger version (function check)"));
|
||||
}
|
||||
|
||||
// Store debugging type
|
||||
if (_directDebugging)
|
||||
{
|
||||
m_sessionType = DBG_SESSION_TYPE_DIRECT;
|
||||
m_frm = new frmDebugger(
|
||||
main, this, wxString::Format(
|
||||
_("Debugger - %s"),
|
||||
m_model->GetTarget()->GetQualifiedName().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sessionType = DBG_SESSION_TYPE_INCONTEXT;
|
||||
m_frm = new frmDebugger(
|
||||
main, this, _("Global Debugger"));
|
||||
}
|
||||
|
||||
if (!Start())
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
|
||||
dbgController::~dbgController()
|
||||
{
|
||||
if (m_dbgConn)
|
||||
{
|
||||
m_dbgConn->Close();
|
||||
delete m_dbgConn;
|
||||
m_dbgConn = NULL;
|
||||
}
|
||||
|
||||
if (m_model)
|
||||
{
|
||||
delete m_model;
|
||||
m_model = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool dbgController::Start()
|
||||
{
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
m_frm->Show(true);
|
||||
|
||||
if(m_frm->GetTabWindow()->GetPageCount()) // Clearing message window, result grid, and re-setting the status message during the module re-exeuction.
|
||||
{
|
||||
m_frm->GetMessageWindow()->Clear();
|
||||
m_frm->GetResultWindow()->ClearGrid();
|
||||
m_frm->LaunchWaitingDialog(_("Initializing..."));
|
||||
}
|
||||
|
||||
if (!m_dbgConn->IsAlive())
|
||||
{
|
||||
if (!m_dbgConn->Reconnect())
|
||||
{
|
||||
// Unable to re-establish the connection for debugger
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread = new pgQueryThread(
|
||||
m_dbgConn, this, &(dbgController::NoticeHandler), this);
|
||||
m_dbgThread->SetEventOnCancellation(false);
|
||||
|
||||
if (m_dbgThread->Create() != wxTHREAD_NO_ERROR)
|
||||
{
|
||||
delete m_dbgThread;
|
||||
m_dbgThread = NULL;
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dbgThread->Run();
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
wxTheApp->Yield(true);
|
||||
|
||||
m_terminated = false;
|
||||
m_isStopping = false;
|
||||
|
||||
if (m_sessionType == DBG_SESSION_TYPE_DIRECT)
|
||||
{
|
||||
pgConn *conn = m_dbgConn->Duplicate(
|
||||
wxT("pgAdmin Debugger - Target Invoker"));
|
||||
|
||||
if (m_model->GetTarget()->RequireUserInput())
|
||||
{
|
||||
dlgDirectDbg dlg(m_frm, this, conn);
|
||||
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
{
|
||||
// Close the connection for the debugging listener
|
||||
m_dbgConn->Close();
|
||||
|
||||
// Close the connection for the debugging executor
|
||||
conn->Close();
|
||||
delete conn;
|
||||
conn = NULL;
|
||||
|
||||
// Declare that execution has been cancelled
|
||||
m_terminated = true;
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
m_frm->CloseProgressBar();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_execConnThread = new pgQueryThread(
|
||||
conn, this, &(dbgController::NoticeHandler), this);
|
||||
m_execConnThread->SetEventOnCancellation(false);
|
||||
|
||||
if (m_execConnThread->Create() != wxTHREAD_NO_ERROR)
|
||||
{
|
||||
delete m_execConnThread;
|
||||
m_execConnThread = NULL;
|
||||
|
||||
conn->Close();
|
||||
delete conn;
|
||||
conn = NULL;
|
||||
|
||||
Stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
return ExecuteTarget();
|
||||
}
|
||||
else if (m_sessionType == DBG_SESSION_TYPE_INCONTEXT)
|
||||
{
|
||||
// TODO:: Ask for the target session and different targets
|
||||
dbgBreakPointList &breakpoints = m_model->GetBreakPoints();
|
||||
|
||||
WX_CLEAR_ARRAY(breakpoints);
|
||||
|
||||
dbgTargetInfo *target = m_model->GetTarget();
|
||||
breakpoints.Append(new dbgBreakPoint(
|
||||
wxString::Format(wxT("%lu"), target->GetOid()),
|
||||
wxString::Format(wxT("%lu"), target->GetPkgOid())));
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(ms_cmdCreateListener, NULL, (long)RESULT_ID_LISTENER_CREATED);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool dbgController::ExecuteTarget()
|
||||
{
|
||||
dbgTargetInfo *target = m_model->GetTarget();
|
||||
wxString strDebugCmdPkgInitializer, strDebugCmdTarget;
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
if (target->GetLanguage() == wxT("edbspl"))
|
||||
{
|
||||
strDebugCmdPkgInitializer = wxString::Format(
|
||||
ms_cmdDebugSPLV1, target->GetPkgOid(), target->GetPkgInitOid());
|
||||
strDebugCmdTarget = wxString::Format(
|
||||
ms_cmdDebugSPLV1, target->GetPkgOid(), target->GetOid());
|
||||
}
|
||||
else
|
||||
{
|
||||
strDebugCmdPkgInitializer = wxString::Format(
|
||||
ms_cmdDebugPLPGSQLV1, target->GetPkgOid(), target->GetPkgInitOid());
|
||||
strDebugCmdTarget = wxString::Format(
|
||||
ms_cmdDebugPLPGSQLV1, target->GetPkgOid(), target->GetOid());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target->GetLanguage() == wxT("edbspl"))
|
||||
{
|
||||
strDebugCmdPkgInitializer = wxString::Format(
|
||||
ms_cmdDebugSPLV2, target->GetPkgInitOid());
|
||||
strDebugCmdTarget = wxString::Format(ms_cmdDebugSPLV2, target->GetOid());
|
||||
}
|
||||
else
|
||||
{
|
||||
strDebugCmdPkgInitializer = wxString::Format(
|
||||
ms_cmdDebugPLPGSQLV2, target->GetPkgInitOid());
|
||||
strDebugCmdTarget = wxString::Format(ms_cmdDebugPLPGSQLV2, target->GetOid());
|
||||
}
|
||||
}
|
||||
if (target->DebugPackageConstructor())
|
||||
{
|
||||
m_execConnThread->AddQuery(strDebugCmdPkgInitializer);
|
||||
}
|
||||
m_execConnThread->AddQuery(strDebugCmdTarget);
|
||||
|
||||
if (target->AddForExecution(m_execConnThread))
|
||||
{
|
||||
// Start the execution thread
|
||||
m_execConnThread->Run();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(_("Couldn't determine how to execute the selected target."));
|
||||
|
||||
Stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void dbgController::NoticeHandler(void *_arg, const char *_msg)
|
||||
{
|
||||
dbgController *controller = (dbgController *)_arg;
|
||||
|
||||
if (controller->m_terminated)
|
||||
return;
|
||||
|
||||
wxMBConv *conv = controller->m_dbgConn->GetConv();
|
||||
|
||||
wxString strMsg(_msg, *conv);
|
||||
|
||||
if (strMsg.EndsWith(wxT("\n")))
|
||||
{
|
||||
strMsg.RemoveLast();
|
||||
}
|
||||
|
||||
if (strMsg.Find(wxT("PLDBGBREAK")) != wxNOT_FOUND)
|
||||
{
|
||||
// NOTICE: PLDBGBREAK 1
|
||||
wxStringTokenizer tokens(strMsg, wxT(":\n"));
|
||||
|
||||
tokens.GetNextToken(); // NOTICE:
|
||||
tokens.GetNextToken(); // PLDBGBREAK
|
||||
|
||||
controller->m_model->GetPort() = tokens.GetNextToken(); // <port number>
|
||||
|
||||
wxCommandEvent btnEvt(wxEVT_COMMAND_BUTTON_CLICKED, MENU_ID_START_DEBUGGING);
|
||||
controller->AddPendingEvent(btnEvt);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxCommandEvent btnEvt(wxEVT_COMMAND_BUTTON_CLICKED, MENU_ID_NOTICE_RECEIVED);
|
||||
|
||||
btnEvt.SetString(strMsg);
|
||||
controller->AddPendingEvent(btnEvt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Debugging actions (called from the frmDebugger)
|
||||
void dbgController::ClearBreakpoint(int _lineNo)
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdClearBreakpointV1, m_model->GetSession().c_str(),
|
||||
m_model->GetDisplayedPackage().c_str(),
|
||||
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
|
||||
NULL, (long) RESULT_ID_NEW_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdClearBreakpointV2, m_model->GetSession().c_str(),
|
||||
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
|
||||
NULL, RESULT_ID_NEW_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dbgController::SetBreakpoint(int _lineNo)
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdSetBreakpointV1, m_model->GetSession().c_str(),
|
||||
m_model->GetDisplayedPackage().c_str(),
|
||||
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
|
||||
NULL, RESULT_ID_NEW_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdSetBreakpointV2, m_model->GetSession().c_str(),
|
||||
m_model->GetDisplayedFunction().c_str(), _lineNo + 1),
|
||||
NULL, RESULT_ID_NEW_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dbgController::Countinue()
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdContinueV1, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdContinueV2, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
|
||||
// Do not allow any action at this time
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::StepOver()
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(ms_cmdStepOverV1, params, RESULT_ID_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(ms_cmdStepOverV2, params, RESULT_ID_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
// Do not allow any action at this time
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::StepInto()
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(ms_cmdStepIntoV1, params, RESULT_ID_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(ms_cmdStepIntoV2, params, RESULT_ID_BREAKPOINT);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
// Do not allow any action at this time
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
|
||||
bool dbgController::Stop()
|
||||
{
|
||||
if (m_terminated)
|
||||
return true;
|
||||
|
||||
if (m_isStopping)
|
||||
return false;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
m_frm->LaunchWaitingDialog(_("Waiting for target stop execution..."));
|
||||
m_isStopping = true;
|
||||
|
||||
switch(m_sessionType)
|
||||
{
|
||||
case DBG_SESSION_TYPE_DIRECT:
|
||||
{
|
||||
// We must send the abort target command to the executor
|
||||
// session
|
||||
if (m_execConnThread)
|
||||
{
|
||||
if (m_execConnThread->IsRunning())
|
||||
{
|
||||
// Ask the direct debugging executor to not to handle any of the error or
|
||||
// result any more.
|
||||
m_execConnThread->CancelExecution();
|
||||
if (m_dbgConn->GetStatus() != CONNECTION_BAD && m_model->GetSession() != wxEmptyString)
|
||||
{
|
||||
// And then, we will ask the backend to abort the target, so that the
|
||||
// target backend will wait for any commands from debugging proxy.
|
||||
pgConn *conn = m_dbgThread->GetConn();
|
||||
pgSet *set = conn->ExecuteSet(
|
||||
wxString::Format(ms_cmdAbortTarget, m_model->GetSession().c_str()));
|
||||
|
||||
if (set)
|
||||
delete set;
|
||||
|
||||
}
|
||||
while (m_execConnThread && m_execConnThread->IsRunning())
|
||||
{
|
||||
wxTheApp->Yield(true);
|
||||
wxMilliSleep(3);
|
||||
}
|
||||
if (m_execConnThread)
|
||||
m_execConnThread->Wait();
|
||||
}
|
||||
if (m_execConnThread)
|
||||
{
|
||||
// We also need to deallocate the momory for the connection
|
||||
pgConn *conn = m_execConnThread->GetConn();
|
||||
|
||||
delete m_execConnThread;
|
||||
m_execConnThread = NULL;
|
||||
|
||||
conn->Close();
|
||||
delete conn;
|
||||
conn = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_dbgThread)
|
||||
{
|
||||
if (m_dbgThread->IsRunning())
|
||||
{
|
||||
m_dbgThread->CancelExecution();
|
||||
m_dbgThread->Wait();
|
||||
}
|
||||
|
||||
delete m_dbgThread;
|
||||
m_dbgThread = NULL;
|
||||
}
|
||||
|
||||
// Disconnect the debugger connection
|
||||
m_dbgConn->Close();
|
||||
m_frm->CloseProgressBar();
|
||||
m_terminated = true;
|
||||
m_isStopping = false;
|
||||
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void dbgController::DepositValue(const wxString &_name, const wxString &_val)
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdDepositValue, m_model->GetSession().c_str(),
|
||||
m_dbgConn->qtDbString(_name).c_str(), -1,
|
||||
m_dbgConn->qtDbString(_val).c_str()),
|
||||
NULL, RESULT_ID_DEPOSIT_VALUE);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
bool dbgController::SelectFrame(int _frameNo)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
if (m_terminated || m_isStopping)
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
ctlStackWindow *stackWin = m_frm->GetStackWindow();
|
||||
dbgStackFrame *frame = (dbgStackFrame *)stackWin->GetClientObject(_frameNo);
|
||||
|
||||
if (!frame || frame->GetFunction() == wxT("0"))
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
wxMessageBox(
|
||||
_("Cannot fetch information about the anonymous block!"),
|
||||
_("Invalid Stack"), wxICON_ERROR | wxOK);
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString strLevel = frame->GetLevel();
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &strLevel));
|
||||
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdSelectFrameV1, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdSelectFrameV2, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dbgController::UpdateBreakpoints()
|
||||
{
|
||||
if (m_terminated || m_isStopping)
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdGetBreakpoints, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_GET_BREAKPOINTS);
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
// Closing Debugger
|
||||
bool dbgController::CloseDebugger()
|
||||
{
|
||||
if (Stop())
|
||||
{
|
||||
if (m_dbgConn)
|
||||
{
|
||||
delete m_dbgConn;
|
||||
|
||||
m_dbgConn = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgTargetInfo *dbgController::GetTargetInfo()
|
||||
{
|
||||
return m_model->GetTarget();
|
||||
}
|
||||
974
debugger/dbgEvents.cpp
Normal file
974
debugger/dbgEvents.cpp
Normal file
|
|
@ -0,0 +1,974 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dbgEvents.cpp - debugger controller events
|
||||
// - This files contains the functions related to the event handling of the
|
||||
// debugger controller.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
#include "ctl/ctlAuiNotebook.h"
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgQueryThread.h"
|
||||
#include "db/pgQueryResultEvent.h"
|
||||
#include "schema/pgObject.h"
|
||||
#include "debugger/dbgConst.h"
|
||||
#include "debugger/dbgBreakPoint.h"
|
||||
#include "debugger/dbgController.h"
|
||||
#include "debugger/dbgModel.h"
|
||||
#include "debugger/ctlStackWindow.h"
|
||||
#include "debugger/ctlMessageWindow.h"
|
||||
#include "debugger/ctlTabWindow.h"
|
||||
#include "debugger/frmDebugger.h"
|
||||
#include "debugger/dlgDirectDbg.h"
|
||||
#include "utils/pgDefs.h"
|
||||
|
||||
#define LOG_DBG_MUTEX_LOCKING 0
|
||||
|
||||
#if LOG_DBG_MUTEX_LOCKING
|
||||
|
||||
#define LOCKMUTEX(m) \
|
||||
wxPrintf(wxT("Mutex locking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
||||
m.Lock(); \
|
||||
wxPrintf(wxT("Mutex locked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
||||
|
||||
#define UNLOCKMUTEX(m) \
|
||||
wxPrintf(wxT("Mutex unlocking @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__); \
|
||||
m.Unlock(); \
|
||||
wxPrintf(wxT("Mutex unlocked @ %s:%d\n"), wxString::FromAscii(__WXFUNCTION__).c_str(), __LINE__);
|
||||
|
||||
#else
|
||||
|
||||
#define LOCKMUTEX(m) \
|
||||
m.Lock();
|
||||
|
||||
#define UNLOCKMUTEX(m) \
|
||||
m.Unlock();
|
||||
|
||||
#endif
|
||||
|
||||
BEGIN_EVENT_TABLE(dbgController, wxEvtHandler)
|
||||
EVT_BUTTON(MENU_ID_NOTICE_RECEIVED, dbgController::OnNoticeReceived)
|
||||
EVT_BUTTON(MENU_ID_START_DEBUGGING, dbgController::OnStartDebugging)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_DIRECT_TARGET_COMPLETE, dbgController::ResultTargetComplete)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_ATTACH_TO_PORT, dbgController::ResultPortAttach)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_LISTENER_CREATED, dbgController::ResultListenerCreated)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_TARGET_READY, dbgController::ResultTargetReady)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_DEL_BREAKPOINT, dbgController::ResultDeletedBreakpoint)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_BREAKPOINT, dbgController::ResultBreakpoint)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT, dbgController::ResultNewBreakpoint)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_NEW_BREAKPOINT_WAIT, dbgController::ResultNewBreakpointWait)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_GET_VARS, dbgController::ResultVarList)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_GET_STACK, dbgController::ResultStack)
|
||||
EVT_PGQUERYRESULT(RESULT_ID_GET_BREAKPOINTS, dbgController::ResultBreakpoints)
|
||||
|
||||
EVT_PGQUERYRESULT(RESULT_ID_DEPOSIT_VALUE, dbgController::ResultDepositValue)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
|
||||
// Event functions
|
||||
void dbgController::OnNoticeReceived(wxCommandEvent &_ev)
|
||||
{
|
||||
m_frm->GetMessageWindow()->AddMessage(_ev.GetString());
|
||||
m_frm->GetTabWindow()->SelectTab(ID_MSG_PAGE);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::OnStartDebugging(wxCommandEvent &_ev)
|
||||
{
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdAttachToPort, m_model->GetPort().c_str()),
|
||||
NULL, RESULT_ID_ATTACH_TO_PORT);
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultTargetComplete(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
m_terminated = true;
|
||||
m_frm->SetStatusText(_("Debugging Completed"));
|
||||
m_frm->CloseProgressBar();
|
||||
|
||||
wxTheApp->Yield(true);
|
||||
|
||||
if (m_execConnThread)
|
||||
{
|
||||
switch(qry->ReturnCode())
|
||||
{
|
||||
case pgQueryResultEvent::PGQ_CONN_LOST:
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Execution of the debugging target terminated due to connection loss.\n"),
|
||||
m_execConnThread->GetId()));
|
||||
break;
|
||||
// This is very unlikely, unless we made mistake creating the query
|
||||
case pgQueryResultEvent::PGQ_STRING_INVALID:
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Execution of the debugging target terminated due to empty query string.\n"),
|
||||
m_execConnThread->GetId()));
|
||||
break;
|
||||
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
|
||||
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
|
||||
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
|
||||
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Execution of the debugging target terminated due to execution error (%d).\n"),
|
||||
m_execConnThread->GetId(), qry->ReturnCode()));
|
||||
break;
|
||||
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
|
||||
m_frm->SetStatusText(_("Debugging Cancelled"));
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Execution of the debugging target cancelled.\n"),
|
||||
m_execConnThread->GetId()));
|
||||
break;
|
||||
case pgQueryResultEvent::PGQ_RESULT_ERROR:
|
||||
{
|
||||
wxString strErr = qry->GetErrorMessage();
|
||||
|
||||
m_frm->SetStatusText(_("Execution completed with an error."));
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
|
||||
m_execConnThread->GetId(), strErr.c_str()));
|
||||
|
||||
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
||||
m_frm->GetMessageWindow()->SetFocus();
|
||||
}
|
||||
|
||||
break;
|
||||
case PGRES_FATAL_ERROR:
|
||||
case PGRES_NONFATAL_ERROR:
|
||||
case PGRES_BAD_RESPONSE:
|
||||
{
|
||||
wxString strErr = qry->GetErrorMessage();
|
||||
m_frm->SetStatusText(_("Execution completed with an error."));
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Function/Procedure terminated with an error.\n%s"),
|
||||
m_execConnThread->GetId(), strErr.c_str()));
|
||||
|
||||
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
||||
m_frm->GetMessageWindow()->SetFocus();
|
||||
}
|
||||
|
||||
break;
|
||||
case PGRES_TUPLES_OK:
|
||||
{
|
||||
m_frm->SetStatusText(_("Execution completed."));
|
||||
pgSet *set = qry->ResultSet();
|
||||
m_frm->GetMessageWindow()->AddMessage(set->GetCommandStatus());
|
||||
|
||||
m_frm->GetResultWindow()->FillResult(set);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
m_frm->SetStatusText(_("Execution completed."));
|
||||
wxLogInfo(
|
||||
wxString::Format(
|
||||
_("Debugger(%ld): Execution of the debugging function/procedure completed\n"),
|
||||
m_execConnThread->GetId()));
|
||||
|
||||
m_frm->GetMessageWindow()->AddMessage(qry->GetMessage());
|
||||
m_frm->GetMessageWindow()->SetFocus();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_execConnThread->IsRunning())
|
||||
{
|
||||
m_execConnThread->CancelExecution();
|
||||
m_execConnThread->Wait();
|
||||
}
|
||||
|
||||
pgConn *execConn = m_execConnThread->GetConn();
|
||||
delete m_execConnThread;
|
||||
|
||||
m_execConnThread = NULL;
|
||||
execConn->Close();
|
||||
delete execConn;
|
||||
}
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
pgQueryThread *oldDebugThread = m_dbgThread;
|
||||
m_dbgThread = NULL;
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
if (oldDebugThread)
|
||||
{
|
||||
oldDebugThread->CancelExecution();
|
||||
oldDebugThread->Wait();
|
||||
|
||||
delete oldDebugThread;
|
||||
}
|
||||
|
||||
// We won't keep the connection open, we will open it only when required
|
||||
if (m_dbgConn)
|
||||
m_dbgConn->Close();
|
||||
|
||||
if (m_frm)
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultPortAttach(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Error attaching the proxy session.")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (qry->ReturnCode() != PGRES_TUPLES_OK)
|
||||
{
|
||||
Stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
|
||||
wxString strSession = qry->ResultSet()->GetVal(0);
|
||||
m_model->GetSession() = strSession;
|
||||
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &strSession));
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
qry->Release();
|
||||
|
||||
// Now create any breakpoints that the user created in last execution. We
|
||||
// start by asking the server to resolve the breakpoint name into an OID (or,
|
||||
// a pair of OID's if the target is defined in a package). As each
|
||||
// (targetInof) result arrives, we add a break-point at the resulting OID.
|
||||
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
|
||||
|
||||
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
|
||||
node = node->GetNext())
|
||||
{
|
||||
dbgBreakPoint *breakpoint = node->GetData();
|
||||
/*
|
||||
* In EnterpriseDB versions <= 9.1 the
|
||||
* pldbg_set_global_breakpoint function took five arguments,
|
||||
* the 2nd argument being the package's OID, if any. Starting
|
||||
* with 9.2, the package OID argument is gone, and the function
|
||||
* takes four arguments like the community version has always
|
||||
* done.
|
||||
*/
|
||||
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
|
||||
{
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdSetBreakpointV1, m_model->GetSession().c_str(),
|
||||
breakpoint->GetPackageOid().c_str(),
|
||||
breakpoint->GetFunctionOid().c_str(),
|
||||
breakpoint->GetLineNo()),
|
||||
NULL, RESULT_ID_NEW_BREAKPOINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdSetBreakpointV2, m_model->GetSession().c_str(),
|
||||
breakpoint->GetFunctionOid().c_str(),
|
||||
breakpoint->GetLineNo()),
|
||||
NULL, RESULT_ID_NEW_BREAKPOINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
bool dbgController::HandleQuery(pgBatchQuery *_qry, const wxString &_err)
|
||||
{
|
||||
if (!_qry)
|
||||
return false;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
// It is possible that we found one error, while running the previous query
|
||||
// and, called Stop function from it, because of an error found.
|
||||
// That may have released the debugger thread
|
||||
//
|
||||
// In this case, we should exit this flow gracefully instead showing any
|
||||
// error
|
||||
if (!m_dbgThread || !m_dbgThread->IsRunning())
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(_qry->ReturnCode())
|
||||
{
|
||||
case pgQueryResultEvent::PGQ_CONN_LOST:
|
||||
// This is very unlikely, unless we made mistake creating the query
|
||||
case pgQueryResultEvent::PGQ_STRING_INVALID:
|
||||
case PGRES_EMPTY_QUERY:
|
||||
case pgQueryResultEvent::PGQ_ERROR_PREPARE_CALLABLE:
|
||||
case pgQueryResultEvent::PGQ_ERROR_EXECUTE_CALLABLE:
|
||||
case pgQueryResultEvent::PGQ_ERROR_SEND_QUERY:
|
||||
case pgQueryResultEvent::PGQ_ERROR_CONSUME_INPUT:
|
||||
case pgQueryResultEvent::PGQ_RESULT_ERROR:
|
||||
case PGRES_BAD_RESPONSE:
|
||||
case PGRES_NONFATAL_ERROR:
|
||||
case PGRES_FATAL_ERROR:
|
||||
break;
|
||||
|
||||
// This is unlikely, we do not generate an event, when execution is
|
||||
// cancelled
|
||||
case pgQueryResultEvent::PGQ_EXECUTION_CANCELLED:
|
||||
break;
|
||||
case PGRES_COMMAND_OK:
|
||||
case PGRES_TUPLES_OK:
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
return true;
|
||||
// Hmm - where did it come from?
|
||||
// We never call a function, which results into these results
|
||||
// Anyway - we will return true as we have the result
|
||||
case PGRES_COPY_IN:
|
||||
case PGRES_COPY_OUT:
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
wxTheApp->Yield(true);
|
||||
|
||||
if (!m_dbgConn->IsAlive())
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
wxMessageBox(
|
||||
_("Connection to the database server lost, debugging cannot continue!"),
|
||||
_("Connection Lost"), wxICON_ERROR | wxOK);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString strErr;
|
||||
if (!_err.IsEmpty())
|
||||
strErr = _err + wxT("\n\n");
|
||||
strErr += _qry->GetErrorMessage();
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
if (_qry->ReturnCode() == PGRES_FATAL_ERROR)
|
||||
{
|
||||
// We will start start listening for new in-context session, if the
|
||||
// current session is closed. On which, the query/target was
|
||||
// running.
|
||||
//
|
||||
// This allows us to have the same behaviour as the old one.
|
||||
//
|
||||
if (m_sessionType == DBG_SESSION_TYPE_INCONTEXT && m_currTargetPid != wxT(""))
|
||||
{
|
||||
// Let's check if the target pid has stopped or exited after
|
||||
// successful debugging, let's move on and wait for the next
|
||||
// target to hit.
|
||||
wxString isTargetRunning = m_dbgConn->ExecuteScalar(wxString::Format(ms_cmdIsBackendRunning, m_currTargetPid.c_str()));
|
||||
|
||||
if (isTargetRunning == wxT("0"))
|
||||
{
|
||||
// Reset the current backend-pid of the target
|
||||
m_currTargetPid = wxT("");
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_TARGET_READY);
|
||||
|
||||
m_frm->LaunchWaitingDialog();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(_("The calling connection was closed or lost."), _("Connection Lost"), wxICON_ERROR | wxOK);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxMessageBox(strErr, _("Execution Error"), wxICON_ERROR | wxOK);
|
||||
}
|
||||
|
||||
wxLogQuietError(strErr);
|
||||
}
|
||||
|
||||
Stop();
|
||||
m_terminated = true;
|
||||
|
||||
m_frm->SetStatusText(_("Debugging aborting..."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultBreakpoint(pgQueryResultEvent &_ev)
|
||||
{
|
||||
if (m_frm)
|
||||
m_frm->CloseProgressBar();
|
||||
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, wxEmptyString))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
wxString func,
|
||||
pkg = wxT("0");
|
||||
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
if (set->HasColumn(wxT("pkg")))
|
||||
{
|
||||
pkg = set->GetVal(wxT("pkg"));
|
||||
}
|
||||
func = set->GetVal(wxT("func"));
|
||||
|
||||
m_model->GetFocusedPackage() = pkg;
|
||||
m_model->GetFocusedFunction() = func;
|
||||
|
||||
int lineNo = (int)(set->GetLong(wxT("linenumber")) - 1);
|
||||
|
||||
if (lineNo < 0)
|
||||
lineNo = -1;
|
||||
else
|
||||
lineNo -= m_lineOffset;
|
||||
m_model->GetCurrLineNo() = lineNo;
|
||||
|
||||
wxString strSource = set->GetVal(wxT("src"));
|
||||
|
||||
if (strSource.IsEmpty())
|
||||
strSource = _("<source not available>");
|
||||
|
||||
dbgCachedStack src(pkg, func, set->GetVal(wxT("targetname")),
|
||||
set->GetVal(wxT("args")), strSource);
|
||||
|
||||
m_model->AddSource(func, src);
|
||||
m_frm->DisplaySource(src);
|
||||
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdGetStack, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_GET_STACK);
|
||||
|
||||
m_frm->EnableToolsAndMenus(true);
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultVarList(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Error fetching variables.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
ctlVarWindow *paramWin = NULL, *pkgVarWin = NULL, *varWin = NULL;
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
while (!set->Eof())
|
||||
{
|
||||
switch((char)(set->GetVal(wxT("varclass"))[0]))
|
||||
{
|
||||
case 'A':
|
||||
{
|
||||
if (paramWin == NULL)
|
||||
paramWin = m_frm->GetParamWindow(true);
|
||||
|
||||
paramWin->AddVar(
|
||||
set->GetVal(wxT("name")),
|
||||
set->GetVal(wxT("value")),
|
||||
set->GetVal(wxT("dtype")),
|
||||
set->GetBool(wxT("isconst")));
|
||||
|
||||
break;
|
||||
}
|
||||
case 'P':
|
||||
{
|
||||
if (pkgVarWin == NULL)
|
||||
pkgVarWin = m_frm->GetPkgVarWindow(true);
|
||||
|
||||
pkgVarWin->AddVar(
|
||||
set->GetVal(wxT("name")),
|
||||
set->GetVal(wxT("value")),
|
||||
set->GetVal(wxT("dtype")),
|
||||
set->GetBool(wxT("isconst")));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (varWin == NULL)
|
||||
varWin = m_frm->GetVarWindow(true);
|
||||
|
||||
varWin->AddVar(
|
||||
set->GetVal(wxT("name")),
|
||||
set->GetVal(wxT("value")),
|
||||
set->GetVal(wxT("dtype")),
|
||||
set->GetBool(wxT("isconst")));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
set->MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultStack(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Error fetching the call stack.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
dbgStackFrameList stacks;
|
||||
ctlStackWindow *stackWin = m_frm->GetStackWindow();
|
||||
|
||||
// Fetched the stack, now fetch the break-points
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdGetBreakpoints, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_GET_BREAKPOINTS);
|
||||
|
||||
stackWin->ClearStack();
|
||||
|
||||
int selected = 0,
|
||||
levelCol = set->ColNumber(wxT("level")),
|
||||
pkgCol = set->HasColumn(wxT("pkg")) ? set->ColNumber(wxT("level")) : -1,
|
||||
funCol = set->ColNumber(wxT("func")),
|
||||
targetCol = set->ColNumber(wxT("targetname")),
|
||||
argsCol = set->ColNumber(wxT("args")),
|
||||
lineCol = set->ColNumber(wxT("linenumber"));
|
||||
|
||||
while (!set->Eof())
|
||||
{
|
||||
// The result set contains one tuple per frame:
|
||||
// package, function, linenumber, args
|
||||
dbgStackFrame *frame = new dbgStackFrame(
|
||||
set->GetVal(levelCol),
|
||||
pkgCol != -1 ? set->GetVal(wxT("pkg")) : wxT("0"),
|
||||
set->GetVal(funCol),
|
||||
wxString::Format(
|
||||
wxT("%s(%s)@%s"),
|
||||
set->GetVal(targetCol).c_str(),
|
||||
set->GetVal(argsCol).c_str(),
|
||||
set->GetVal(lineCol).c_str()));
|
||||
|
||||
// Select this one in the stack window
|
||||
if (frame->GetFunction() == m_model->GetDisplayedFunction() &&
|
||||
frame->GetPackage() == m_model->GetDisplayedPackage())
|
||||
{
|
||||
selected = set->CurrentPos() - 1;
|
||||
}
|
||||
|
||||
stacks.Append(frame);
|
||||
set->MoveNext();
|
||||
}
|
||||
stackWin->SetStack(stacks, selected);
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultBreakpoints(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Error fetching breakpoints.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
int pkgCol = -1, funcCol = set->ColNumber(wxT("func")),
|
||||
lineCol = set->ColNumber(wxT("linenumber"));
|
||||
|
||||
if (set->HasColumn(wxT("pkg")))
|
||||
{
|
||||
pkgCol = set->ColNumber(wxT("pkg"));
|
||||
}
|
||||
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_GET_VARS);
|
||||
|
||||
m_frm->ClearBreakpointMarkers();
|
||||
dbgBreakPointList &breakpoints = m_model->GetBreakPoints();
|
||||
WX_CLEAR_ARRAY(breakpoints);
|
||||
|
||||
while (!set->Eof())
|
||||
{
|
||||
// The result set contains one tuple per breakpoint:
|
||||
// pkg, func, linenumber, target
|
||||
// or,
|
||||
// func, linenumber, target
|
||||
wxString pkg = (pkgCol == -1) ? wxT("0") : set->GetVal(pkgCol);
|
||||
wxString func = set->GetVal(funcCol);
|
||||
int lineNumber = (int)set->GetLong(lineCol);
|
||||
|
||||
// Save this break-points in break-point list
|
||||
breakpoints.Append(new dbgBreakPoint(func, pkg, lineNumber));
|
||||
|
||||
// Mark the break-point in the viewer
|
||||
if (pkg == m_model->GetDisplayedPackage() &&
|
||||
func == m_model->GetDisplayedFunction())
|
||||
{
|
||||
m_frm->MarkBreakpoint(lineNumber - 1);
|
||||
}
|
||||
set->MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultNewBreakpoint(pgQueryResultEvent &_ev)
|
||||
{
|
||||
// We will wait for the last new break-point, which will be handled in
|
||||
// dbgController::ResultNewBreakpointWait function
|
||||
if (!HandleQuery(_ev.GetQuery(), _("Could not create the breakpoint.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
// Release the result-set
|
||||
_ev.GetQuery()->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultNewBreakpointWait(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Could not create the breakpoint.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = _ev.GetQuery()->ResultSet();
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdWaitForTarget, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_TARGET_READY);
|
||||
|
||||
m_frm->LaunchWaitingDialog();
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultDeletedBreakpoint(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Could not drop the breakpoint.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
if (set->GetBool(0))
|
||||
{
|
||||
m_frm->SetStatusText(_("Breakpoint dropped"));
|
||||
}
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ResultDepositValue()
|
||||
//
|
||||
// This event handler is called when the proxy completes a 'deposit' operation
|
||||
// (in response to an earlier call to pldbg_deposit_value()).
|
||||
//
|
||||
// We schedule a refresh of our variable window(s) for the next idle period.
|
||||
//
|
||||
void dbgController::ResultDepositValue(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry, _("Could not deposit the new value.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
if (set->GetBool(0))
|
||||
{
|
||||
m_frm->SetStatusText(_("Value changed"));
|
||||
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(ms_cmdGetVars, m_model->GetSession().c_str()),
|
||||
NULL, RESULT_ID_GET_VARS);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogError(_("Could not deposit the new value."));
|
||||
}
|
||||
}
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultListenerCreated(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (!HandleQuery(qry,
|
||||
_("Could not create the proxy listener for in-process debugging.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
if (qry->ReturnCode() == PGRES_TUPLES_OK)
|
||||
{
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
// We now have a global listener and a session handle. Grab the session
|
||||
// handle (we'll need it for just about everything else).
|
||||
m_model->GetSession() = set->GetVal(0);
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
|
||||
// Now create any global breakpoints that the user requested. We start
|
||||
// by asking the server to resolve the breakpoint name into an OID (or,
|
||||
// a pair of OID's if the target is defined in a package). As each
|
||||
// (targetInof) result arrives, we add a break-point at the resulting
|
||||
// OID.
|
||||
unsigned int idx = 1;
|
||||
long resultId;
|
||||
|
||||
dbgBreakPointList breakpoints = m_model->GetBreakPoints();
|
||||
for (dbgBreakPointList::Node *node = breakpoints.GetFirst(); node;
|
||||
node = node->GetNext(), ++idx)
|
||||
{
|
||||
dbgBreakPoint *breakpoint = node->GetData();
|
||||
|
||||
if(idx < breakpoints.GetCount())
|
||||
{
|
||||
resultId = RESULT_ID_NEW_BREAKPOINT;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultId = RESULT_ID_NEW_BREAKPOINT_WAIT;
|
||||
}
|
||||
|
||||
/*
|
||||
* In EnterpriseDB versions <= 9.1 the
|
||||
* pldbg_set_global_breakpoint function took five arguments,
|
||||
* the 2nd argument being the package's OID, if any. Starting
|
||||
* with 9.2, the package OID argument is gone, and the function
|
||||
* takes four arguments like the community version has always
|
||||
* done.
|
||||
*/
|
||||
if (m_dbgConn->GetIsEdb() && !m_dbgConn->BackendMinimumVersion(9, 2))
|
||||
{
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdAddBreakpointEDB, m_model->GetSession().c_str(),
|
||||
breakpoint->GetPackageOid().c_str(),
|
||||
breakpoint->GetFunctionOid().c_str(),
|
||||
m_model->GetTargetPid().c_str()),
|
||||
NULL, resultId);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(
|
||||
wxString::Format(
|
||||
ms_cmdAddBreakpointPG, m_model->GetSession().c_str(),
|
||||
breakpoint->GetFunctionOid().c_str(),
|
||||
m_model->GetTargetPid().c_str()),
|
||||
NULL, resultId);
|
||||
}
|
||||
}
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dbgController::ResultTargetReady(pgQueryResultEvent &_ev)
|
||||
{
|
||||
pgBatchQuery *qry = _ev.GetQuery();
|
||||
|
||||
if (m_frm)
|
||||
{
|
||||
m_frm->CloseProgressBar();
|
||||
m_frm->EnableToolsAndMenus(false);
|
||||
}
|
||||
|
||||
if (!HandleQuery(qry,
|
||||
_("Error fetching target information.")))
|
||||
return;
|
||||
|
||||
LOCKMUTEX(m_dbgThreadLock);
|
||||
// Do not bother to process the result, if the debugger thread is not
|
||||
// running or not exists
|
||||
if (m_dbgThread && m_dbgThread->IsRunning())
|
||||
{
|
||||
bool goAhead = false;
|
||||
goAhead = (qry->ReturnCode() == PGRES_TUPLES_OK);
|
||||
pgSet *set = qry->ResultSet();
|
||||
|
||||
// Save the current running target pid
|
||||
m_currTargetPid = set->GetVal(0);
|
||||
|
||||
// Next line release the actual result-set
|
||||
set = NULL;
|
||||
|
||||
// Release the result-set
|
||||
qry->Release();
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
|
||||
pgParamsArray *params = new pgParamsArray;
|
||||
|
||||
params->Add(new pgParam(PGOID_TYPE_INT4, &(m_model->GetSession())));
|
||||
if (m_ver <= DEBUGGER_V2_API)
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV1, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dbgThread->AddQuery(ms_cmdWaitForBreakpointV2, params, RESULT_ID_BREAKPOINT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UNLOCKMUTEX(m_dbgThreadLock);
|
||||
}
|
||||
}
|
||||
63
debugger/dbgModel.cpp
Normal file
63
debugger/dbgModel.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dbgModel.cpp - debugger model
|
||||
// - It contains the data and information related the debugging session
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
#include "db/pgConn.h"
|
||||
#include "db/pgQueryThread.h"
|
||||
#include "db/pgQueryResultEvent.h"
|
||||
#include "debugger/dbgModel.h"
|
||||
|
||||
|
||||
dbgModel::dbgModel(Oid _target, pgConn *_conn)
|
||||
: m_target(NULL), m_currLineNo(-1), m_targetPid(wxT("NULL"))
|
||||
{
|
||||
m_target = new dbgTargetInfo(_target, _conn);
|
||||
}
|
||||
|
||||
|
||||
bool dbgModel::GetSource(const wxString &_funcOid, dbgCachedStack *_cached)
|
||||
{
|
||||
dbgSourceHash::iterator match = m_sourceMap.find(_funcOid);
|
||||
|
||||
if (match == m_sourceMap.end())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
if (_cached)
|
||||
{
|
||||
*_cached = match->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dbgModel::ClearCachedSource()
|
||||
{
|
||||
m_sourceMap.clear();
|
||||
|
||||
// Put a dummy entry for invalid function OID to the cache. This is
|
||||
// displayed at least for inline code blocks, as we currently have no way
|
||||
// to fetch the source for those
|
||||
m_sourceMap[wxT("0")] = dbgCachedStack(wxT("0"), wxT("0"), wxT(""), wxT(""), _("<source not available>"));
|
||||
}
|
||||
|
||||
|
||||
void dbgModel::AddSource(const wxString &_funcOid, const dbgCachedStack &_source)
|
||||
{
|
||||
m_sourceMap[_funcOid] = _source;
|
||||
}
|
||||
647
debugger/dbgTargetInfo.cpp
Normal file
647
debugger/dbgTargetInfo.cpp
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// dbgTargetInfo.cpp - debugger
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin3.h"
|
||||
#include "utils/pgDefs.h"
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "debugger/dbgTargetInfo.h"
|
||||
#include "debugger/dbgConst.h"
|
||||
#include "utils/misc.h"
|
||||
#include "utils/pgfeatures.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// dbgTargetInfo constructor
|
||||
//
|
||||
// This class implements a container that holds information necessary to invoke
|
||||
// a debugger target (a function or procedure).
|
||||
//
|
||||
// When the constructor is called, it sends a query to the server to retreive:
|
||||
// the OID of the target,
|
||||
// the name of the target,
|
||||
// the name of the schema in which the target is defined
|
||||
// the name of the language in which the target is defined
|
||||
// the number of arguments expected by the target
|
||||
// the argument names
|
||||
// the argument types
|
||||
// the argument modes (IN, OUT, or INOUT)
|
||||
// the target type (function or procedure)
|
||||
//
|
||||
// This class offers a number of (inline) member functions that you can call
|
||||
// to extract the above information after it's been queried from the server.
|
||||
|
||||
dbgTargetInfo::dbgTargetInfo(Oid _target, pgConn *_conn)
|
||||
: m_args(NULL), m_inputParamCnt(0), m_hasVariadic(false)
|
||||
{
|
||||
wxMBConv *conv = _conn->GetConv();
|
||||
wxString targetQuery =
|
||||
wxT("SELECT\n")
|
||||
wxT(" p.proname AS name, l.lanname, p.proretset, p.prorettype, y.typname AS rettype,\n")
|
||||
wxT(" CASE WHEN proallargtypes IS NOT NULL THEN\n")
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT\n")
|
||||
wxT(" pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
|
||||
wxT(" FROM\n")
|
||||
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
|
||||
wxT(" p.proallargtypes, 1)) AS s(i)), ',')\n")
|
||||
wxT(" ELSE\n")
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT\n")
|
||||
wxT(" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
|
||||
wxT(" FROM\n")
|
||||
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(\n")
|
||||
wxT(" p.proargtypes, 1)) AS s(i)), ',')\n")
|
||||
wxT(" END AS proargtypenames,\n")
|
||||
wxT(" CASE WHEN proallargtypes IS NOT NULL THEN\n")
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT proallargtypes[s.i] FROM\n")
|
||||
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(proallargtypes, 1)) s(i)), ',')\n")
|
||||
wxT(" ELSE\n")
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT proargtypes[s.i] FROM\n")
|
||||
wxT(" pg_catalog.generate_series(0, pg_catalog.array_upper(proargtypes, 1)) s(i)), ',')\n")
|
||||
wxT(" END AS proargtypes,\n")
|
||||
wxT(" pg_catalog.array_to_string(p.proargnames, ',') AS proargnames,\n")
|
||||
wxT(" pg_catalog.array_to_string(proargmodes, ',') AS proargmodes,\n");
|
||||
|
||||
if (_conn->GetIsEdb())
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" CASE WHEN n.nspparent <> 0 THEN n.oid ELSE 0 END AS pkg,\n")
|
||||
wxT(" CASE WHEN n.nspparent <> 0 THEN n.nspname ELSE '' END AS pkgname,\n")
|
||||
wxT(" CASE WHEN n.nspparent <> 0 THEN (SELECT oid FROM pg_proc WHERE pronamespace=n.oid AND proname='cons') ELSE 0 END AS pkgconsoid,\n")
|
||||
wxT(" CASE WHEN n.nspparent <> 0 THEN g.oid ELSE n.oid END AS schema,\n")
|
||||
wxT(" CASE WHEN n.nspparent <> 0 THEN g.nspname ELSE n.nspname END AS schemaname,\n")
|
||||
wxT(" NOT (l.lanname = 'edbspl' AND protype = '1') AS isfunc,\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" 0 AS pkg,\n")
|
||||
wxT(" '' AS pkgname,\n")
|
||||
wxT(" 0 AS pkgconsoid,\n")
|
||||
wxT(" n.oid AS schema,\n")
|
||||
wxT(" n.nspname AS schemaname,\n")
|
||||
wxT(" true AS isfunc,\n");
|
||||
}
|
||||
if (_conn->BackendMinimumVersion(8, 4))
|
||||
{
|
||||
targetQuery += wxT(" pg_catalog.pg_get_function_identity_arguments(p.oid) AS signature,");
|
||||
}
|
||||
else if (_conn->BackendMinimumVersion(8, 1))
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" CASE\n")
|
||||
wxT(" WHEN proallargtypes IS NOT NULL THEN pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT\n")
|
||||
wxT(" CASE\n")
|
||||
wxT(" WHEN p.proargmodes[s.i] = 'i' THEN ''\n")
|
||||
wxT(" WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n")
|
||||
wxT(" WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n")
|
||||
wxT(" WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n")
|
||||
wxT(" END ||\n")
|
||||
wxT(" CASE WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n")
|
||||
wxT(" ELSE p.proargnames[s.i] || ' '\n")
|
||||
wxT(" END ||\n")
|
||||
wxT(" pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n")
|
||||
wxT(" FROM\n")
|
||||
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n")
|
||||
wxT(" WHERE p.proargmodes[s.i] != 't'\n")
|
||||
wxT(" ), ', ')\n")
|
||||
wxT(" ELSE\n")
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT\n")
|
||||
wxT(" CASE\n")
|
||||
wxT(" WHEN COALESCE(p.proargnames[s.i+1], '') = '' THEN ''\n")
|
||||
wxT(" ELSE p.proargnames[s.i+1] || ' '\n")
|
||||
wxT(" END ||\n")
|
||||
wxT(" pg_catalog.format_type(p.proargtypes[s.i], NULL)\n")
|
||||
wxT(" FROM\n")
|
||||
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargtypes, 1)) AS s(i)\n")
|
||||
wxT(" ), ', ')\n")
|
||||
wxT(" END AS signature,\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
targetQuery += wxT(" '' AS signature,");
|
||||
}
|
||||
|
||||
if (_conn->HasFeature(FEATURE_FUNCTION_DEFAULTS))
|
||||
{
|
||||
// EnterpriseDB 8.3R2
|
||||
if(!_conn->BackendMinimumVersion(8, 4))
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" pg_catalog.array_to_string(ARRAY(\n")
|
||||
wxT(" SELECT\n")
|
||||
wxT(" CASE WHEN p.proargdefvals[x.j] != '-' THEN\n")
|
||||
wxT(" pg_catalog.pg_get_expr(p.proargdefvals[x.j], 'pg_catalog.pg_class'::regclass, true)\n")
|
||||
wxT(" ELSE '-' END\n")
|
||||
wxT(" FROM\n")
|
||||
wxT(" pg_catalog.generate_series(1, pg_catalog.array_upper(p.proargdefvals, 1)) AS x(j)\n")
|
||||
wxT(" ), ',') AS proargdefaults,\n")
|
||||
wxT(" CASE WHEN p.proargdefvals IS NULL THEN '0'\n")
|
||||
wxT(" ELSE pg_catalog.array_upper(p.proargdefvals, 1)::text END AS pronargdefaults\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" pg_catalog.pg_get_expr(p.proargdefaults, 'pg_catalog.pg_class'::regclass, false) AS proargdefaults,\n")
|
||||
wxT(" p.pronargdefaults\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" '' AS proargdefaults, 0 AS pronargdefaults\n");
|
||||
}
|
||||
targetQuery +=
|
||||
wxT("FROM\n")
|
||||
wxT(" pg_catalog.pg_proc p\n")
|
||||
wxT(" LEFT JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid\n")
|
||||
wxT(" LEFT JOIN pg_catalog.pg_language l ON p.prolang = l.oid\n")
|
||||
wxT(" LEFT JOIN pg_catalog.pg_type y ON p.prorettype = y.oid\n");
|
||||
if(_conn->GetIsEdb())
|
||||
{
|
||||
targetQuery +=
|
||||
wxT(" LEFT JOIN pg_catalog.pg_namespace g ON n.nspparent = g.oid\n");
|
||||
}
|
||||
targetQuery +=
|
||||
wxString::Format(wxT("WHERE p.oid = %ld"), (long)_target);
|
||||
|
||||
pgSet *set = _conn->ExecuteSet(targetQuery);
|
||||
|
||||
if (conv == NULL)
|
||||
{
|
||||
conv = &wxConvLibc;
|
||||
}
|
||||
|
||||
if (!set || _conn->GetLastResultStatus() != PGRES_TUPLES_OK)
|
||||
{
|
||||
if (set)
|
||||
delete set;
|
||||
wxLogError(_("Could not fetch information about the debugger target.\n") +
|
||||
_conn->GetLastError());
|
||||
|
||||
throw (std::runtime_error(
|
||||
(const char *)(_conn->GetLastError().c_str())));
|
||||
}
|
||||
|
||||
if (set->NumRows() == 0)
|
||||
{
|
||||
delete set;
|
||||
|
||||
wxLogError(_("Can't find the debugging target"));
|
||||
throw (std::runtime_error("Can't find target!"));
|
||||
}
|
||||
|
||||
m_oid = _target;
|
||||
m_name = set->GetVal(wxT("name"));
|
||||
m_schema = set->GetVal(wxT("schemaname"));
|
||||
m_package = set->GetVal(wxT("pkgname"));
|
||||
m_language = set->GetVal(wxT("lanname"));
|
||||
m_returnType = set->GetVal(wxT("rettype"));
|
||||
m_funcSignature = set->GetVal(wxT("signature"));
|
||||
m_isFunction = set->GetBool(wxT("isfunc"));
|
||||
m_returnsSet = set->GetBool(wxT("proretset"));
|
||||
m_pkgOid = set->GetOid(wxT("pkg"));
|
||||
m_pkgInitOid = set->GetOid(wxT("pkgconsoid"));
|
||||
m_schemaOid = set->GetOid(wxT("schema"));
|
||||
m_fqName = qtIdent(m_schema) + wxT(".") +
|
||||
(m_pkgOid == 0 ? wxT("") : (qtIdent(m_package) + wxT("."))) + qtIdent(m_name);
|
||||
|
||||
wxArrayString argModes, argNames, argTypes, argTypeOids, argDefVals,
|
||||
argBaseTypes;
|
||||
|
||||
// Fetch Argument Modes (if available)
|
||||
if (!set->IsNull(set->ColNumber(wxT("proargmodes"))))
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = set->GetVal(wxT("proargmodes"));
|
||||
|
||||
if (!tmp.IsEmpty())
|
||||
getArrayFromCommaSeparatedList(tmp, argModes);
|
||||
}
|
||||
// Fetch Argument Names (if available)
|
||||
if (!set->IsNull(set->ColNumber(wxT("proargnames"))))
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = set->GetVal(wxT("proargnames"));
|
||||
|
||||
if (!tmp.IsEmpty())
|
||||
getArrayFromCommaSeparatedList(tmp, argNames);
|
||||
}
|
||||
// Fetch Argument Type-Names (if available)
|
||||
if (!set->IsNull(set->ColNumber(wxT("proargtypenames"))))
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = set->GetVal(wxT("proargtypenames"));
|
||||
|
||||
if (!tmp.IsEmpty())
|
||||
getArrayFromCommaSeparatedList(tmp, argTypes);
|
||||
}
|
||||
// Fetch Argument Type-Names (if available)
|
||||
if (!set->IsNull(set->ColNumber(wxT("proargtypes"))))
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = set->GetVal(wxT("proargtypes"));
|
||||
if (!tmp.IsEmpty())
|
||||
getArrayFromCommaSeparatedList(tmp, argTypeOids);
|
||||
}
|
||||
|
||||
size_t nArgDefs = (size_t)set->GetLong(wxT("pronargdefaults"));
|
||||
// Fetch Argument Default Values (if available)
|
||||
if (!set->IsNull(set->ColNumber(wxT("proargdefaults"))) && nArgDefs != 0)
|
||||
{
|
||||
wxString tmp;
|
||||
tmp = set->GetVal(wxT("proargdefaults"));
|
||||
|
||||
if (!tmp.IsEmpty())
|
||||
getArrayFromCommaSeparatedList(tmp, argDefVals);
|
||||
}
|
||||
|
||||
wxString argName, argDefVal;
|
||||
short argMode;
|
||||
Oid argTypeOid;
|
||||
size_t argCnt = argTypes.Count();
|
||||
|
||||
// This function/procedure does not take any arguments
|
||||
if (argCnt == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
size_t idx = 0;
|
||||
m_args = new pgDbgArgs();
|
||||
|
||||
for (; idx < argCnt; idx++)
|
||||
{
|
||||
argTypeOid = (Oid)strtoul(argTypeOids[idx].mb_str(wxConvUTF8), 0, 10);
|
||||
argDefVal = wxEmptyString;
|
||||
|
||||
argName = wxEmptyString;
|
||||
if (idx < argNames.Count())
|
||||
argName = argNames[idx];
|
||||
|
||||
if (argName.IsEmpty())
|
||||
argName.Printf( wxT( "dbgParam%d" ), (idx + 1));
|
||||
|
||||
if (idx < argModes.Count())
|
||||
{
|
||||
wxString tmp = argModes[idx];
|
||||
switch ((char)(tmp.c_str())[0])
|
||||
{
|
||||
case 'i':
|
||||
argMode = pgParam::PG_PARAM_IN;
|
||||
m_inputParamCnt++;
|
||||
break;
|
||||
case 'b':
|
||||
m_inputParamCnt++;
|
||||
argMode = pgParam::PG_PARAM_INOUT;
|
||||
break;
|
||||
case 'o':
|
||||
argMode = pgParam::PG_PARAM_OUT;
|
||||
break;
|
||||
case 'v':
|
||||
m_inputParamCnt++;
|
||||
argMode = pgParam::PG_PARAM_VARIADIC;
|
||||
m_hasVariadic = true;
|
||||
break;
|
||||
case 't':
|
||||
continue;
|
||||
default:
|
||||
m_inputParamCnt++;
|
||||
argMode = pgParam::PG_PARAM_IN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inputParamCnt++;
|
||||
argMode = pgParam::PG_PARAM_IN;
|
||||
}
|
||||
|
||||
// In EDBAS 90, if an SPL-function has both an OUT-parameter
|
||||
// and a return value (which is not possible on PostgreSQL otherwise),
|
||||
// the return value is transformed into an extra OUT-parameter
|
||||
// named "_retval_"
|
||||
if (argName == wxT("_retval_") && _conn->EdbMinimumVersion(9, 0))
|
||||
{
|
||||
// this will be the return type for this object
|
||||
m_returnType = argTypes[idx];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_args->Add(new dbgArgInfo(argName, argTypes[idx], argTypeOid, argMode));
|
||||
}
|
||||
|
||||
if (m_args->GetCount() == 0)
|
||||
{
|
||||
delete m_args;
|
||||
m_args = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nArgDefs != 0)
|
||||
{
|
||||
argCnt = m_args->GetCount();
|
||||
|
||||
// Set the default as the value for the argument
|
||||
for (idx = argCnt - 1;; idx--)
|
||||
{
|
||||
dbgArgInfo *arg = (dbgArgInfo *)((*m_args)[idx]);
|
||||
|
||||
if (arg->GetMode() == pgParam::PG_PARAM_INOUT ||
|
||||
arg->GetMode() == pgParam::PG_PARAM_IN)
|
||||
{
|
||||
nArgDefs--;
|
||||
|
||||
if (argDefVals[nArgDefs] != wxT("-"))
|
||||
{
|
||||
arg->SetDefault(argDefVals[nArgDefs]);
|
||||
}
|
||||
|
||||
if (nArgDefs == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// operator[]
|
||||
//
|
||||
// This operator function makes it easy to index into the m_args array
|
||||
// using concise syntax.
|
||||
dbgArgInfo *dbgTargetInfo::operator[](int index)
|
||||
{
|
||||
if (m_args == NULL)
|
||||
return (dbgArgInfo *)NULL;
|
||||
|
||||
if (index < 0 || index >= (int)m_args->GetCount())
|
||||
return (dbgArgInfo *)NULL;
|
||||
|
||||
return (dbgArgInfo *)((*m_args)[index]);
|
||||
}
|
||||
|
||||
dbgArgInfo::dbgArgInfo(const wxString &_name, const wxString &_type, Oid _typeOid,
|
||||
short _mode)
|
||||
: m_name(_name), m_type(_type), m_typeOid(_typeOid), m_mode(_mode),
|
||||
m_hasDefault(false), m_useDefault(false), m_null(false)
|
||||
{
|
||||
if (!_type.EndsWith(wxT("[]"), &m_baseType))
|
||||
{
|
||||
m_baseType = wxEmptyString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pgParam *dbgArgInfo::GetParam(wxMBConv *_conv)
|
||||
{
|
||||
return new pgParam(m_typeOid,
|
||||
(m_null ? (wxString *)NULL : &m_val),
|
||||
_conv, m_mode);
|
||||
}
|
||||
|
||||
bool dbgTargetInfo::AddForExecution(pgQueryThread *_thread)
|
||||
{
|
||||
wxASSERT(_thread != NULL);
|
||||
|
||||
if (_thread == NULL)
|
||||
return false;
|
||||
|
||||
pgConn *conn = _thread->GetConn();
|
||||
|
||||
pgParamsArray *params = NULL;
|
||||
wxString strQuery;
|
||||
bool useCallable = false;
|
||||
|
||||
// Basically - we can call the function/target three ways:
|
||||
// 1. If it is a edbspl procedure, we can use callable statement
|
||||
// 2. If the database server is of type EnterpriseDB, and
|
||||
// function/procedure is type 'edbspl', we should use the anonymous
|
||||
// function block for:
|
||||
// a. Version < 9.0
|
||||
// b. Package function/procedure
|
||||
// 3. Otherwise, we should use the simple function call (except using EXEC
|
||||
// for the procedure in 'edbspl' instead of using SELECT)
|
||||
if (_thread->SupportCallableStatement() &&
|
||||
m_language == wxT("edbspl") &&
|
||||
!m_isFunction)
|
||||
{
|
||||
useCallable = true;
|
||||
strQuery = wxT("CALL ") + m_fqName + wxT("(");
|
||||
|
||||
if (m_args)
|
||||
{
|
||||
params = new pgParamsArray();
|
||||
wxMBConv *conv = conn->GetConv();
|
||||
|
||||
for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
|
||||
{
|
||||
params->Add(((*m_args)[idx])->GetParam(conv));
|
||||
|
||||
if (idx != 0)
|
||||
strQuery += wxT(", ");
|
||||
strQuery += wxString::Format(wxT("$%d::"), idx + 1) +
|
||||
((*m_args)[idx])->GetTypeName();
|
||||
}
|
||||
}
|
||||
strQuery += wxT(")");
|
||||
}
|
||||
else if (conn->GetIsEdb() && !conn->BackendMinimumVersion(9, 3))
|
||||
{
|
||||
wxString strDeclare, strStatement, strResult;
|
||||
bool useAnonymousBlock = false;
|
||||
|
||||
if (m_language == wxT("edbspl"))
|
||||
{
|
||||
useAnonymousBlock = true;
|
||||
if (!m_isFunction)
|
||||
{
|
||||
strStatement = wxT("\tEXEC ") + m_fqName;
|
||||
}
|
||||
else if (m_returnType == wxT("void") || m_returnsSet ||
|
||||
!conn->BackendMinimumVersion(8, 4))
|
||||
{
|
||||
strStatement = wxT("\tPERFORM ") + m_fqName;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString resultVar = wxT("v_retVal");
|
||||
strStatement = wxT("\t") + resultVar + wxT(" := ") + m_fqName;
|
||||
strDeclare.Append(wxT("\t"))
|
||||
.Append(resultVar)
|
||||
.Append(wxT(" "))
|
||||
.Append(m_returnType)
|
||||
.Append(wxT(";\n"));
|
||||
strResult = wxT("\tDBMS_OUTPUT.PUT_LINE(E'\\n\\nResult:\\n--------\\n' || ") +
|
||||
resultVar +
|
||||
wxT("::text || E'\\n\\nNOTE: This is the result generated during the function execution by the debugger.\\n');\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_returnType == wxT("record"))
|
||||
{
|
||||
strStatement = wxT("\tSELECT ") + m_fqName;
|
||||
}
|
||||
else
|
||||
{
|
||||
strStatement = wxT("\tSELECT * FROM ") + m_fqName;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_args && m_args->GetCount() > 0)
|
||||
{
|
||||
strStatement.Append(wxT("("));
|
||||
|
||||
for(int idx = 0, firstProcessed = false; idx < (int)m_args->GetCount(); idx++)
|
||||
{
|
||||
dbgArgInfo *arg = (*m_args)[idx];
|
||||
|
||||
if (!conn->EdbMinimumVersion(8, 4) &&
|
||||
arg->GetMode() == pgParam::PG_PARAM_OUT &&
|
||||
(!m_isFunction || m_language == wxT("edbspl")))
|
||||
{
|
||||
if (firstProcessed)
|
||||
strStatement.Append(wxT(", "));
|
||||
firstProcessed = true;
|
||||
|
||||
strStatement.Append(wxT("NULL::")).Append(arg->GetTypeName());
|
||||
}
|
||||
else if (conn->EdbMinimumVersion(8, 4) && useAnonymousBlock &&
|
||||
(arg->GetMode() == pgParam::PG_PARAM_OUT ||
|
||||
arg->GetMode() == pgParam::PG_PARAM_INOUT))
|
||||
{
|
||||
wxString strParam = wxString::Format(wxT("p_param%d"), idx);
|
||||
|
||||
strDeclare.Append(wxT("\t"))
|
||||
.Append(strParam)
|
||||
.Append(wxT(" "))
|
||||
.Append(arg->GetTypeName());
|
||||
|
||||
if (arg->GetMode() == pgParam::PG_PARAM_INOUT)
|
||||
{
|
||||
strDeclare.Append(wxT(" := "))
|
||||
.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
|
||||
.Append(wxT("::"))
|
||||
.Append(arg->GetTypeName());
|
||||
}
|
||||
strDeclare.Append(wxT(";\n"));
|
||||
|
||||
if (firstProcessed)
|
||||
strStatement.Append(wxT(", "));
|
||||
firstProcessed = true;
|
||||
|
||||
strStatement.Append(strParam);
|
||||
}
|
||||
else if (arg->GetMode() != pgParam::PG_PARAM_OUT)
|
||||
{
|
||||
if (firstProcessed)
|
||||
strStatement.Append(wxT(", "));
|
||||
firstProcessed = true;
|
||||
|
||||
if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
|
||||
strStatement += wxT("VARIADIC ");
|
||||
|
||||
strStatement
|
||||
.Append(arg->Null() ? wxT("NULL") : conn->qtDbString(arg->Value()))
|
||||
.Append(wxT("::"))
|
||||
.Append(arg->GetTypeName());
|
||||
}
|
||||
}
|
||||
strStatement.Append(wxT(")"));
|
||||
strQuery = strStatement;
|
||||
}
|
||||
else if (!m_isFunction && m_language == wxT("edbspl"))
|
||||
{
|
||||
strQuery = strStatement;
|
||||
}
|
||||
else
|
||||
{
|
||||
strQuery = strStatement.Append(wxT("()"));
|
||||
}
|
||||
if (useAnonymousBlock)
|
||||
{
|
||||
strQuery = wxT("DECLARE\n") +
|
||||
strDeclare + wxT("BEGIN\n") + strStatement + wxT(";\n") + strResult + wxT("END;");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_isFunction)
|
||||
{
|
||||
strQuery = wxT("EXEC ") + m_fqName + wxT("(");
|
||||
}
|
||||
else if (m_returnType == wxT("record"))
|
||||
{
|
||||
strQuery = wxT("SELECT ") + m_fqName + wxT("(");
|
||||
}
|
||||
else
|
||||
{
|
||||
strQuery = wxT("SELECT * FROM ") + m_fqName + wxT("(");
|
||||
}
|
||||
|
||||
if (m_args)
|
||||
{
|
||||
params = new pgParamsArray();
|
||||
wxMBConv *conv = conn->GetConv();
|
||||
unsigned int noInParams = 0;
|
||||
|
||||
for(int idx = 0; idx < (int)m_args->GetCount(); idx++)
|
||||
{
|
||||
dbgArgInfo *arg = (*m_args)[idx];
|
||||
|
||||
if (arg->GetMode() != pgParam::PG_PARAM_OUT)
|
||||
{
|
||||
params->Add(arg->GetParam(conv));
|
||||
|
||||
if (noInParams != 0)
|
||||
strQuery += wxT(", ");
|
||||
|
||||
if (arg->GetMode() == pgParam::PG_PARAM_VARIADIC)
|
||||
strQuery += wxT("VARIADIC ");
|
||||
|
||||
noInParams++;
|
||||
strQuery += wxString::Format(wxT("$%d::"), noInParams) +
|
||||
((*m_args)[idx])->GetTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The function may not have IN/IN OUT/VARIADIC arguments, but only
|
||||
* OUT one(s).
|
||||
*/
|
||||
if (params->GetCount() == 0)
|
||||
{
|
||||
delete params;
|
||||
params = NULL;
|
||||
}
|
||||
}
|
||||
strQuery += wxT(")");
|
||||
}
|
||||
|
||||
_thread->AddQuery(strQuery, params, RESULT_ID_DIRECT_TARGET_COMPLETE, NULL, useCallable);
|
||||
|
||||
return true;
|
||||
}
|
||||
273
debugger/debugger.cpp
Normal file
273
debugger/debugger.cpp
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin III - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// debugger.cpp - Debugger factories
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// wxWindows headers
|
||||
#include <wx/wx.h>
|
||||
|
||||
// App headers
|
||||
#include "pgAdmin3.h"
|
||||
#include "frm/frmMain.h"
|
||||
#include "debugger/debugger.h"
|
||||
#include "debugger/dbgController.h"
|
||||
#include "schema/pgFunction.h"
|
||||
#include "schema/pgTrigger.h"
|
||||
#include "schema/edbPackageFunction.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// Debugger factory
|
||||
///////////////////////////////////////////////////
|
||||
debuggerFactory::debuggerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
|
||||
{
|
||||
mnu->Append(id, _("&Debug"), _("Debug the selected object"));
|
||||
}
|
||||
|
||||
wxWindow *debuggerFactory::StartDialog(frmMain *form, pgObject *obj)
|
||||
{
|
||||
// Check here to make sure the function still exists before proceeding.
|
||||
// There is still a very small window in which it might be dropped, but
|
||||
// that will be handled by a cache lookup failure error in the database
|
||||
// We also make sure the function name doesn't contain a : as that will
|
||||
// sent the debugger API nuts.
|
||||
pgSet *res = obj->GetConnection()->ExecuteSet(wxT("SELECT proname FROM pg_proc WHERE oid = ") + NumToStr((long)obj->GetOid()));
|
||||
|
||||
if (res->NumRows() != 1)
|
||||
{
|
||||
wxLogError(_("The selected function could not be found."));
|
||||
ctlTree *browser = form->GetBrowser();
|
||||
wxTreeItemId item = browser->GetSelection();
|
||||
if (obj == browser->GetObject(item))
|
||||
{
|
||||
form->Refresh(obj);
|
||||
}
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
|
||||
if (res->GetVal(wxT("proname")).Contains(wxT(":")))
|
||||
{
|
||||
wxLogError(_("Functions with a colon in the name cannot be debugged."));
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
try
|
||||
{
|
||||
new dbgController(form, obj, true);
|
||||
}
|
||||
catch (const std::runtime_error &)
|
||||
{
|
||||
// just ignore this errors, we already logged them in native messages to
|
||||
// the end-user
|
||||
}
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
|
||||
bool debuggerFactory::CheckEnable(pgObject *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// Can't debug catalog objects.
|
||||
if (obj->GetSchema() && obj->GetSchema()->GetMetaType() == PGM_CATALOG)
|
||||
return false;
|
||||
|
||||
// Must be a super user or object owner to create breakpoints of any kind.
|
||||
if (!obj->GetServer() || !(obj->GetServer()->GetSuperUser() || obj->GetServer()->GetUsername() == obj->GetOwner()))
|
||||
return false;
|
||||
|
||||
// EnterpriseDB 8.3 or earlier does not support debugging by the non-superuser
|
||||
if (!obj->GetServer()->GetSuperUser() && !obj->GetConnection()->EdbMinimumVersion(8, 4) && obj->GetConnection()->GetIsEdb())
|
||||
return false;
|
||||
|
||||
if (!obj->IsCollection())
|
||||
{
|
||||
switch (obj->GetMetaType())
|
||||
{
|
||||
case PGM_FUNCTION:
|
||||
{
|
||||
pgFunction *func = (pgFunction *)obj;
|
||||
|
||||
// If this is an EDB wrapped function, no debugging allowed
|
||||
if (obj->GetConnection()->GetIsEdb() && func->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
|
||||
return false;
|
||||
|
||||
if (func->GetReturnType() != wxT("trigger") &&
|
||||
func->GetReturnType() != wxT("\"trigger\""))
|
||||
{
|
||||
if (func->GetLanguage() == wxT("plpgsql") &&
|
||||
obj->GetDatabase()->CanDebugPlpgsql())
|
||||
return true;
|
||||
else if (func->GetLanguage() == wxT("edbspl") &&
|
||||
obj->GetDatabase()->CanDebugEdbspl())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EDB_PACKAGEFUNCTION:
|
||||
if (obj->GetDatabase()->GetConnection()->EdbMinimumVersion(8, 2) &&
|
||||
obj->GetDatabase()->CanDebugEdbspl() &&
|
||||
obj->GetName() != wxT("cons") &&
|
||||
((edbPackageFunction *)obj)->GetSource() != wxEmptyString &&
|
||||
(!((edbPackageFunction *)obj)->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$"))))
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// Breakpoint factory
|
||||
///////////////////////////////////////////////////
|
||||
breakpointFactory::breakpointFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : actionFactory(list)
|
||||
{
|
||||
mnu->Append(id, _("&Set breakpoint"), _("Set a breakpoint on the selected object"));
|
||||
}
|
||||
|
||||
wxWindow *breakpointFactory::StartDialog(frmMain *form, pgObject *obj)
|
||||
{
|
||||
wxString dbgOid;
|
||||
if (obj->GetMetaType() == PGM_TRIGGER)
|
||||
dbgOid = NumToStr((long)((pgTrigger *)obj)->GetFunctionOid());
|
||||
else
|
||||
dbgOid = NumToStr((long)obj->GetOid());
|
||||
|
||||
// Check here to make sure the function still exists before proceeding.
|
||||
// There is still a very small window in which it might be dropped, but
|
||||
// we should be able to handle most cases here without having to do this
|
||||
// deep down in query threads.
|
||||
// We also make sure the function name doesn't contain a : as that will
|
||||
// sent the debugger API nuts.
|
||||
pgSet *res = obj->GetConnection()->ExecuteSet(
|
||||
wxT("SELECT count(*) AS count, proname FROM pg_proc WHERE oid = ") + dbgOid + wxT(" GROUP BY proname"));
|
||||
if (res->GetVal(wxT("proname")).Contains(wxT(":")))
|
||||
{
|
||||
wxLogError(_("Functions with a colon in the name cannot be debugged."));
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
|
||||
if (res->GetLong(wxT("count")) != 1)
|
||||
{
|
||||
wxLogError(_("The selected function could not be found."));
|
||||
|
||||
ctlTree *browser = form->GetBrowser();
|
||||
wxTreeItemId item = browser->GetSelection();
|
||||
if (obj == browser->GetObject(item))
|
||||
{
|
||||
pgCollection *coll = browser->GetParentCollection(obj->GetId());
|
||||
browser->DeleteChildren(coll->GetId());
|
||||
coll->ShowTreeDetail(browser);
|
||||
}
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
new dbgController(form, obj, false);
|
||||
}
|
||||
catch (const std::runtime_error &)
|
||||
{
|
||||
// just ignore this errors, we already logged them in native messages to
|
||||
// the end-user
|
||||
}
|
||||
|
||||
delete res;
|
||||
return (wxWindow *)NULL;
|
||||
}
|
||||
|
||||
bool breakpointFactory::CheckEnable(pgObject *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// Can't debug catalog objects.
|
||||
if (obj->GetSchema() && obj->GetSchema()->GetMetaType() == PGM_CATALOG)
|
||||
return false;
|
||||
|
||||
// Must be a super user to create breakpoints of any kind.
|
||||
if (!obj->GetServer() || !obj->GetServer()->GetSuperUser())
|
||||
return false;
|
||||
|
||||
if (!obj->IsCollection())
|
||||
{
|
||||
switch (obj->GetMetaType())
|
||||
{
|
||||
case PGM_FUNCTION:
|
||||
{
|
||||
pgFunction *func = (pgFunction *)obj;
|
||||
|
||||
// If this is an EDB wrapped function, no debugging allowed
|
||||
if (obj->GetConnection()->GetIsEdb() &&
|
||||
func->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
|
||||
return false;
|
||||
|
||||
if (func->GetLanguage() == wxT("plpgsql") &&
|
||||
obj->GetDatabase()->CanDebugPlpgsql())
|
||||
return true;
|
||||
else if (func->GetLanguage() == wxT("edbspl") &&
|
||||
obj->GetDatabase()->CanDebugEdbspl())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EDB_PACKAGEFUNCTION:
|
||||
if (obj->GetDatabase()->GetConnection()->EdbMinimumVersion(8, 2) &&
|
||||
obj->GetDatabase()->CanDebugEdbspl() &&
|
||||
obj->GetName() != wxT("cons") &&
|
||||
((edbPackageFunction *)obj)->GetSource() != wxEmptyString &&
|
||||
(!((edbPackageFunction *)obj)->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$"))))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case PGM_TRIGGER:
|
||||
{
|
||||
pgTrigger *trig = (pgTrigger *)obj;
|
||||
|
||||
// If this is an EDB wrapped function, no debugging allowed
|
||||
if (obj->GetConnection()->GetIsEdb() &&
|
||||
trig->GetSource().Trim(false).StartsWith(wxT("$__EDBwrapped__$")))
|
||||
return false;
|
||||
|
||||
if (trig->GetLanguage() == wxT("plpgsql") &&
|
||||
obj->GetDatabase()->CanDebugPlpgsql())
|
||||
return true;
|
||||
else if (trig->GetLanguage() == wxT("edbspl") &&
|
||||
obj->GetDatabase()->CanDebugEdbspl())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue