pgadmin3/frm/frmEditGrid.cpp
lsv 2734a746a9 Migration to wxWidgets 3.1.5
Выполнен переход на новые библтотеки. Устранены проблемы совместимости.
Множество мелких правок.
2021-05-21 11:09:20 +05:00

3386 lines
82 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmEditGrid.cpp - Edit Grid Box
//
//////////////////////////////////////////////////////////////////////////
// wxWindows headers
#include <wx/wx.h>
#include <wx/grid.h>
// App headers
#include "pgAdmin3.h"
#include "utils/pgDefs.h"
#include "frm/frmMain.h"
#include "frm/menu.h"
#include "db/pgQueryThread.h"
#include <wx/generic/gridctrl.h>
#include <wx/clipbrd.h>
#include "frm/frmAbout.h"
#include "frm/frmEditGrid.h"
#include "ctl/ctlMenuToolbar.h"
#include "dlg/dlgEditGridOptions.h"
#include "frm/frmHint.h"
#include "schema/pgCatalogObject.h"
#include "schema/pgTable.h"
#include "schema/pgForeignTable.h"
#include "schema/pgView.h"
#include "schema/gpExtTable.h"
#include "schema/pgPartition.h"
// wxAUI
#include <wx/aui/aui.h>
// Icons
#include "images/viewdata.pngc"
#include "images/storedata.pngc"
#include "images/readdata.pngc"
#include "images/delete.pngc"
#include "images/edit_undo.pngc"
#include "images/sortfilter.pngc"
#include "images/help.pngc"
#include "images/clip_copy.pngc"
#include "images/clip_paste.pngc"
#define CTRLID_LIMITCOMBO 4226
BEGIN_EVENT_TABLE(frmEditGrid, pgFrame)
EVT_ERASE_BACKGROUND( frmEditGrid::OnEraseBackground)
EVT_SIZE( frmEditGrid::OnSize)
EVT_MENU(MNU_REFRESH, frmEditGrid::OnRefresh)
EVT_MENU(MNU_DELETE, frmEditGrid::OnDelete)
EVT_MENU(MNU_SAVE, frmEditGrid::OnSave)
EVT_MENU(MNU_INCLUDEFILTER, frmEditGrid::OnIncludeFilter)
EVT_MENU(MNU_EXCLUDEFILTER, frmEditGrid::OnExcludeFilter)
EVT_MENU(MNU_REMOVEFILTERS, frmEditGrid::OnRemoveFilters)
EVT_MENU(MNU_ASCSORT, frmEditGrid::OnAscSort)
EVT_MENU(MNU_DESCSORT, frmEditGrid::OnDescSort)
EVT_MENU(MNU_REMOVESORT, frmEditGrid::OnRemoveSort)
EVT_MENU(MNU_UNDO, frmEditGrid::OnUndo)
EVT_MENU(MNU_OPTIONS, frmEditGrid::OnOptions)
EVT_MENU(MNU_HELP, frmEditGrid::OnHelp)
EVT_MENU(MNU_CONTENTS, frmEditGrid::OnContents)
EVT_MENU(MNU_COPY, frmEditGrid::OnCopy)
EVT_MENU(MNU_PASTE, frmEditGrid::OnPaste)
EVT_MENU(MNU_LIMITBAR, frmEditGrid::OnToggleLimitBar)
EVT_MENU(MNU_TOOLBAR, frmEditGrid::OnToggleToolBar)
EVT_MENU(MNU_SCRATCHPAD, frmEditGrid::OnToggleScratchPad)
EVT_MENU(MNU_DEFAULTVIEW, frmEditGrid::OnDefaultView)
EVT_MENU(MNU_CLOSE, frmEditGrid::OnClose)
EVT_CLOSE( frmEditGrid::OnCloseWindow)
EVT_KEY_DOWN( frmEditGrid::OnKey)
EVT_GRID_RANGE_SELECT( frmEditGrid::OnGridSelectCells)
EVT_GRID_SELECT_CELL( frmEditGrid::OnCellChange)
EVT_GRID_EDITOR_SHOWN( frmEditGrid::OnEditorShown)
EVT_GRID_EDITOR_HIDDEN( frmEditGrid::OnEditorHidden)
EVT_GRID_CELL_RIGHT_CLICK( frmEditGrid::OnCellRightClick)
EVT_GRID_LABEL_RIGHT_CLICK( frmEditGrid::OnLabelRightClick)
EVT_AUI_PANE_BUTTON( frmEditGrid::OnAuiUpdate)
END_EVENT_TABLE()
frmEditGrid::frmEditGrid(frmMain *form, const wxString &_title, pgConn *_conn, pgSchemaObject *obj, bool pkAscending)
: pgFrame(NULL, _title)
{
closing = false;
SetIcon(*viewdata_png_ico);
SetFont(settings->GetSystemFont());
dlgName = wxT("frmEditGrid");
RestorePosition(-1, -1, 600, 500, 300, 350);
connection = _conn;
mainForm = form;
thread = 0;
relkind = 0;
limit = 0;
relid = (Oid)obj->GetOid();
editorCell = new sqlCell();
// notify wxAUI which frame to use
manager.SetManagedWindow(this);
manager.SetFlags(wxAUI_MGR_DEFAULT | wxAUI_MGR_TRANSPARENT_DRAG);
SetMinSize(wxSize(300, 350));
CreateStatusBar();
SetStatusBarPane(-1);
sqlGrid = new ctlSQLEditGrid(this, CTL_EDITGRID, wxDefaultPosition, wxDefaultSize);
sqlGrid->SetTable(0);
#ifdef __WXMSW__
sqlGrid->SetDefaultRowSize(sqlGrid->GetDefaultRowSize() + 2, true);
#endif
// Set up toolbar
toolBar = new ctlMenuToolbar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_NODIVIDER);
toolBar->SetToolBitmapSize(wxSize(16, 16));
toolBar->AddTool(MNU_SAVE, wxEmptyString, *storedata_png_bmp, _("Save the changed row."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_REFRESH, wxEmptyString, *readdata_png_bmp, _("Refresh."), wxITEM_NORMAL);
toolBar->AddTool(MNU_UNDO, wxEmptyString, *edit_undo_png_bmp, _("Undo change of data."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_COPY, wxEmptyString, *clip_copy_png_bmp, _("Copy selected lines to clipboard."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_PASTE, wxEmptyString, *clip_paste_png_bmp, _("Paste data from the clipboard."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_DELETE, wxEmptyString, *delete_png_bmp, _("Delete selected rows."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_OPTIONS, wxEmptyString, *sortfilter_png_bmp, _("Sort/filter options."), wxITEM_NORMAL);
toolBar->AddSeparator();
toolBar->AddTool(MNU_HELP, wxEmptyString, *help_png_bmp, _("Display help on this window."));
toolBar->Realize();
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
toolBar->EnableTool(MNU_DELETE, false);
// Setup the limit bar
#ifndef __WXMAC__
cbLimit = new wxComboBox(this, CTRLID_LIMITCOMBO, wxEmptyString, wxPoint(0, 0), wxSize(GetCharWidth() * 12, -1), wxArrayString(), wxCB_DROPDOWN);
#else
cbLimit = new wxComboBox(this, CTRLID_LIMITCOMBO, wxEmptyString, wxPoint(0, 0), wxSize(GetCharWidth() * 24, -1), wxArrayString(), wxCB_DROPDOWN);
#endif
cbLimit->Append(_("No limit"));
cbLimit->Append(_("1000 rows"));
cbLimit->Append(_("500 rows"));
cbLimit->Append(_("100 rows"));
cbLimit->SetValue(_("No limit"));
// Finally, the scratchpad
scratchPad = new wxTextCtrl(this, -1, wxT(""), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxHSCROLL);
// Menus
// File menu
fileMenu = new wxMenu();
fileMenu->Append(MNU_SAVE, _("&Save\tCtrl-S"), _("Save the changed row."));
fileMenu->AppendSeparator();
fileMenu->Append(MNU_CLOSE, _("&Close\tCtrl-W"), _("Close this window."));
fileMenu->Enable(MNU_SAVE, false);
// Edit menu
editMenu = new wxMenu();
editMenu->Append(MNU_UNDO, _("&Undo\tCtrl-Z"), _("Undo change of data."));
editMenu->AppendSeparator();
editMenu->Append(MNU_COPY, _("&Copy\tCtrl-C"), _("Copy selected cells to clipboard."));
editMenu->Append(MNU_PASTE, _("&Paste\tCtrl-V"), _("Paste data from the clipboard."));
editMenu->Append(MNU_DELETE, _("&Delete"), _("Delete selected rows."));
editMenu->Enable(MNU_UNDO, false);
editMenu->Enable(MNU_DELETE, false);
// View menu
viewMenu = new wxMenu();
viewMenu->Append(MNU_REFRESH, _("&Refresh\tF5"), _("Refresh."));
viewMenu->AppendSeparator();
viewMenu->Append(MNU_LIMITBAR, _("&Limit bar\tCtrl-Alt-L"), _("Show or hide the row limit options bar."), wxITEM_CHECK);
viewMenu->Append(MNU_SCRATCHPAD, _("S&cratch pad\tCtrl-Alt-S"), _("Show or hide the scratch pad."), wxITEM_CHECK);
viewMenu->Append(MNU_TOOLBAR, _("&Tool bar\tCtrl-Alt-T"), _("Show or hide the tool bar."), wxITEM_CHECK);
viewMenu->AppendSeparator();
viewMenu->Append(MNU_DEFAULTVIEW, _("&Default view\tCtrl-Alt-V"), _("Restore the default view."));
// Tools menu
toolsMenu = new wxMenu();
toolsMenu->Append(MNU_OPTIONS, _("&Sort / Filter ..."), _("Sort / Filter options."));
toolsMenu->AppendSeparator();
toolsMenu->Append(MNU_INCLUDEFILTER, _("Filter By &Selection"), _("Display only those rows that have this value in this column."));
toolsMenu->Append(MNU_EXCLUDEFILTER, _("Filter E&xcluding Selection"), _("Display only those rows that do not have this value in this column."));
toolsMenu->Append(MNU_REMOVEFILTERS, _("&Remove Filter"), _("Remove all filters on this table"));
toolsMenu->AppendSeparator();
toolsMenu->Append(MNU_ASCSORT, _("Sort &Ascending"), _("Append an ASCENDING sort condition based on this column"));
toolsMenu->Append(MNU_DESCSORT, _("Sort &Descending"), _("Append a DESCENDING sort condition based on this column"));
toolsMenu->Append(MNU_REMOVESORT, _("&Remove Sort"), _("Remove all sort conditions"));
// Help menu
helpMenu = new wxMenu();
helpMenu->Append(MNU_CONTENTS, _("&Help contents"), _("Open the helpfile."));
helpMenu->Append(MNU_HELP, _("&Edit grid help"), _("Display help on this window."));
#ifdef __WXMAC__
menuFactories = new menuFactoryList();
aboutFactory *af = new aboutFactory(menuFactories, helpMenu, 0);
wxApp::s_macAboutMenuItemId = af->GetId();
menuFactories->RegisterMenu(this, wxCommandEventHandler(pgFrame::OnAction));
#endif
menuBar = new wxMenuBar();
menuBar->Append(fileMenu, _("&File"));
menuBar->Append(editMenu, _("&Edit"));
menuBar->Append(viewMenu, _("&View"));
menuBar->Append(toolsMenu, _("&Tools"));
menuBar->Append(helpMenu, _("&Help"));
SetMenuBar(menuBar);
// Accelerators
wxAcceleratorEntry entries[8];
entries[0].Set(wxACCEL_CTRL, (int)'S', MNU_SAVE);
entries[1].Set(wxACCEL_NORMAL, WXK_F5, MNU_REFRESH);
entries[2].Set(wxACCEL_CTRL, (int)'Z', MNU_UNDO);
entries[3].Set(wxACCEL_NORMAL, WXK_F1, MNU_HELP);
entries[4].Set(wxACCEL_CTRL, (int)'C', MNU_COPY);
entries[5].Set(wxACCEL_CTRL, (int)'V', MNU_PASTE);
entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, MNU_DELETE);
entries[7].Set(wxACCEL_CTRL, (int)'W', MNU_CLOSE);
wxAcceleratorTable accel(8, entries);
SetAcceleratorTable(accel);
sqlGrid->SetAcceleratorTable(accel);
// Kickstart wxAUI
manager.AddPane(toolBar, wxAuiPaneInfo().Name(wxT("toolBar")).Caption(_("Tool bar")).ToolbarPane().Top().LeftDockable(false).RightDockable(false));
manager.AddPane(cbLimit, wxAuiPaneInfo().Name(wxT("limitBar")).Caption(_("Limit bar")).ToolbarPane().Top().LeftDockable(false).RightDockable(false));
manager.AddPane(sqlGrid, wxAuiPaneInfo().Name(wxT("sqlGrid")).Caption(_("Data grid")).Center().CaptionVisible(false).CloseButton(false).MinSize(wxSize(200, 100)).BestSize(wxSize(300, 200)));
manager.AddPane(scratchPad, wxAuiPaneInfo().Name(wxT("scratchPad")).Caption(_("Scratch pad")).Bottom().MinSize(wxSize(200, 100)).BestSize(wxSize(300, 150)));
// Now load the layout
wxString perspective;
settings->Read(wxT("frmEditGrid/Perspective-") + wxString(FRMEDITGRID_PERSPECTIVE_VER), &perspective, FRMEDITGRID_DEFAULT_PERSPECTIVE);
manager.LoadPerspective(perspective, true);
// and reset the captions for the current language
manager.GetPane(wxT("toolBar")).Caption(_("Tool bar"));
manager.GetPane(wxT("limitBar")).Caption(_("Limit bar"));
manager.GetPane(wxT("sqlGrid")).Caption(_("Data grid"));
manager.GetPane(wxT("scratchPad")).Caption(_("Scratch pad"));
// Sync the View menu options
viewMenu->Check(MNU_LIMITBAR, manager.GetPane(wxT("limitBar")).IsShown());
viewMenu->Check(MNU_TOOLBAR, manager.GetPane(wxT("toolBar")).IsShown());
viewMenu->Check(MNU_SCRATCHPAD, manager.GetPane(wxT("scratchPad")).IsShown());
// tell the manager to "commit" all the changes just made
manager.Update();
autoOrderBy = false;
if (obj->GetMetaType() == PGM_TABLE || obj->GetMetaType() == GP_PARTITION)
{
pgTable *table = (pgTable *)obj;
relkind = 'r';
hasOids = table->GetHasOids();
tableName = table->GetSchema()->GetQuotedFullIdentifier() + wxT(".") + table->GetQuotedIdentifier();
primaryKeyColNumbers = table->GetPrimaryKeyColNumbers();
autoOrderBy = true; // default order by PK/OID will be discarded when a user defines his order
orderBy = table->GetQuotedPrimaryKey();
if (orderBy.IsEmpty() && hasOids)
orderBy = wxT("oid");
if (!orderBy.IsEmpty())
{
if (pkAscending)
{
orderBy.Replace(wxT(","), wxT(" ASC,"));
orderBy += wxT(" ASC");
}
else
{
orderBy.Replace(wxT(","), wxT(" DESC,"));
orderBy += wxT(" DESC");
}
}
}
else if (obj->GetMetaType() == PGM_VIEW)
{
pgView *view = (pgView *)obj;
relkind = 'v';
hasOids = false;
tableName = view->GetSchema()->GetQuotedFullIdentifier() + wxT(".") + view->GetQuotedIdentifier();
}
else if (obj->GetMetaType() == PGM_FOREIGNTABLE)
{
pgForeignTable *foreigntable = (pgForeignTable *)obj;
relkind = 'f';
hasOids = false;
tableName = foreigntable->GetSchema()->GetQuotedFullIdentifier() + wxT(".") + foreigntable->GetQuotedIdentifier();
}
else if (obj->GetMetaType() == GP_EXTTABLE)
{
gpExtTable *exttable = (gpExtTable *)obj;
relkind = 'x';
hasOids = false;
tableName = exttable->GetSchema()->GetQuotedFullIdentifier() + wxT(".") + exttable->GetQuotedIdentifier();
}
else if (obj->GetMetaType() == PGM_CATALOGOBJECT)
{
pgCatalogObject *catobj = (pgCatalogObject *)obj;
relkind = 'v';
hasOids = false;
tableName = catobj->GetSchema()->GetQuotedFullIdentifier() + wxT(".") + catobj->GetQuotedIdentifier();
}
}
void frmEditGrid::OnEraseBackground(wxEraseEvent &event)
{
event.Skip();
}
void frmEditGrid::OnSize(wxSizeEvent &event)
{
event.Skip();
}
void frmEditGrid::OnToggleLimitBar(wxCommandEvent &event)
{
if (viewMenu->IsChecked(MNU_LIMITBAR))
manager.GetPane(wxT("limitBar")).Show(true);
else
manager.GetPane(wxT("limitBar")).Show(false);
manager.Update();
}
void frmEditGrid::OnToggleToolBar(wxCommandEvent &event)
{
if (viewMenu->IsChecked(MNU_TOOLBAR))
manager.GetPane(wxT("toolBar")).Show(true);
else
manager.GetPane(wxT("toolBar")).Show(false);
manager.Update();
}
void frmEditGrid::OnToggleScratchPad(wxCommandEvent &event)
{
if (viewMenu->IsChecked(MNU_SCRATCHPAD))
manager.GetPane(wxT("scratchPad")).Show(true);
else
manager.GetPane(wxT("scratchPad")).Show(false);
manager.Update();
}
void frmEditGrid::OnAuiUpdate(wxAuiManagerEvent &event)
{
if(event.pane->name == wxT("limitBar"))
{
viewMenu->Check(MNU_LIMITBAR, false);
}
else if(event.pane->name == wxT("toolBar"))
{
viewMenu->Check(MNU_TOOLBAR, false);
}
else if(event.pane->name == wxT("scratchPad"))
{
viewMenu->Check(MNU_SCRATCHPAD, false);
}
event.Skip();
}
void frmEditGrid::OnDefaultView(wxCommandEvent &event)
{
manager.LoadPerspective(FRMEDITGRID_DEFAULT_PERSPECTIVE, true);
// Reset the captions for the current language
manager.GetPane(wxT("toolBar")).Caption(_("Tool bar"));
manager.GetPane(wxT("limitBar")).Caption(_("Limit bar"));
manager.GetPane(wxT("sqlGrid")).Caption(_("Data grid"));
manager.GetPane(wxT("scratchPad")).Caption(_("Scratch pad"));
// tell the manager to "commit" all the changes just made
manager.Update();
// Sync the View menu options
viewMenu->Check(MNU_LIMITBAR, manager.GetPane(wxT("limitBar")).IsShown());
viewMenu->Check(MNU_TOOLBAR, manager.GetPane(wxT("toolBar")).IsShown());
viewMenu->Check(MNU_SCRATCHPAD, manager.GetPane(wxT("scratchPad")).IsShown());
}
void frmEditGrid::SetSortCols(const wxString &cols)
{
if (orderBy != cols)
{
orderBy = cols;
}
}
void frmEditGrid::SetFilter(const wxString &filter)
{
if (rowFilter != filter)
{
rowFilter = filter;
}
}
void frmEditGrid::SetLimit(const int rowlimit)
{
if (rowlimit != limit)
{
limit = rowlimit;
if (limit <= 0)
cbLimit->SetValue(_("No limit"));
else
cbLimit->SetValue(wxString::Format(wxPLURAL("%i row", "%i rows", limit), limit));
}
}
void frmEditGrid::OnLabelRightClick(wxGridEvent &event)
{
wxMenu *xmenu = new wxMenu();
wxArrayInt rows = sqlGrid->GetSelectedRows();
xmenu->Append(MNU_COPY, _("&Copy"), _("Copy selected cells to clipboard."));
xmenu->Append(MNU_PASTE, _("&Paste"), _("Paste data from the clipboard."));
xmenu->Append(MNU_DELETE, _("&Delete"), _("Delete selected rows."));
if ((rows.GetCount()) && (!sqlGrid->IsCurrentCellReadOnly()))
{
xmenu->Enable(MNU_COPY, true);
xmenu->Enable(MNU_DELETE, true);
xmenu->Enable(MNU_PASTE, true);
}
else
{
xmenu->Enable(MNU_COPY, false);
xmenu->Enable(MNU_DELETE, false);
xmenu->Enable(MNU_PASTE, false);
}
sqlGrid->PopupMenu(xmenu);
}
void frmEditGrid::OnCellRightClick(wxGridEvent &event)
{
wxMenu *xmenu = new wxMenu();
// If we cannot refresh, assume there is a data thread running. We cannot
// check thread->IsRunning() as it can crash if the thread is in some
// states :-(
if (!toolBar->GetToolEnabled(MNU_REFRESH))
return;
sqlGrid->SetGridCursor(event.GetRow(), event.GetCol());
xmenu->Append(MNU_INCLUDEFILTER, _("Filter By &Selection"), _("Display only those rows that have this value in this column."));
xmenu->Append(MNU_EXCLUDEFILTER, _("Filter E&xcluding Selection"), _("Display only those rows that do not have this value in this column."));
xmenu->Append(MNU_REMOVEFILTERS, _("&Remove Filter"), _("Remove all filters on this table"));
xmenu->InsertSeparator(3);
xmenu->Append(MNU_ASCSORT, _("Sort &Ascending"), _("Append an ASCENDING sort condition based on this column"));
xmenu->Append(MNU_DESCSORT, _("Sort &Descending"), _("Append a DESCENDING sort condition based on this column"));
xmenu->Append(MNU_REMOVESORT, _("&Remove Sort"), _("Remove all sort conditions"));
xmenu->Enable(MNU_INCLUDEFILTER, true);
xmenu->Enable(MNU_EXCLUDEFILTER, true);
xmenu->Enable(MNU_REMOVEFILTERS, true);
xmenu->Enable(MNU_ASCSORT, true);
xmenu->Enable(MNU_DESCSORT, true);
xmenu->Enable(MNU_REMOVESORT, true);
sqlGrid->PopupMenu(xmenu);
}
void frmEditGrid::OnCellChange(wxGridEvent &event)
{
sqlTable *table = sqlGrid->GetTable();
bool doSkip = true;
if (table)
{
if (table->LastRow() >= 0)
{
if (table->LastRow() != event.GetRow())
{
doSkip = DoSave();
}
}
else if (sqlGrid->GetGridCursorRow() != event.GetRow())
{
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
fileMenu->Enable(MNU_SAVE, false);
editMenu->Enable(MNU_UNDO, false);
}
}
if (doSkip)
event.Skip();
}
void frmEditGrid::OnIncludeFilter(wxCommandEvent &event)
{
int curcol = sqlGrid->GetGridCursorCol();
int currow = sqlGrid->GetGridCursorRow();
if (curcol == -1 || currow == -1)
return;
sqlTable *table = sqlGrid->GetTable();
wxString column_label = qtIdent(table->GetColLabelValueUnformatted(curcol));
wxString new_filter_string;
size_t old_filter_string_length = GetFilter().Trim().Len();
if (old_filter_string_length > 0)
{
new_filter_string = GetFilter().Trim() + wxT(" \n AND ");
}
if (table->IsColText(curcol))
{
if (sqlGrid->GetCellValue(currow, curcol).IsNull())
{
new_filter_string += column_label + wxT(" IS NULL ");
}
else
{
if (sqlGrid->GetCellValue(currow, curcol) == wxT("\'\'"))
{
new_filter_string += column_label + wxT(" = ''");
}
else
{
new_filter_string += column_label + wxT(" = ") + connection->qtDbString(sqlGrid->GetCellValue(currow, curcol)) + wxT(" ");
}
}
}
else
{
if (sqlGrid->GetCellValue(currow, curcol).IsNull())
{
new_filter_string += column_label + wxT(" IS NULL ");
}
else
{
new_filter_string += column_label + wxT(" = ") + sqlGrid->GetCellValue(currow, curcol);
}
}
SetFilter(new_filter_string);
Go();
}
void frmEditGrid::OnExcludeFilter(wxCommandEvent &event)
{
int curcol = sqlGrid->GetGridCursorCol();
int currow = sqlGrid->GetGridCursorRow();
if (curcol == -1 || currow == -1)
return;
sqlTable *table = sqlGrid->GetTable();
wxString column_label = qtIdent(table->GetColLabelValueUnformatted(curcol));
wxString new_filter_string;
size_t old_filter_string_length = GetFilter().Trim().Len();
if (old_filter_string_length > 0)
{
new_filter_string = GetFilter().Trim() + wxT(" \n AND ");
}
if (table->IsColText(curcol))
{
if (sqlGrid->GetCellValue(currow, curcol).IsNull())
{
new_filter_string += column_label + wxT(" IS NOT NULL ");
}
else
{
if (sqlGrid->GetCellValue(currow, curcol) == wxT("\'\'"))
{
new_filter_string += column_label + wxString::Format(wxT(" IS DISTINCT FROM '' ")) ;
}
else
{
new_filter_string += column_label + wxT(" IS DISTINCT FROM ") + connection->qtDbString(sqlGrid->GetCellValue(currow, curcol)) + wxT(" ");
}
}
}
else
{
if (sqlGrid->GetCellValue(currow, curcol).IsNull())
{
new_filter_string += column_label + wxT(" IS NOT NULL ") ;
}
else
{
new_filter_string += column_label + wxT(" IS DISTINCT FROM ") + sqlGrid->GetCellValue(currow, curcol);
}
}
SetFilter(new_filter_string);
Go();
}
void frmEditGrid::OnRemoveFilters(wxCommandEvent &event)
{
SetFilter(wxT(""));
Go();
}
void frmEditGrid::OnAscSort(wxCommandEvent &ev)
{
int curcol = sqlGrid->GetGridCursorCol();
if (curcol == -1)
return;
sqlTable *table = sqlGrid->GetTable();
wxString column_label = qtIdent(table->GetColLabelValueUnformatted(curcol));
wxString old_sort_string, new_sort_string;
if (autoOrderBy)
{
autoOrderBy = false;
old_sort_string = wxT("");
}
else
old_sort_string = GetSortCols().Trim();
if (old_sort_string.Find(column_label) == wxNOT_FOUND)
{
if (old_sort_string.Len() > 0)
new_sort_string = old_sort_string + wxT(" , ");
new_sort_string += column_label + wxT(" ASC ");
}
else
{
if (old_sort_string.Find(column_label + wxT(" ASC")) == wxNOT_FOUND)
{
// Previous occurrence was for DESCENDING sort
new_sort_string = old_sort_string;
new_sort_string.Replace(column_label + wxT(" DESC"), column_label + wxT(" ASC"));
}
else
{
// Previous occurrence was for ASCENDING sort. Nothing to do
new_sort_string = old_sort_string;
}
}
SetSortCols(new_sort_string);
Go();
}
void frmEditGrid::OnDescSort(wxCommandEvent &ev)
{
int curcol = sqlGrid->GetGridCursorCol();
if (curcol == -1)
return;
sqlTable *table = sqlGrid->GetTable();
wxString column_label = qtIdent(table->GetColLabelValueUnformatted(curcol));
wxString old_sort_string, new_sort_string;
if (autoOrderBy)
{
autoOrderBy = false;
old_sort_string = wxT("");
}
else
old_sort_string = GetSortCols().Trim();
if (old_sort_string.Find(column_label) == wxNOT_FOUND)
{
if (old_sort_string.Len() > 0)
new_sort_string = old_sort_string + wxT(" , ");
new_sort_string += column_label + wxT(" DESC ");
}
else
{
if (old_sort_string.Find(column_label + wxT(" DESC")) == wxNOT_FOUND)
{
// Previous occurrence was for ASCENDING sort
new_sort_string = old_sort_string;
new_sort_string.Replace(column_label + wxT(" ASC"), column_label + wxT(" DESC"));
}
else
{
// Previous occurrence was for DESCENDING sort. Nothing to do
new_sort_string = old_sort_string;
}
}
SetSortCols(new_sort_string);
Go();
}
void frmEditGrid::OnRemoveSort(wxCommandEvent &ev)
{
SetSortCols(wxT(""));
Go();
}
void frmEditGrid::OnCopy(wxCommandEvent &ev)
{
wxWindow *wnd = FindFocus();
if (wnd == scratchPad)
{
scratchPad->Copy();
}
else
{
if (editorCell->IsSet())
{
if (wxTheClipboard->Open())
{
if (sqlGrid->GetTable()->IsColText(sqlGrid->GetGridCursorCol()))
{
wxStyledTextCtrl *text = (wxStyledTextCtrl *)sqlGrid->GetCellEditor(sqlGrid->GetGridCursorRow(), sqlGrid->GetGridCursorCol())->GetControl();
if (text && !text->GetSelectedText().IsEmpty())
{
wxTheClipboard->SetData(new wxTextDataObject(text->GetSelectedText()));
SetStatusText(_("Data from one cell copied to clipboard."));
}
}
else
{
wxTextCtrl *text = (wxTextCtrl *)sqlGrid->GetCellEditor(sqlGrid->GetGridCursorRow(), sqlGrid->GetGridCursorCol())->GetControl();
if (text && !text->GetStringSelection().IsEmpty())
{
wxTheClipboard->SetData(new wxTextDataObject(text->GetStringSelection()));
SetStatusText(_("Data from one cell copied to clipboard."));
}
}
wxTheClipboard->Close();
}
}
else if(sqlGrid->GetNumberRows() > 0)
{
int copied;
copied = sqlGrid->Copy(false);
SetStatusText(wxString::Format(
wxPLURAL("Data from %d row copied to clipboard.", "Data from %d rows copied to clipboard.", copied),
copied));
}
}
}
void frmEditGrid::OnPaste(wxCommandEvent &ev)
{
wxWindow *wnd = FindFocus();
if (wnd == scratchPad)
{
scratchPad->Paste();
}
else if (editorCell->IsSet())
{
if (wxTheClipboard->Open())
{
if (wxTheClipboard->IsSupported(wxDF_TEXT))
{
wxTextDataObject data;
wxTheClipboard->GetData(data);
wxControl *ed = sqlGrid->GetCellEditor(editorCell->GetRow(), editorCell->GetCol())->GetControl();
if (ed->IsKindOf(CLASSINFO(wxStyledTextCtrl)))
{
wxStyledTextCtrl *txtEd = (wxStyledTextCtrl *)ed;
txtEd->ReplaceSelection(data.GetText());
}
else if (ed->IsKindOf(CLASSINFO(wxCheckBox)))
{
wxCheckBox *boolEd = (wxCheckBox *)ed;
if (data.GetText().Lower() == wxT("true"))
boolEd->Set3StateValue(wxCHK_CHECKED);
else if (data.GetText().Lower() == wxT("false"))
boolEd->Set3StateValue(wxCHK_UNCHECKED);
else
boolEd->Set3StateValue(wxCHK_UNDETERMINED);
}
else if (ed->IsKindOf(CLASSINFO(wxTextCtrl)))
{
wxTextCtrl *txtEd = (wxTextCtrl *)ed;
long x, y;
txtEd->GetSelection(&x, &y);
txtEd->Replace(x, y, data.GetText());
//txtEd->SetValue(data.GetText());
}
}
wxTheClipboard->Close();
}
}
else if(sqlGrid->GetNumberRows() > 0)
{
if (toolBar->GetToolEnabled(MNU_SAVE))
{
wxMessageDialog msg(this, _("There is unsaved data in a row.\nDo you want to store to the database?"), _("Unsaved data"),
wxYES_NO | wxICON_QUESTION | wxCANCEL);
switch (msg.ShowModal())
{
case wxID_YES:
if (!DoSave())
return;
break;
case wxID_CANCEL:
return;
break;
case wxID_NO:
CancelChange();
break;
}
}
if (sqlGrid->GetTable()->Paste())
{
toolBar->EnableTool(MNU_SAVE, true);
toolBar->EnableTool(MNU_UNDO, true);
fileMenu->Enable(MNU_SAVE, true);
editMenu->Enable(MNU_UNDO, true);
}
}
}
void frmEditGrid::OnHelp(wxCommandEvent &ev)
{
DisplayHelp(wxT("editgrid"), HELP_PGADMIN);
}
void frmEditGrid::OnContents(wxCommandEvent &ev)
{
DisplayHelp(wxT("index"), HELP_PGADMIN);
}
void frmEditGrid::OnKey(wxKeyEvent &event)
{
int curcol = sqlGrid->GetGridCursorCol();
int currow = sqlGrid->GetGridCursorRow();
if (curcol == -1 || currow == -1)
return;
int keycode = event.GetKeyCode();
wxCommandEvent ev;
switch (keycode)
{
case WXK_DELETE:
{
if (editorCell->IsSet() || !toolBar->GetToolEnabled(MNU_DELETE))
{
if (!sqlGrid->IsCurrentCellReadOnly())
{
sqlGrid->EnableCellEditControl();
sqlGrid->ShowCellEditControl();
wxGridCellEditor *edit = sqlGrid->GetCellEditor(currow, curcol);
if (edit)
{
wxControl *ctl = edit->GetControl();
if (ctl)
{
wxStyledTextCtrl *txt = wxDynamicCast(ctl, wxStyledTextCtrl);
if (txt)
txt->SetText(wxEmptyString);
}
edit->DecRef();
}
}
}
else
{
OnDelete(ev);
}
return;
}
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
// check for shift etc.
if (event.ControlDown() || event.ShiftDown())
{
// Inject a RETURN into the control
wxGridCellEditor *edit = sqlGrid->GetCellEditor(currow, curcol);
if (edit)
{
wxControl *ctl = edit->GetControl();
if (ctl)
{
wxStyledTextCtrl *txt = wxDynamicCast(ctl, wxStyledTextCtrl);
if (txt)
txt->ReplaceSelection(END_OF_LINE);
}
edit->DecRef();
}
return;
}
else
{
if( keycode != WXK_NUMPAD_ENTER )
{
// if we are at the end of the row
if (curcol == sqlGrid->GetNumberCols() - 1)
{
// we first get to the first column of the next row
curcol = 0;
currow++;
// * if the displayed object is a table,
// we first need to make sure that the new selected
// cell is read/write, otherwise we need to select
// the next one
// * if the displayed object is not a table (for
// example, a view), all cells are readonly, so
// we skip that part
if (relkind == 'r')
{
// locate first editable column
while (curcol < sqlGrid->GetNumberCols() && sqlGrid->IsReadOnly(currow, curcol))
curcol++;
// next line is completely read-only
if (curcol == sqlGrid->GetNumberCols())
return;
}
}
else
curcol++;
}
else // ( keycode==WXK_NUMPAD_ENTER )
{
currow++;
}
OnSave(ev);
sqlGrid->SetGridCursor(currow, curcol);
return;
}
case WXK_TAB:
if (event.ControlDown())
{
wxStyledTextCtrl *text = (wxStyledTextCtrl *)sqlGrid->GetCellEditor(sqlGrid->GetGridCursorRow(), sqlGrid->GetGridCursorCol())->GetControl();
if (text)
text->SetText(wxT("\t"));
return;
}
break;
case WXK_ESCAPE:
CancelChange();
break;
default:
if (sqlGrid->IsEditable() && keycode >= WXK_SPACE && keycode < WXK_START)
{
if (sqlGrid->IsCurrentCellReadOnly())
return;
toolBar->EnableTool(MNU_SAVE, true);
toolBar->EnableTool(MNU_UNDO, true);
fileMenu->Enable(MNU_SAVE, true);
editMenu->Enable(MNU_UNDO, true);
}
break;
}
event.Skip();
}
void frmEditGrid::OnClose(wxCommandEvent &event)
{
this->Close();
}
void frmEditGrid::OnCloseWindow(wxCloseEvent &event)
{
// We need to call OnCellChange to check if some cells for the row have been changed
wxGridEvent evt;
OnCellChange(evt);
// If MNU_SAVE item is still enabled, we need to ask about the unsaved data
if (toolBar->GetToolEnabled(MNU_SAVE))
{
int flag = wxYES_NO | wxICON_QUESTION;
if (event.CanVeto())
flag |= wxCANCEL;
wxMessageDialog msg(this, _("There is unsaved data in a row.\nDo you want to store to the database?"), _("Unsaved data"),
flag);
switch (msg.ShowModal())
{
case wxID_YES:
{
if (!DoSave())
{
event.Veto();
return;
}
break;
}
case wxID_CANCEL:
event.Veto();
return;
}
}
Abort();
Destroy();
}
void frmEditGrid::OnUndo(wxCommandEvent &event)
{
sqlGrid->DisableCellEditControl();
sqlGrid->GetTable()->UndoLine(sqlGrid->GetGridCursorRow());
sqlGrid->ForceRefresh();
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
fileMenu->Enable(MNU_SAVE, false);
editMenu->Enable(MNU_UNDO, false);
}
void frmEditGrid::OnRefresh(wxCommandEvent &event)
{
if (!toolBar->GetToolEnabled(MNU_REFRESH))
return;
if (toolBar->GetToolEnabled(MNU_SAVE))
{
wxMessageDialog msg(this, _("There is unsaved data in a row.\nDo you want to store to the database?"), _("Unsaved data"),
wxYES_NO | wxICON_QUESTION | wxCANCEL);
switch (msg.ShowModal())
{
case wxID_YES:
{
if (!DoSave())
return;
break;
}
case wxID_CANCEL:
return;
}
}
sqlGrid->DisableCellEditControl();
Go();
}
void frmEditGrid::OnSave(wxCommandEvent &event)
{
if (sqlGrid->GetBatchCount() == 0)
DoSave();
}
bool frmEditGrid::DoSave()
{
sqlGrid->HideCellEditControl();
sqlGrid->SaveEditControlValue();
sqlGrid->DisableCellEditControl();
if (!sqlGrid->GetTable()->StoreLine())
return false;
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
fileMenu->Enable(MNU_SAVE, false);
editMenu->Enable(MNU_UNDO, false);
return true;
}
void frmEditGrid::CancelChange()
{
sqlGrid->HideCellEditControl();
sqlGrid->SaveEditControlValue();
sqlGrid->DisableCellEditControl();
toolBar->EnableTool(MNU_SAVE, false);
toolBar->EnableTool(MNU_UNDO, false);
fileMenu->Enable(MNU_SAVE, false);
editMenu->Enable(MNU_UNDO, false);
sqlGrid->GetTable()->UndoLine(sqlGrid->GetGridCursorRow());
sqlGrid->ForceRefresh();
}
void frmEditGrid::OnOptions(wxCommandEvent &event)
{
if (toolBar->GetToolEnabled(MNU_SAVE))
{
wxMessageDialog msg(this, _("There is unsaved data in a row.\nDo you want to store to the database?"), _("Unsaved data"),
wxYES_NO | wxICON_QUESTION | wxCANCEL);
switch (msg.ShowModal())
{
case wxID_YES:
{
if (!DoSave())
return;
break;
}
case wxID_CANCEL:
return;
case wxID_NO:
CancelChange();
}
}
dlgEditGridOptions winOptions(this, connection, tableName, sqlGrid);
if (winOptions.ShowModal())
Go();
}
template < class T >
int ArrayCmp(T *a, T *b)
{
if (*a == *b)
return 0;
if (*a > *b)
return 1;
else
return -1;
}
void frmEditGrid::OnDelete(wxCommandEvent &event)
{
// Don't bugger about with keypresses to the scratch pad.
if (FindFocus() == scratchPad)
{
event.Skip();
return;
}
if (editorCell->IsSet())
{
if (sqlGrid->GetTable()->IsColBoolean(sqlGrid->GetGridCursorCol()))
return;
if (sqlGrid->GetTable()->IsColText(sqlGrid->GetGridCursorCol()))
{
wxStyledTextCtrl *text = (wxStyledTextCtrl *)sqlGrid->GetCellEditor(sqlGrid->GetGridCursorRow(), sqlGrid->GetGridCursorCol())->GetControl();
if (text && text->GetCurrentPos() <= text->GetTextLength())
{
if (text->GetSelectionStart() == text->GetSelectionEnd())
text->SetSelection(text->GetSelectionStart(), text->GetSelectionStart() + 1);
text->Clear();
}
}
else
{
wxTextCtrl *text = (wxTextCtrl *)sqlGrid->GetCellEditor(sqlGrid->GetGridCursorRow(), sqlGrid->GetGridCursorCol())->GetControl();
if (text)
{
long x, y;
text->GetSelection(&x, &y);
if (x != y)
text->Remove(x, x + y + 1);
else
text->Remove(x, x + 1);
}
}
return;
}
// If the delete button is disabled, don't try to delete anything
if (!toolBar->GetToolEnabled(MNU_DELETE))
return;
wxArrayInt delrows = sqlGrid->GetSelectedRows();
int i = delrows.GetCount();
if (i == 0)
return;
wxString prompt;
if (i == 1)
prompt = _("Are you sure you wish to delete the selected row?");
else
prompt.Printf(_("Are you sure you wish to delete the %d selected rows?"), i);
wxMessageDialog msg(this, prompt, _("Delete rows?"), wxYES_NO | wxICON_QUESTION);
if (msg.ShowModal() != wxID_YES)
return;
sqlGrid->BeginBatch();
// Sort the grid so we always delete last->first, otherwise we
// could end up deleting anything because the array returned by
// GetSelectedRows is in the order that rows were selected by
// the user.
delrows.Sort(ArrayCmp);
// don't care a lot about optimizing here; doing it line by line
// just as sqlTable::DeleteRows does
bool show_continue_message = true;
while (i--)
{
if (!sqlGrid->DeleteRows(delrows.Item(i), 1) &&
i > 0 &&
show_continue_message)
{
wxMessageDialog msg(this, wxString::Format(wxPLURAL(
"There was an error deleting the previous record.\nAre you sure you wish to delete the remaining %d row?",
"There was an error deleting the previous record.\nAre you sure you wish to delete the remaining %d rows?",
i), i), _("Delete more records ?"), wxYES_NO | wxICON_QUESTION);
if (msg.ShowModal() != wxID_YES)
break;
else
show_continue_message = false;
}
}
sqlGrid->EndBatch();
SetStatusText(wxString::Format(wxPLURAL("%d row.", "%d rows.", sqlGrid->GetTable()->GetNumberStoredRows()), sqlGrid->GetTable()->GetNumberStoredRows()), 0);
}
void frmEditGrid::OnEditorShown(wxGridEvent &event)
{
toolBar->EnableTool(MNU_SAVE, true);
toolBar->EnableTool(MNU_UNDO, true);
fileMenu->Enable(MNU_SAVE, true);
editMenu->Enable(MNU_UNDO, true);
editorCell->SetCell(event.GetRow(), event.GetCol());
event.Skip();
}
void frmEditGrid::OnEditorHidden(wxGridEvent &event)
{
editorCell->ClearCell();
}
void frmEditGrid::OnGridSelectCells(wxGridRangeSelectEvent &event)
{
if (sqlGrid->IsEditable())
{
wxArrayInt rows = sqlGrid->GetSelectedRows();
bool enable = rows.GetCount() > 0;
if (enable)
{
wxCommandEvent nullEvent;
OnSave(event);
// check if a readonly line is selected
int row, col;
size_t i;
for (i = 0 ; i < rows.GetCount() ; i++)
{
row = rows.Item(i);
bool lineEnabled = false;
if (row == sqlGrid->GetNumberRows() - 1)
{
// the (*) line may not be deleted/copied
enable = false;
break;
}
for (col = 0 ; col < sqlGrid->GetNumberCols() ; col++)
{
if (!sqlGrid->IsReadOnly(row, col))
{
lineEnabled = true;
break;
}
}
if (!lineEnabled)
{
enable = false;
break;
}
}
}
toolBar->EnableTool(MNU_DELETE, enable);
editMenu->Enable(MNU_DELETE, enable);
}
event.Skip();
}
void frmEditGrid::ShowForm(bool filter)
{
bool abort = false;
if (relkind == 'r' || relkind == 'v' || relkind == 'x' || relkind == 'f')
{
if (filter)
{
dlgEditGridOptions *winOptions = new dlgEditGridOptions(this, connection, tableName, sqlGrid);
abort = !(winOptions->ShowModal());
}
if (abort)
{
// Hack to ensure there's a table for ~wxGrid() to delete
sqlGrid->CreateGrid(0, 0);
sqlGrid->SetTable(0);
Close();
Destroy();
}
else
{
Show(true);
Go();
}
}
else
{
wxLogError(__("No Table or view."));
// Hack to ensure there's a table for ~wxGrid() to delete
sqlGrid->CreateGrid(0, 0);
Close();
Destroy();
}
}
void frmEditGrid::Go()
{
long templong;
if (cbLimit->GetValue() != wxT("") &&
cbLimit->GetValue() != _("No limit") &&
!cbLimit->GetValue().BeforeFirst(' ').ToLong(&templong))
{
wxLogError(_("The row limit must be an integer number or 'No limit'"));
return;
}
if (cbLimit->GetValue() == _("No limit"))
SetLimit(0);
else
{
cbLimit->GetValue().BeforeFirst(' ').ToLong(&templong);
SetLimit(templong);
}
// Check we have access
if (connection->ExecuteScalar(wxT("SELECT count(*) FROM ") + tableName + wxT(" WHERE false")) == wxT(""))
return;
SetStatusText(_("Refreshing data, please wait."), 0);
toolBar->EnableTool(MNU_REFRESH, false);
viewMenu->Enable(MNU_REFRESH, false);
toolBar->EnableTool(MNU_OPTIONS, false);
toolsMenu->Enable(MNU_OPTIONS, false);
toolsMenu->Enable(MNU_INCLUDEFILTER, false);
toolsMenu->Enable(MNU_EXCLUDEFILTER, false);
toolsMenu->Enable(MNU_REMOVEFILTERS, false);
toolsMenu->Enable(MNU_ASCSORT, false);
toolsMenu->Enable(MNU_DESCSORT, false);
toolsMenu->Enable(MNU_REMOVESORT, false);
wxString qry = wxT("SELECT ");
if (hasOids)
qry += wxT("oid, ");
qry += wxT("* FROM ") + tableName;
if (!rowFilter.IsEmpty())
{
qry += wxT(" WHERE ") + rowFilter;
}
if (!orderBy.IsEmpty())
{
qry += wxT("\n ORDER BY ") + orderBy;
}
if (limit > 0)
qry += wxT(" LIMIT ") + wxString::Format(wxT("%i"), limit);
thread = new pgQueryThread(connection, qry);
if (thread->Create() != wxTHREAD_NO_ERROR)
{
Abort();
toolBar->EnableTool(MNU_REFRESH, true);
viewMenu->Enable(MNU_REFRESH, true);
toolBar->EnableTool(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_INCLUDEFILTER, true);
toolsMenu->Enable(MNU_EXCLUDEFILTER, true);
toolsMenu->Enable(MNU_REMOVEFILTERS, true);
toolsMenu->Enable(MNU_ASCSORT, true);
toolsMenu->Enable(MNU_DESCSORT, true);
toolsMenu->Enable(MNU_REMOVESORT, true);
return;
}
thread->Run();
while (thread && thread->IsRunning())
{
wxTheApp->Yield(true);
wxMilliSleep(10);
}
// Brute force check to ensure the user didn't get bored and close the window
if (closing)
return;
if (!thread)
{
toolBar->EnableTool(MNU_REFRESH, true);
viewMenu->Enable(MNU_REFRESH, true);
toolBar->EnableTool(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_INCLUDEFILTER, true);
toolsMenu->Enable(MNU_EXCLUDEFILTER, true);
toolsMenu->Enable(MNU_REMOVEFILTERS, true);
toolsMenu->Enable(MNU_ASCSORT, true);
toolsMenu->Enable(MNU_DESCSORT, true);
toolsMenu->Enable(MNU_REMOVESORT, true);
return;
}
if (!thread->DataValid())
{
Abort();
toolBar->EnableTool(MNU_REFRESH, true);
viewMenu->Enable(MNU_REFRESH, true);
toolBar->EnableTool(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_INCLUDEFILTER, true);
toolsMenu->Enable(MNU_EXCLUDEFILTER, true);
toolsMenu->Enable(MNU_REMOVEFILTERS, true);
toolsMenu->Enable(MNU_ASCSORT, true);
toolsMenu->Enable(MNU_DESCSORT, true);
toolsMenu->Enable(MNU_REMOVESORT, true);
return;
}
SetStatusText(wxString::Format(wxPLURAL("%d row.", "%d rows.", (int)thread->DataSet()->NumRows()), (int)thread->DataSet()->NumRows()), 0);
sqlGrid->BeginBatch();
// to force the grid to create scrollbars, we make sure the size so small that scrollbars are needed
// later, we will resize the grid's parent to force the correct size (now including scrollbars, even if
// they are suppressed initially. Win32 won't need this.
// !!! This hack breaks columns auto-sizing ( GetClientSize().GetWidth() is used in ctlSQLGrid::AutoSizeColumns() )
// !!! Is it still required?
//sqlGrid->SetSize(10, 10);
sqlGrid->SetTable(new sqlTable(connection, thread, tableName, relid, hasOids, primaryKeyColNumbers, relkind), true);
sqlGrid->AutoSizeColumns(false);
sqlGrid->EndBatch();
toolBar->EnableTool(MNU_REFRESH, true);
viewMenu->Enable(MNU_REFRESH, true);
toolBar->EnableTool(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_OPTIONS, true);
toolsMenu->Enable(MNU_INCLUDEFILTER, true);
toolsMenu->Enable(MNU_EXCLUDEFILTER, true);
toolsMenu->Enable(MNU_REMOVEFILTERS, true);
toolsMenu->Enable(MNU_ASCSORT, true);
toolsMenu->Enable(MNU_DESCSORT, true);
toolsMenu->Enable(MNU_REMOVESORT, true);
manager.Update();
if (!hasOids && primaryKeyColNumbers.IsEmpty() && relkind == 'r')
frmHint::ShowHint(this, HINT_READONLY_NOPK, tableName);
// Set the thread variable to zero so we don't try to
// abort it if the user cancels now.
thread = 0;
}
frmEditGrid::~frmEditGrid()
{
closing = true;
mainForm->RemoveFrame(this);
settings->Write(wxT("frmEditGrid/Perspective-") + wxString(FRMEDITGRID_PERSPECTIVE_VER), manager.SavePerspective());
manager.UnInit();
if (connection)
delete connection;
}
void frmEditGrid::Abort()
{
if (sqlGrid->GetTable())
{
sqlGrid->HideCellEditControl();
sqlGrid->SetTable(0);
}
if (thread)
{
SetStatusText(_("aborting."), 0);
if (thread->IsRunning())
{
thread->CancelExecution();
thread->Wait();
}
delete thread;
thread = 0;
}
}
ctlSQLEditGrid::ctlSQLEditGrid(wxFrame *parent, wxWindowID id, const wxPoint &pos, const wxSize &size)
: ctlSQLGrid(parent, id, pos, size)
{
}
bool ctlSQLEditGrid::CheckRowPresent(int row)
{
return GetTable()->CheckInCache(row);
}
void ctlSQLEditGrid::ResizeEditor(int row, int col)
{
if (GetTable()->needsResizing(col))
{
wxGridCellAttr *attr = GetCellAttr(row, col);
wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
if ( renderer )
{
wxClientDC dc(GetGridWindow());
wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
renderer->DecRef();
int w = wxMax(size.GetWidth(), 15) + 20;
int h = wxMax(size.GetHeight(), 15) + 20;
wxGridCellEditor *editor = attr->GetEditor(this, row, col);
if (editor)
{
wxRect cellRect = CellToRect(m_currentCellCoords);
wxRect rect = cellRect;
rect.SetWidth(w);
rect.SetHeight(h);
// we might have scrolled
CalcUnscrolledPosition(0, 0, &w, &h);
rect.SetLeft(rect.GetLeft() - w);
rect.SetTop(rect.GetTop() - h);
// Clip rect to client size
GetClientSize(&w, &h);
rect.SetRight(wxMin(rect.GetRight(), w));
rect.SetBottom(wxMin(rect.GetBottom(), h));
// but not smaller than original cell
rect.SetWidth(wxMax(cellRect.GetWidth(), rect.GetWidth()));
rect.SetHeight(wxMax(cellRect.GetHeight(), rect.GetHeight()));
editor->SetSize(rect);
editor->DecRef();
}
}
attr->DecRef();
}
}
wxArrayInt ctlSQLEditGrid::GetSelectedRows() const
{
wxArrayInt rows, rows2;
wxGridCellCoordsArray tl = GetSelectionBlockTopLeft(), br = GetSelectionBlockBottomRight();
int maxCol = ((ctlSQLEditGrid *)this)->GetNumberCols() - 1;
size_t i;
for (i = 0 ; i < tl.GetCount() ; i++)
{
wxGridCellCoords c1 = tl.Item(i), c2 = br.Item(i);
if (c1.GetCol() != 0 || c2.GetCol() != maxCol)
return rows2;
int j;
for (j = c1.GetRow() ; j <= c2.GetRow() ; j++)
rows.Add(j);
}
rows2 = wxGrid::GetSelectedRows();
rows.Sort(ArrayCmp);
rows2.Sort(ArrayCmp);
size_t i2 = 0, cellRowMax = rows.GetCount();
for (i = 0 ; i < rows2.GetCount() ; i++)
{
int row = rows2.Item(i);
while (i2 < cellRowMax && rows.Item(i2) < row)
i2++;
if (i2 == cellRowMax || row != rows.Item(i2))
rows.Add(row);
}
return rows;
}
class sqlGridTextEditor : public wxGridCellTextEditor
{
public:
virtual wxGridCellEditor *Clone() const
{
return new sqlGridTextEditor();
}
void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler);
void BeginEdit(int row, int col, wxGrid *grid);
#if wxCHECK_VERSION(2, 9, 0)
void ApplyEdit(int row, int col, wxGrid *grid);
bool EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *);
#else
bool EndEdit(int row, int col, wxGrid *grid);
#endif
wxString GetValue() const;
virtual void Reset()
{
DoReset(m_startValue);
}
void StartingKey(wxKeyEvent &event);
protected:
void DoBeginEdit(const wxString &startValue);
wxStyledTextCtrl *Text() const
{
return (wxStyledTextCtrl *)m_control;
}
void DoReset(const wxString &startValue);
wxString m_startValue;
};
void sqlGridTextEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler)
{
int flags = wxSTC_WRAP_NONE;
m_control = new wxStyledTextCtrl(parent, id,
wxDefaultPosition, wxDefaultSize, flags
);
wxGridCellEditor::Create(parent, id, evtHandler);
}
void sqlGridTextEditor::BeginEdit(int row, int col, wxGrid *grid)
{
m_startValue = grid->GetTable()->GetValue(row, col);
DoBeginEdit(m_startValue);
((ctlSQLEditGrid *)grid)->ResizeEditor(row, col);
}
void sqlGridTextEditor::DoBeginEdit(const wxString &startValue)
{
Text()->SetMarginWidth(1, 0);
Text()->SetText(startValue);
Text()->SetCurrentPos(Text()->GetTextLength());
Text()->SetUseHorizontalScrollBar(false);
Text()->SetUseVerticalScrollBar(false);
Text()->SetSelection(0, -1);
Text()->SetFocus();
}
#if wxCHECK_VERSION(2, 9, 0)
void sqlGridTextEditor::ApplyEdit(int row, int col, wxGrid *grid)
{
wxString value = Text()->GetText();
grid->GetTable()->SetValue(row, col, value);
}
#endif
#if wxCHECK_VERSION(2, 9, 0)
bool sqlGridTextEditor::EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *)
#else
bool sqlGridTextEditor::EndEdit(int row, int col, wxGrid *grid)
#endif
{
bool changed = false;
wxString value = Text()->GetText();
if (value != m_startValue)
changed = true;
#if !wxCHECK_VERSION(2, 9, 0)
if (changed)
grid->GetTable()->SetValue(row, col, value);
#endif
return changed;
}
wxString sqlGridTextEditor::GetValue() const
{
return Text()->GetText();
}
void sqlGridTextEditor::DoReset(const wxString &startValue)
{
Text()->SetText(startValue);
Text()->SetSelection(-1, -1);
}
void sqlGridTextEditor::StartingKey(wxKeyEvent &event)
{
wxChar ch;
#if wxUSE_UNICODE
ch = event.GetUnicodeKey();
if (ch <= 127)
ch = (wxChar)event.GetKeyCode();
#else
ch = (wxChar)event.GetKeyCode();
#endif
if (ch != (wxChar)WXK_BACK)
{
Text()->SetText(ch);
Text()->GotoPos(Text()->GetLength());
}
}
class sqlGridNumericEditor : public wxGridCellTextEditor
{
public:
sqlGridNumericEditor(int len = -1, int prec = -1)
{
numlen = len;
numprec = prec;
}
virtual wxGridCellEditor *Clone() const
{
return new sqlGridNumericEditor(numlen, numprec);
}
virtual void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler);
virtual bool IsAcceptedKey(wxKeyEvent &event);
virtual void BeginEdit(int row, int col, wxGrid *grid);
#if wxCHECK_VERSION(2, 9, 0)
void ApplyEdit(int row, int col, wxGrid *grid);
bool EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *);
#else
bool EndEdit(int row, int col, wxGrid *grid);
#endif
virtual void Reset()
{
DoReset(m_startValue);
}
virtual void StartingKey(wxKeyEvent &event);
virtual void SetParameters(const wxString &params);
protected:
int numlen, numprec;
wxString m_startValue;
};
void sqlGridNumericEditor::StartingKey(wxKeyEvent &event)
{
int keycode = event.GetKeyCode();
bool allowed = false;
switch (keycode)
{
case WXK_DECIMAL:
case WXK_NUMPAD_DECIMAL:
case '.':
if (numprec)
allowed = true;
break;
case '+':
case WXK_ADD:
case WXK_NUMPAD_ADD:
case '-':
case WXK_SUBTRACT:
case WXK_NUMPAD_SUBTRACT:
case WXK_NUMPAD0:
case WXK_NUMPAD1:
case WXK_NUMPAD2:
case WXK_NUMPAD3:
case WXK_NUMPAD4:
case WXK_NUMPAD5:
case WXK_NUMPAD6:
case WXK_NUMPAD7:
case WXK_NUMPAD8:
case WXK_NUMPAD9:
allowed = true;
break;
default:
if (wxIsdigit(keycode))
allowed = true;
break;
}
if (allowed)
wxGridCellTextEditor::StartingKey(event);
else
event.Skip();
}
bool sqlGridNumericEditor::IsAcceptedKey(wxKeyEvent &event)
{
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
int keycode = event.GetKeyCode();
switch ( keycode )
{
case WXK_DECIMAL:
case WXK_NUMPAD_DECIMAL:
return (numprec != 0);
case '+':
case WXK_ADD:
case WXK_NUMPAD_ADD:
case '-':
case WXK_SUBTRACT:
case WXK_NUMPAD_SUBTRACT:
case WXK_NUMPAD0:
case WXK_NUMPAD1:
case WXK_NUMPAD2:
case WXK_NUMPAD3:
case WXK_NUMPAD4:
case WXK_NUMPAD5:
case WXK_NUMPAD6:
case WXK_NUMPAD7:
case WXK_NUMPAD8:
case WXK_NUMPAD9:
return true;
default:
return wxIsdigit(keycode) != 0;
}
}
return false;
}
void sqlGridNumericEditor::BeginEdit(int row, int col, wxGrid *grid)
{
m_startValue = grid->GetTable()->GetValue(row, col);
wxString value = m_startValue;
// localize value here
DoBeginEdit(value);
}
#if wxCHECK_VERSION(2, 9, 0)
void sqlGridNumericEditor::ApplyEdit(int row, int col, wxGrid *grid)
{
wxString value = Text()->GetValue();
grid->GetTable()->SetValue(row, col, value);
m_startValue = wxEmptyString;
Text()->SetValue(m_startValue);
}
#endif
#if wxCHECK_VERSION(2, 9, 0)
bool sqlGridNumericEditor::EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *)
#else
bool sqlGridNumericEditor::EndEdit(int row, int col, wxGrid *grid)
#endif
{
wxASSERT_MSG(m_control,
wxT("The sqlGridNumericEditor must be Created first!"));
bool changed = false;
wxString value = Text()->GetValue();
// de-localize value here
if (value != m_startValue)
changed = true;
#if !wxCHECK_VERSION(2, 9, 0)
if (changed)
grid->GetTable()->SetValue(row, col, value);
m_startValue = wxEmptyString;
Text()->SetValue(m_startValue);
#endif
return changed;
}
void sqlGridNumericEditor::SetParameters(const wxString &params)
{
if ( !params )
{
// reset to default
numlen = -1;
numprec = -1;
}
else
{
long tmp;
if ( params.BeforeFirst(wxT(',')).ToLong(&tmp) )
{
numlen = (int)tmp;
if ( params.AfterFirst(wxT(',')).ToLong(&tmp) )
{
numprec = (int)tmp;
// skip the error message below
return;
}
}
}
}
void sqlGridNumericEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler)
{
m_control = new wxTextCtrl(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize);
wxGridCellEditor::Create(parent, id, evtHandler);
Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
}
//////////////////////////////////////////////////////////////////////
// Bool editor
//////////////////////////////////////////////////////////////////////
class sqlGridBoolEditor : public wxGridCellEditor
{
public:
sqlGridBoolEditor() { }
virtual void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler);
virtual void SetSize(const wxRect &rect);
virtual void Show(bool show, wxGridCellAttr *attr = (wxGridCellAttr *)NULL);
virtual bool IsAcceptedKey(wxKeyEvent &event);
virtual void BeginEdit(int row, int col, wxGrid *grid);
#if wxCHECK_VERSION(2, 9, 0)
virtual void ApplyEdit(int row, int col, wxGrid *grid); // pure virtual in wx 2.9+, doesn't exist in prior versions
virtual bool EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *);
#else
virtual bool EndEdit(int row, int col, wxGrid *grid);
#endif
virtual void Reset();
virtual void StartingClick();
virtual void StartingKey(wxKeyEvent &event);
virtual wxGridCellEditor *Clone() const
{
return new sqlGridBoolEditor;
}
virtual wxString GetValue() const;
protected:
wxCheckBox *CBox() const
{
return (wxCheckBox *)m_control;
}
private:
wxCheckBoxState m_startValue;
DECLARE_NO_COPY_CLASS(sqlGridBoolEditor)
};
void sqlGridBoolEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler)
{
m_control = new wxCheckBox(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER);
wxGridCellEditor::Create(parent, id, evtHandler);
}
void sqlGridBoolEditor::SetSize(const wxRect &r)
{
bool resize = false;
wxSize size = m_control->GetSize();
wxCoord minSize = wxMin(r.width, r.height);
// check if the checkbox is not too big/small for this cell
wxSize sizeBest = m_control->GetBestSize();
if ( !(size == sizeBest) )
{
// reset to default size if it had been made smaller
size = sizeBest;
resize = true;
}
if ( size.x >= minSize || size.y >= minSize )
{
// leave 1 pixel margin
size.x = size.y = minSize - 2;
resize = true;
}
if ( resize )
{
m_control->SetSize(size);
}
// position it in the centre of the rectangle (TODO: support alignment?)
#if defined(__WXGTK__)
// the checkbox without label still has some space to the right in wxGTK,
// so shift it to the right
size.x -= 8;
#elif defined(__WXMSW__)
// here too, but in other way
size.x += 1;
size.y -= 2;
#endif
int hAlign = wxALIGN_CENTRE;
int vAlign = wxALIGN_CENTRE;
if (GetCellAttr())
GetCellAttr()->GetAlignment(& hAlign, & vAlign);
int x = 0, y = 0;
if (hAlign == wxALIGN_LEFT)
{
x = r.x + 2;
#ifdef __WXMSW__
x += 2;
#endif
y = r.y + r.height / 2 - size.y / 2;
}
else if (hAlign == wxALIGN_RIGHT)
{
x = r.x + r.width - size.x - 2;
y = r.y + r.height / 2 - size.y / 2;
}
else if (hAlign == wxALIGN_CENTRE)
{
x = r.x + r.width / 2 - size.x / 2;
y = r.y + r.height / 2 - size.y / 2;
}
m_control->Move(x, y);
}
void sqlGridBoolEditor::Show(bool show, wxGridCellAttr *attr)
{
m_control->Show(show);
if ( show )
{
wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY;
CBox()->SetBackgroundColour(colBg);
}
}
void sqlGridBoolEditor::BeginEdit(int row, int col, wxGrid *grid)
{
wxASSERT_MSG(m_control, wxT("The sqlGridBoolEditor must be Created first!"));
wxString value = grid->GetTable()->GetValue(row, col);
if (value == wxT("TRUE"))
m_startValue = wxCHK_CHECKED;
else if (value == wxT("FALSE"))
m_startValue = wxCHK_UNCHECKED;
else
m_startValue = wxCHK_UNDETERMINED;
CBox()->Set3StateValue(m_startValue);
CBox()->SetFocus();
}
#define BOOL_EDIT_SWITCH switch (value) \
{ \
case wxCHK_UNCHECKED:\
grid->GetTable()->SetValue(row, col, wxT("FALSE"));\
break;\
case wxCHK_CHECKED:\
grid->GetTable()->SetValue(row, col, wxT("TRUE"));\
break;\
case wxCHK_UNDETERMINED:\
grid->GetTable()->SetValue(row, col, wxEmptyString);\
break;\
}\
#if wxCHECK_VERSION(2, 9, 0)
// pure virtual in 2.9+, doesn't exist in prior versions
void sqlGridBoolEditor::ApplyEdit(int row, int col, wxGrid *grid)
{
wxCheckBoxState value = CBox()->Get3StateValue();
BOOL_EDIT_SWITCH
}
#endif
#if wxCHECK_VERSION(2, 9, 0)
bool sqlGridBoolEditor::EndEdit(int row, int col, const wxGrid *grid, const wxString &, wxString *)
#else
bool sqlGridBoolEditor::EndEdit(int row, int col, wxGrid *grid)
#endif
{
wxASSERT_MSG(m_control, wxT("The sqlGridBoolEditor must be Created first!"));
bool changed = false;
wxCheckBoxState value = CBox()->Get3StateValue();
if ( value != m_startValue )
changed = true;
#if !wxCHECK_VERSION(2, 9, 0)
if ( changed )
{
BOOL_EDIT_SWITCH
}
#endif
return changed;
}
void sqlGridBoolEditor::Reset()
{
wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be Created first!"));
CBox()->Set3StateValue(m_startValue);
}
void sqlGridBoolEditor::StartingClick()
{
// We used to cycle the value on click here but
// that can lead to odd behaviour of the cell.
// Without cycling here, the checkbox is displayed
// but the user must toggle the box itself - she
// cannot just keep clicking the cell.
}
bool sqlGridBoolEditor::IsAcceptedKey(wxKeyEvent &event)
{
if ( wxGridCellEditor::IsAcceptedKey(event) )
{
int keycode = event.GetKeyCode();
switch ( keycode )
{
case WXK_SPACE:
case '+':
case '-':
case 'n':
case 'N':
return true;
}
}
return false;
}
void sqlGridBoolEditor::StartingKey(wxKeyEvent &event)
{
int keycode = event.GetKeyCode();
wxCheckBoxState value = CBox()->Get3StateValue();
switch ( keycode )
{
case WXK_SPACE:
switch (value)
{
case wxCHK_UNCHECKED:
case wxCHK_UNDETERMINED:
CBox()->Set3StateValue(wxCHK_CHECKED);
break;
case wxCHK_CHECKED:
CBox()->Set3StateValue(wxCHK_UNCHECKED);
break;
}
break;
case '+':
CBox()->Set3StateValue(wxCHK_CHECKED);
break;
case '-':
CBox()->Set3StateValue(wxCHK_UNCHECKED);
break;
case 'n':
case 'N':
CBox()->Set3StateValue(wxCHK_UNDETERMINED);
break;
}
}
// return the value as "1" for true and the empty string for false
wxString sqlGridBoolEditor::GetValue() const
{
wxCheckBoxState value = CBox()->Get3StateValue();
switch (value)
{
case wxCHK_UNCHECKED:
return wxT("FALSE");
break;
case wxCHK_CHECKED:
return wxT("TRUE");
break;
case wxCHK_UNDETERMINED:
return wxEmptyString;
break;
}
return wxEmptyString;
}
//////////////////////////////////////////////////////////////////////
// End Bool editor
//////////////////////////////////////////////////////////////////////
sqlTable::sqlTable(pgConn *conn, pgQueryThread *_thread, const wxString &tabName, const OID _relid, bool _hasOid, const wxString &_pkCols, char _relkind)
{
connection = conn;
primaryKeyColNumbers = _pkCols;
relid = _relid;
relkind = _relkind;
tableName = tabName;
hasOids = _hasOid;
thread = _thread;
rowsCached = 0;
rowsAdded = 0;
rowsStored = 0;
rowsDeleted = 0;
dataPool = 0;
addPool = new cacheLinePool(500); // arbitrary initial size
lastRow = -1;
int i;
lineIndex = 0;
nRows = thread->DataSet()->NumRows();
nCols = thread->DataSet()->NumCols();
columns = new sqlCellAttr[nCols];
savedLine.cols = new wxString[nCols];
// Get the "real" column list, including any dropped columns, as
// key positions etc do not ignore these.
pgSet *allColsSet = connection->ExecuteSet(
wxT("SELECT attisdropped FROM pg_attribute")
wxT(" WHERE attnum > 0 AND attrelid=") + NumToStr(relid) + wxT("::oid\n")
wxT(" ORDER BY attnum"));
int x = 1;
if (allColsSet)
{
for (i = 0; i < allColsSet->NumRows(); i++)
{
if (allColsSet->GetVal(wxT("attisdropped")) == wxT("t"))
{
colMap.Add(0);
}
else
{
colMap.Add(x);
x++;
}
allColsSet->MoveNext();
}
delete allColsSet;
}
pgSet *colSet = connection->ExecuteSet(
wxT("SELECT n.nspname AS nspname, relname, format_type(t.oid,NULL) AS typname, format_type(t.oid, att.atttypmod) AS displaytypname, ")
wxT("nt.nspname AS typnspname, attname, attnum, COALESCE(b.oid, t.oid) AS basetype, atthasdef, pg_get_expr(def.adbin,0) AS adsrc,\n")
wxT(" CASE WHEN t.typbasetype::oid=0 THEN att.atttypmod else t.typtypmod END AS typmod,\n")
wxT(" CASE WHEN t.typbasetype::oid=0 THEN att.attlen else t.typlen END AS typlen\n")
wxT(" FROM pg_attribute att\n")
wxT(" JOIN pg_type t ON t.oid=att.atttypid\n")
wxT(" JOIN pg_namespace nt ON nt.oid=t.typnamespace\n")
wxT(" JOIN pg_class c ON c.oid=attrelid\n")
wxT(" JOIN pg_namespace n ON n.oid=relnamespace\n")
wxT(" LEFT OUTER JOIN pg_type b ON b.oid=t.typbasetype\n")
wxT(" LEFT OUTER JOIN pg_attrdef def ON adrelid=attrelid AND adnum=attnum\n")
wxT(" WHERE attnum > 0 AND NOT attisdropped AND attrelid=") + NumToStr(relid) + wxT("::oid\n")
wxT(" ORDER BY attnum"));
bool canInsert = false;
if (colSet)
{
if (hasOids)
{
columns[0].name = wxT("oid");
columns[0].numeric = true;
columns[0].attr->SetReadOnly(true);
columns[0].type = PGTYPCLASS_NUMERIC;
}
for (i = (hasOids ? 1 : 0) ; i < nCols ; i++)
{
wxGridCellEditor *editor = 0;
columns[i].name = colSet->GetVal(wxT("attname"));
columns[i].typeName = colSet->GetVal(wxT("typname"));
columns[i].displayTypeName = colSet->GetVal(wxT("displaytypname"));
// Special case for character datatypes. We always cast them to text to avoid
// truncation issues with casts like ::character(3)
if (columns[i].typeName == wxT("character") || columns[i].typeName == wxT("character varying") || columns[i].typeName == wxT("\"char\""))
columns[i].typeName = wxT("text");
columns[i].type = (Oid)colSet->GetOid(wxT("basetype"));
if ((columns[i].type == PGOID_TYPE_INT4 || columns[i].type == PGOID_TYPE_INT8 || columns[i].type == PGOID_TYPE_INT2)
&& colSet->GetBool(wxT("atthasdef")))
{
wxString adsrc = colSet->GetVal(wxT("adsrc"));
if (adsrc == wxT("nextval('") + colSet->GetVal(wxT("relname")) + wxT("_") + columns[i].name + wxT("_seq'::text)") ||
adsrc == wxT("nextval('") + colSet->GetVal(wxT("nspname")) + wxT(".") + colSet->GetVal(wxT("relname")) + wxT("_") + columns[i].name + wxT("_seq'::text)") ||
adsrc == wxT("nextval('") + colSet->GetVal(wxT("relname")) + wxT("_") + columns[i].name + wxT("_seq'::regclass)") ||
adsrc == wxT("nextval('") + colSet->GetVal(wxT("nspname")) + wxT(".") + colSet->GetVal(wxT("relname")) + wxT("_") + columns[i].name + wxT("_seq'::regclass)"))
{
if (columns[i].type == PGOID_TYPE_INT4)
columns[i].type = (Oid)PGOID_TYPE_SERIAL;
else if (columns[i].type == PGOID_TYPE_INT8)
columns[i].type = (Oid)PGOID_TYPE_SERIAL8;
else
columns[i].type = (Oid)PGOID_TYPE_SERIAL2;
}
}
columns[i].typlen = colSet->GetLong(wxT("typlen"));
columns[i].typmod = colSet->GetLong(wxT("typmod"));
switch (columns[i].type)
{
case PGOID_TYPE_BOOL:
columns[i].numeric = false;
columns[i].attr->SetReadOnly(false);
editor = new sqlGridBoolEditor();
break;
case PGOID_TYPE_INT8:
case PGOID_TYPE_SERIAL8:
SetNumberEditor(i, 20);
break;
case PGOID_TYPE_INT2:
case PGOID_TYPE_SERIAL2:
SetNumberEditor(i, 5);
break;
case PGOID_TYPE_INT4:
case PGOID_TYPE_SERIAL:
SetNumberEditor(i, 10);
break;
case PGOID_TYPE_OID:
case PGOID_TYPE_TID:
case PGOID_TYPE_XID:
case PGOID_TYPE_CID:
SetNumberEditor(i, 10);
break;
case PGOID_TYPE_FLOAT4:
case PGOID_TYPE_FLOAT8:
columns[i].numeric = true;
columns[i].attr->SetReadOnly(false);
editor = new sqlGridNumericEditor();
break;
case PGOID_TYPE_MONEY:
columns[i].numeric = false;
columns[i].attr->SetReadOnly(false);
editor = new wxGridCellTextEditor();
break;
case PGOID_TYPE_NUMERIC:
{
columns[i].numeric = true;
columns[i].attr->SetReadOnly(false);
int len = columns[i].size();
int prec = columns[i].precision();
if (prec > 0)
len -= (prec);
editor = new sqlGridNumericEditor(len, prec);
break;
}
case PGOID_TYPE_BYTEA:
columns[i].numeric = false;
columns[i].attr->SetReadOnly(true);
break;
case PGOID_TYPE_DATE:
case PGOID_TYPE_TIME:
case PGOID_TYPE_TIMETZ:
case PGOID_TYPE_TIMESTAMP:
case PGOID_TYPE_TIMESTAMPTZ:
case PGOID_TYPE_INTERVAL:
columns[i].numeric = false;
columns[i].attr->SetReadOnly(false);
editor = new sqlGridTextEditor();
break;
case PGOID_TYPE_CHAR:
case PGOID_TYPE_NAME:
case PGOID_TYPE_TEXT:
default:
columns[i].numeric = false;
columns[i].attr->SetReadOnly(false);
columns[i].needResize = true;
editor = new sqlGridTextEditor();
break;
}
if (editor)
columns[i].attr->SetEditor(editor);
if (relkind != 'r' || (!hasOids && primaryKeyColNumbers.IsNull()))
{
// for security reasons, we need oid or pk to enable updates. If none is available,
// the table definition can be considered faulty.
columns[i].attr->SetReadOnly(true);
}
if (!columns[i].attr->IsReadOnly())
canInsert = true;
wxStringTokenizer collist(primaryKeyColNumbers, wxT(","));
long cn = 0;
long attnum = colSet->GetLong(wxT("attnum"));
while (cn < attnum && collist.HasMoreTokens())
{
cn = StrToLong(collist.GetNextToken());
if (cn == attnum)
columns[i].isPrimaryKey = true;
}
colSet->MoveNext();
}
delete colSet;
}
else
{
// um, we never should reach here because this would mean
// that datatypes are unreadable.
// *if* we reach here, namespace info is missing.
for (i = 0 ; i < nCols ; i++)
{
columns[i].typeName = thread->DataSet()->ColType(i);
columns[i].name = thread->DataSet()->ColName(i);
}
}
if (nRows)
{
dataPool = new cacheLinePool(nRows);
lineIndex = new int[nRows];
for (i = 0 ; i < nRows ; i++)
lineIndex[i] = i;
}
if (canInsert)
{
// an empty line waiting for inserts
rowsAdded = 1;
}
}
sqlTable::~sqlTable()
{
if (thread)
delete thread;
if (dataPool)
delete dataPool;
delete addPool;
delete[] columns;
if (lineIndex)
delete[] lineIndex;
}
int sqlTable::GetNumberCols()
{
return nCols;
}
int sqlTable::GetNumberRows()
{
return nRows + rowsAdded - rowsDeleted;
}
int sqlTable::GetNumberStoredRows()
{
return nRows + rowsStored - rowsDeleted;
}
bool ctlSQLEditGrid::IsColText(int col)
{
return GetTable()->IsColText(col);
}
bool sqlTable::IsColText(int col)
{
return !columns[col].numeric && !(columns[col].type == PGOID_TYPE_BOOL);
}
bool sqlTable::IsColBoolean(int col)
{
return (columns[col].type == PGOID_TYPE_BOOL);
}
wxString sqlTable::GetColLabelValue(int col)
{
wxString label = columns[col].name + wxT("\n");
if (columns[col].isPrimaryKey)
label += wxT("[PK] ");
switch (columns[col].type)
{
case (Oid)PGOID_TYPE_SERIAL:
label += wxT("serial");
break;
case (Oid)PGOID_TYPE_SERIAL8:
label += wxT("bigserial");
break;
case (Oid)PGOID_TYPE_SERIAL2:
label += wxT("smallserial");
break;
default:
label += columns[col].displayTypeName;
break;
}
return label;
}
wxString sqlTable::GetColLabelValueUnformatted(int col)
{
return columns[col].name;
}
wxString sqlTable::GetRowLabelValue(int row)
{
wxString label;
if (row < nRows - rowsDeleted || GetLine(row)->stored)
label.Printf(wxT("%d"), row + 1);
else
label = wxT("*");
return label;
}
void sqlTable::SetNumberEditor(int col, int len)
{
columns[col].numeric = true;
columns[col].attr->SetReadOnly(false);
columns[col].attr->SetEditor(new sqlGridNumericEditor(len, 0));
}
bool sqlTable::CheckInCache(int row)
{
if (row > nRows - rowsDeleted + rowsAdded)
return false;
if (row >= nRows - rowsDeleted)
return true;
return dataPool->IsFilled(row);
}
cacheLine *sqlTable::GetLine(int row)
{
cacheLine *line;
if (row < nRows - rowsDeleted)
line = dataPool->Get(lineIndex[row]);
else
line = addPool->Get(row - (nRows - rowsDeleted));
return line;
}
wxString sqlTable::MakeKey(cacheLine *line)
{
wxString whereClause;
if (!primaryKeyColNumbers.IsEmpty())
{
wxStringTokenizer collist(primaryKeyColNumbers, wxT(","));
long cn;
int offset;
if (hasOids)
offset = 0;
else
offset = 1;
while (collist.HasMoreTokens())
{
cn = StrToLong(collist.GetNextToken());
// Translate the column location to the real location in the actual columns still present
cn = colMap[cn - 1];
wxString colval = line->cols[cn - offset];
if (colval.IsEmpty())
return wxEmptyString;
if (colval == wxT("''") && columns[cn - offset].typeName == wxT("text"))
colval = wxEmptyString;
if (!whereClause.IsEmpty())
whereClause += wxT(" AND ");
whereClause += qtIdent(columns[cn - offset].name) + wxT(" = ") + connection->qtDbString(colval);
if (columns[cn - offset].typeName != wxT(""))
{
whereClause += wxT("::");
whereClause += columns[cn - offset].displayTypeName;
}
}
}
else if (hasOids)
whereClause = wxT("oid = ") + line->cols[0];
return whereClause;
}
void sqlTable::UndoLine(int row)
{
if (lastRow >= 0 && row >= 0)
{
cacheLine *line = GetLine(row);
if (line)
{
int i;
for (i = 0 ; i < nCols ; i++)
line->cols[i] = savedLine.cols[i];
ctlMenuToolbar *tb = (ctlMenuToolbar *)((wxFrame *)GetView()->GetParent())->GetToolBar();
if (tb)
{
tb->EnableTool(MNU_SAVE, false);
tb->EnableTool(MNU_UNDO, false);
}
wxMenu *fm = ((frmEditGrid *)GetView()->GetParent())->GetFileMenu();
if (fm)
fm->Enable(MNU_SAVE, false);
wxMenu *em = ((frmEditGrid *)GetView()->GetParent())->GetEditMenu();
if (em)
em->Enable(MNU_UNDO, false);
}
}
lastRow = -1;
}
bool sqlTable::StoreLine()
{
bool done = false;
GetView()->BeginBatch();
if (lastRow >= 0)
{
cacheLine *line = GetLine(lastRow);
int i;
wxString colList, valList;
if (line->stored)
{
// UPDATE
for (i = (hasOids ? 1 : 0) ; i < nCols ; i++)
{
if (savedLine.cols[i] != line->cols[i])
{
if (!valList.IsNull())
valList += wxT(", ");
valList += qtIdent(columns[i].name) + wxT("=") + columns[i].Quote(connection, line->cols[i]);
}
}
if (valList.IsEmpty())
done = true;
else
{
wxString key = MakeKey(&savedLine);
wxASSERT(!key.IsEmpty());
done = connection->ExecuteVoid(wxT(
"UPDATE ") + tableName + wxT(
" SET ") + valList + wxT(
" WHERE ") + key);
}
}
else
{
// INSERT
for (i = 0 ; i < nCols ; i++)
{
if (!columns[i].attr->IsReadOnly() && !line->cols[i].IsEmpty())
{
if (!colList.IsNull())
{
valList += wxT(", ");
colList += wxT(", ");
}
colList += qtIdent(columns[i].name);
valList += columns[i].Quote(connection, line->cols[i]);
}
}
if (!valList.IsEmpty())
{
pgSet *set = connection->ExecuteSet(
wxT("INSERT INTO ") + tableName
+ wxT("(") + colList
+ wxT(") VALUES (") + valList
+ wxT(")"));
if (set)
{
if (set->GetInsertedCount() > 0)
{
if (hasOids)
line->cols[0] = NumToStr((long)set->GetInsertedOid());
delete set;
done = true;
rowsStored++;
((wxFrame *)GetView()->GetParent())->SetStatusText(wxString::Format(wxT("%d rows."), GetNumberStoredRows()));
if (rowsAdded == rowsStored)
GetView()->AppendRows();
// Read back what we inserted to get default vals
wxString key = MakeKey(line);
if (key.IsEmpty())
{
// That's a problem: obviously, the key generated isn't present
// because it's serial or default or otherwise generated in the backend
// we don't get.
// That's why the whole line is declared readonly.
line->readOnly = true;
}
else
{
set = connection->ExecuteSet(
wxT("SELECT * FROM ") + tableName +
wxT(" WHERE ") + key);
if (set)
{
for (i = (hasOids ? 1 : 0) ; i < nCols ; i++)
{
line->cols[i] = set->GetVal(columns[i].name);
}
delete set;
}
}
}
}
}
}
if (done)
{
line->stored = true;
lastRow = -1;
}
else
GetView()->SelectRow(lastRow);
}
GetView()->EndBatch();
return done;
}
void sqlTable::SetValue(int row, int col, const wxString &value)
{
cacheLine *line = GetLine(row);
if (!line)
{
// Bad problem, no line!
return;
}
if (row != lastRow)
{
if (lastRow >= 0)
StoreLine();
if (!line->cols)
line->cols = new wxString[nCols];
// remember line contents for later reference in update ... where
int i;
for (i = 0 ; i < nCols ; i++)
savedLine.cols[i] = line->cols[i];
lastRow = row;
}
ctlMenuToolbar *tb = (ctlMenuToolbar *)((wxFrame *)GetView()->GetParent())->GetToolBar();
if (tb)
{
tb->EnableTool(MNU_SAVE, true);
tb->EnableTool(MNU_UNDO, true);
}
wxMenu *fm = ((frmEditGrid *)GetView()->GetParent())->GetFileMenu();
if (fm)
fm->Enable(MNU_SAVE, true);
wxMenu *em = ((frmEditGrid *)GetView()->GetParent())->GetEditMenu();
if (em)
em->Enable(MNU_UNDO, true);
line->cols[col] = value;
}
wxString sqlTable::GetValue(int row, int col)
{
wxString val;
cacheLine *line;
if (row < nRows - rowsDeleted)
line = dataPool->Get(lineIndex[row]);
else
line = addPool->Get(row - (nRows - rowsDeleted));
if (!line)
{
// Bad problem, no line!
return val;
}
if (!line->cols)
{
line->cols = new wxString[nCols];
if (row < nRows - rowsDeleted)
{
if (!thread)
{
wxLogError(__("Unexpected empty cache line: dataSet already closed."));
return val;
}
line->stored = true;
if (lineIndex[row] != thread->DataSet()->CurrentPos() - 1)
thread->DataSet()->Locate(lineIndex[row] + 1);
int i;
for (i = 0 ; i < nCols ; i++)
{
wxString val;
if (thread->DataSet()->ColType(i) == wxT("bytea"))
val = _("<binary data>");
else
{
val = thread->DataSet()->GetVal(i);
if (val.IsEmpty())
{
if (!thread->DataSet()->IsNull(i))
val = wxT("''");
}
else if (val == wxT("''"))
val = wxT("\\'\\'");
}
line->cols[i] = val;
}
rowsCached++;
if (rowsCached == nRows)
{
delete thread;
thread = 0;
}
}
}
if (columns[col].type == PGOID_TYPE_BOOL)
{
if (line->cols[col] != wxEmptyString)
line->cols[col] = (StrToBool(line->cols[col]) ? wxT("TRUE") : wxT("FALSE"));
}
val = line->cols[col];
return val;
}
bool sqlTable::AppendRows(size_t rows)
{
rowsAdded += rows;
GetLine(nRows + rowsAdded - rowsDeleted - 1);
wxGridTableMessage msg(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, rows);
GetView()->ProcessTableMessage(msg);
return true;
}
bool sqlTable::DeleteRows(size_t pos, size_t rows)
{
size_t i = pos;
size_t rowsDone = 0;
while (i < pos + rows)
{
cacheLine *line = GetLine(pos);
if (!line)
break;
// If line->cols is null, it probably means we need to force the cacheline to be populated.
if (!line->cols)
{
GetValue(pos, 0);
line = GetLine(pos);
}
if (line->stored)
{
wxString key = MakeKey(line);
wxASSERT(!key.IsEmpty());
bool done = connection->ExecuteVoid(wxT(
"DELETE FROM ") + tableName + wxT(" WHERE ") + key);
if (!done)
break;
if ((int)pos < nRows - rowsDeleted)
{
rowsDeleted++;
if ((int)pos < nRows - rowsDeleted)
memmove(lineIndex + pos, lineIndex + pos + 1, sizeof(*lineIndex) * (nRows - rowsDeleted - pos));
}
else
{
rowsAdded--;
if (GetLine(pos)->stored)
rowsStored--;
addPool->Delete(pos - (nRows - rowsDeleted));
}
rowsDone++;
}
else
{
// last empty line won't be deleted, just cleared
int j;
for (j = 0 ; j < nCols ; j++)
line->cols[j] = wxT("");
}
i++;
}
if (rowsDone > 0 && GetView())
{
wxGridTableMessage msg(this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, pos, rowsDone);
GetView()->ProcessTableMessage(msg);
}
return (rowsDone != 0);
}
bool sqlTable::Paste()
{
int row, col;
int start, pos, len;
wxArrayString data;
wxString text, quoteChar, colSep;
bool inQuotes, inData, skipSerial;
if (!this)
return false;
if (wxTheClipboard->Open())
{
if (wxTheClipboard->IsSupported(wxDF_TEXT))
{
wxTextDataObject textData;
wxTheClipboard->GetData(textData);
text = textData.GetText();
}
else
{
wxTheClipboard->Close();
return false;
}
wxTheClipboard->Close();
}
else
{
return false;
}
start = pos = 0;
len = text.Len();
quoteChar = settings->GetCopyQuoteChar();
colSep = settings->GetCopyColSeparator();
inQuotes = inData = false;
while (pos < len && !(text[pos] == '\n' && !inQuotes))
{
if (!inData)
{
if (text[pos] == quoteChar)
{
inQuotes = inData = true;
pos++;
start++;
continue;
}
else
{
inQuotes = false;
}
inData = true;
}
if (inQuotes && text[pos] == quoteChar &&
(text[pos + 1] == colSep || text[pos + 1] == '\r' || text[pos + 1] == '\n'))
{
data.Add(text.Mid(start, pos - start));
start = (pos += 2);
inData = false;
}
else if (!inQuotes && text[pos] == colSep)
{
data.Add(text.Mid(start, pos - start));
start = ++pos;
inData = false;
}
else
{
pos++;
}
}
if (start < pos)
{
if (inQuotes && text[pos - 1] == quoteChar)
data.Add(text.Mid(start, pos - start - 1));
else
data.Add(text.Mid(start, pos - start));
}
row = GetNumberRows() - 1;
skipSerial = false;
for (col = 0; col < nCols; col++)
{
if (columns[col].type == (unsigned int)PGOID_TYPE_SERIAL ||
columns[col].type == (unsigned int)PGOID_TYPE_SERIAL8 ||
columns[col].type == (unsigned int)PGOID_TYPE_SERIAL2)
{
wxMessageDialog msg(GetView()->GetParent(),
_("This table contains serial columns. Do you want to use the values in the clipboard for these columns?"),
_("Paste Data"), wxYES_NO | wxICON_QUESTION);
if (msg.ShowModal() != wxID_YES)
{
skipSerial = true;
}
break;
}
}
bool pasted = false;
for (col = (hasOids ? 1 : 0); col < nCols && col < (int)data.GetCount(); col++)
{
if (!(skipSerial && (columns[col].type == (unsigned int)PGOID_TYPE_SERIAL ||
columns[col].type == (unsigned int)PGOID_TYPE_SERIAL8 ||
columns[col].type == (unsigned int)PGOID_TYPE_SERIAL2)))
{
SetValue(row, col, data.Item(col));
GetView()->SetGridCursor(row, col);
GetView()->MakeCellVisible(row, col);
pasted = true;
}
}
GetView()->ForceRefresh();
return pasted;
}
wxGridCellAttr *sqlTable::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind)
{
cacheLine *line = GetLine(row);
if (line && line->readOnly)
{
wxGridCellAttr *attr = new wxGridCellAttr(columns[col].attr);
attr->SetReadOnly();
return attr;
}
else
{
columns[col].attr->IncRef();
return columns[col].attr;
}
}
wxString sqlCellAttr::Quote(pgConn *conn, const wxString &value)
{
wxString str;
if (value.IsEmpty())
str = wxT("NULL");
else if (numeric)
str = conn->qtDbString(value);
else if (value == wxT("\\'\\'"))
str = conn->qtDbString(wxT("''"));
else if (value == wxT("''"))
str = wxT("''");
else if (type == PGOID_TYPE_BOOL)
str = value;
else if (type == PGOID_TYPE_BIT)
// Don't cast this one
return wxT("B'") + value + wxT("'");
else
str = conn->qtDbString(value);
return str + wxT("::") + displayTypeName;
}
int sqlCellAttr::size()
{
if (typlen == -1 && typmod > 0)
{
return (typmod - 4) >> 16;
}
else
return typlen;
}
int sqlCellAttr::precision()
{
if (typlen == -1 && typmod > 0)
{
return (typmod - 4) & 0x7fff;
}
else
return -1;
}
cacheLinePool::cacheLinePool(int initialLines)
{
ptr = new cacheLine*[initialLines];
if (ptr)
{
anzLines = initialLines;
memset(ptr, 0, sizeof(cacheLine *)*anzLines);
}
else
{
anzLines = 0;
wxLogError(__("Out of Memory for cacheLinePool"));
}
}
cacheLinePool::~cacheLinePool()
{
if (ptr)
{
while (anzLines--)
{
if (ptr[anzLines])
delete ptr[anzLines];
}
delete[] ptr;
}
}
void cacheLinePool::Delete(int lineNo)
{
if (ptr && lineNo >= 0 && lineNo < anzLines)
{
#if 1
if (ptr[lineNo])
delete ptr[lineNo];
if (lineNo < anzLines - 1)
{
// beware: overlapping copy
memmove(ptr + lineNo, ptr + lineNo + 1, sizeof(cacheLine *) * (anzLines - lineNo - 1));
}
#else
cacheLine *c;
c = ptr[0];
ptr[0] = ptr[1];
ptr[1] = c;
#endif
ptr[anzLines - 1] = 0;
}
}
cacheLine *cacheLinePool::Get(int lineNo)
{
if (lineNo < 0) return 0;
if (lineNo >= anzLines)
{
cacheLine **old = ptr;
int oldAnz = anzLines;
anzLines = lineNo + 100;
ptr = new cacheLine*[anzLines];
if (!ptr)
{
anzLines = 0;
wxLogError(__("Out of Memory for cacheLinePool"));
}
else
{
if (oldAnz)
{
memcpy(ptr, old, sizeof(cacheLine *)*oldAnz);
delete[] old;
}
memset(ptr + oldAnz, 0, anzLines - oldAnz);
}
}
if (lineNo < anzLines)
{
if (!ptr[lineNo])
ptr[lineNo] = new cacheLine();
return ptr[lineNo];
}
return 0;
}
bool cacheLinePool::IsFilled(int lineNo)
{
return (lineNo < anzLines && ptr[lineNo]);
}
bool editGridFactoryBase::CheckEnable(pgObject *obj)
{
if (obj)
{
pgaFactory *factory = obj->GetFactory();
return factory == &pg_partitionFactory || factory == &tableFactory || factory == &foreignTableFactory || factory == &viewFactory || factory == &extTableFactory || factory == &catalogObjectFactory;
}
return false;
}
wxWindow *editGridFactoryBase::ViewData(frmMain *form, pgObject *obj, bool filter)
{
pgDatabase *db = ((pgSchemaObject *)obj)->GetDatabase();
wxString applicationname = appearanceFactory->GetLongAppName() + _(" - Edit Grid");
pgServer *server = db->GetServer();
pgConn *conn = db->CreateConn(applicationname);
if (conn)
{
wxString txt = _("Edit Data - ")
+ server->GetDescription()
+ wxT(" (") + server->GetName()
+ wxT(":") + NumToStr((long)server->GetPort())
+ wxT(") - ") + db->GetName()
+ wxT(" - ") + obj->GetFullIdentifier();
frmEditGrid *eg = new frmEditGrid(form, txt, conn, (pgSchemaObject *)obj, pkAscending);
eg->SetLimit(rowlimit);
eg->ShowForm(filter);
return eg;
}
return 0;
}
editGridFactory::editGridFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : editGridFactoryBase(list)
{
mnu->Append(id, _("View &All Rows\tCtrl-D"), _("View the data in the selected object."));
toolbar->AddTool(id, _("View All Rows\tCtrl-D"), *viewdata_png_bmp, _("View the data in the selected object."), wxITEM_NORMAL);
context = false;
}
wxWindow *editGridFactory::StartDialog(frmMain *form, pgObject *obj)
{
return ViewData(form, obj, false);
}
#include "images/viewfiltereddata.pngc"
editGridFilteredFactory::editGridFilteredFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : editGridFactoryBase(list)
{
mnu->Append(id, _("View F&iltered Rows...\tCtrl-G"), _("Apply a filter and view the data in the selected object."));
toolbar->AddTool(id, _("View Filtered Rows\tCtrl-G"), *viewfiltereddata_png_bmp, _("Apply a filter and view the data in the selected object."), wxITEM_NORMAL);
context = false;
}
wxWindow *editGridFilteredFactory::StartDialog(frmMain *form, pgObject *obj)
{
return ViewData(form, obj, true);
}
editGridLimitedFactory::editGridLimitedFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar, int limit, bool ascending) : editGridFactoryBase(list)
{
if (ascending)
mnu->Append(id, wxString::Format(wxPLURAL("View Top %i Row", "View Top %i Rows", limit), limit), _("View a limited number of rows in the selected object."));
else
mnu->Append(id, wxString::Format(wxPLURAL("View Last %i Row", "View Last %i Rows", limit), limit), _("View a limited number of rows in the selected object."));
pkAscending = ascending;
rowlimit = limit;
context = false;
}
wxWindow *editGridLimitedFactory::StartDialog(frmMain *form, pgObject *obj)
{
return ViewData(form, obj, false);
}