mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 06:05:49 -06:00
Выполнен переход на wxWidgets 3.2 для улучшения поддержки DPI. Также обновлена среда разработки до Microsoft Visual Studio 2022.
3386 lines
82 KiB
C++
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 ¶ms);
|
|
|
|
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 ¶ms)
|
|
{
|
|
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"), *GetBundleSVG(viewdata_png_bmp, "viewdata.svg", wxSize(32, 32)), _("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"), *GetBundleSVG(viewfiltereddata_png_bmp, "viewfiltereddata.svg", wxSize(32, 32)) , _("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);
|
|
}
|