1
//////////////////////////////////////////////////////////////////////////
3
// pgAdmin III - PostgreSQL Tools
4
// RCS-ID: $Id: frmEditGrid.cpp 5108 2006-05-02 07:19:22Z dpage $
5
// Copyright (C) 2002 - 2006, The pgAdmin Development Team
6
// This software is released under the Artistic Licence
8
// frmEditGrid.cpp - Edit Grid Box
10
//////////////////////////////////////////////////////////////////////////
16
#ifdef __WX_FULLSOURCE
17
#include "wx/generic/gridsel.h"
19
#include "wxgridsel.h"
28
#include <wx/generic/gridctrl.h>
29
#include <wx/clipbrd.h>
31
#include "frmEditGrid.h"
32
#include "dlgEditGridOptions.h"
39
#include "images/viewdata.xpm"
40
#include "images/storedata.xpm"
41
#include "images/readdata.xpm"
42
#include "images/delete.xpm"
43
#include "images/edit_undo.xpm"
44
#include "images/sortfilter.xpm"
45
#include "images/help.xpm"
46
#include "images/clip_copy.xpm"
50
BEGIN_EVENT_TABLE(frmEditGrid, pgFrame)
51
EVT_MENU(MNU_REFRESH, frmEditGrid::OnRefresh)
52
EVT_MENU(MNU_DELETE, frmEditGrid::OnDelete)
53
EVT_MENU(MNU_SAVE, frmEditGrid::OnSave)
54
EVT_MENU(MNU_UNDO, frmEditGrid::OnUndo)
55
EVT_MENU(MNU_OPTIONS, frmEditGrid::OnOptions)
56
EVT_MENU(MNU_HELP, frmEditGrid::OnHelp)
57
EVT_MENU(MNU_COPY, frmEditGrid::OnCopy)
58
EVT_CLOSE( frmEditGrid::OnClose)
59
EVT_KEY_DOWN( frmEditGrid::OnKey)
60
EVT_GRID_RANGE_SELECT(frmEditGrid::OnGridSelectCells)
61
EVT_GRID_SELECT_CELL(frmEditGrid::OnCellChange)
62
EVT_GRID_EDITOR_SHOWN(frmEditGrid::OnEditorShown)
63
EVT_GRID_LABEL_LEFT_DCLICK(frmEditGrid::OnLabelDoubleClick)
64
EVT_GRID_LABEL_RIGHT_CLICK(frmEditGrid::OnLabelRightClick)
68
frmEditGrid::frmEditGrid(frmMain *form, const wxString& _title, pgConn *_conn, pgSchemaObject *obj)
69
: pgFrame(NULL, _title)
71
wxLogInfo(wxT("Creating EditGrid"));
72
SetIcon(wxIcon(viewdata_xpm));
73
wxWindowBase::SetFont(settings->GetSystemFont());
74
dlgName = wxT("frmEditGrid");
75
RestorePosition(-1, -1, 600, 500, 200, 150);
80
relid=(Oid)obj->GetOid();
86
sqlGrid = new ctlSQLGrid(this, CTL_EDITGRID, wxDefaultPosition, wxDefaultSize);
87
sqlGrid->SetSizer(new wxBoxSizer(wxVERTICAL));
89
wxFont fntLabel(settings->GetSystemFont());
90
fntLabel.SetWeight(wxBOLD);
91
sqlGrid->SetLabelFont(fntLabel);
94
toolBar = CreateToolBar();
97
toolBar->SetToolBitmapSize(wxSize(16, 16));
99
toolBar->AddTool(MNU_SAVE, _("Save"), wxBitmap(storedata_xpm), _("Saved the changed row."), wxITEM_NORMAL);
100
toolBar->AddSeparator();
101
toolBar->AddTool(MNU_REFRESH, _("Refresh"), wxBitmap(readdata_xpm), _("Refresh"), wxITEM_NORMAL);
102
toolBar->AddTool(MNU_UNDO, _("Undo"), wxBitmap(edit_undo_xpm), _("Undo change of data."), wxITEM_NORMAL);
103
toolBar->AddSeparator();
104
toolBar->AddTool(MNU_COPY, _("Copy"), wxBitmap(clip_copy_xpm), _("Copy selected lines to clipboard"), wxITEM_NORMAL);
105
toolBar->AddSeparator();
106
toolBar->AddTool(MNU_DELETE, _("Delete"), wxBitmap(delete_xpm), _("Delete selected lines."), wxITEM_NORMAL);
107
toolBar->AddSeparator();
108
toolBar->AddTool(MNU_OPTIONS, _("Options"), wxBitmap(sortfilter_xpm), _("Sort/filter options."), wxITEM_NORMAL);
109
toolBar->AddSeparator();
110
toolBar->AddTool(MNU_HELP, _("Help"), wxBitmap(help_xpm), _("Display help on SQL commands."));
113
toolBar->EnableTool(MNU_SAVE, false);
114
toolBar->EnableTool(MNU_UNDO, false);
115
toolBar->EnableTool(MNU_COPY, false);
116
toolBar->EnableTool(MNU_DELETE, false);
119
wxAcceleratorEntry entries[6];
121
entries[0].Set(wxACCEL_CTRL, (int)'S', MNU_SAVE);
122
entries[1].Set(wxACCEL_NORMAL, WXK_F5, MNU_REFRESH);
123
entries[2].Set(wxACCEL_CTRL, (int)'Z', MNU_UNDO);
124
entries[3].Set(wxACCEL_NORMAL, WXK_F1, MNU_HELP);
125
entries[4].Set(wxACCEL_CTRL, (int)'C', MNU_COPY);
126
entries[5].Set(wxACCEL_NORMAL, WXK_DELETE, MNU_DELETE);
128
wxAcceleratorTable accel(6, entries);
129
SetAcceleratorTable(accel);
132
if (obj->GetMetaType() == PGM_TABLE)
134
pgTable *table = (pgTable*)obj;
137
hasOids = table->GetHasOids();
138
tableName = table->GetQuotedFullIdentifier();
139
primaryKeyColNumbers = table->GetPrimaryKeyColNumbers();
140
orderBy = table->GetQuotedPrimaryKey();
141
if (orderBy.IsEmpty() && hasOids)
143
if (!orderBy.IsEmpty())
144
orderBy += wxT(" ASC");
146
else if (obj->IsCreatedBy(viewFactory))
148
pgView *view=(pgView*)obj;
152
tableName=view->GetQuotedFullIdentifier();
156
void frmEditGrid::SetSortCols(const wxString &cols)
158
if (orderBy != cols) {
160
optionsChanged = true;
164
void frmEditGrid::SetFilter(const wxString &filter)
166
if (rowFilter != filter) {
168
optionsChanged = true;
172
void frmEditGrid::OnLabelRightClick(wxGridEvent& event)
174
wxArrayInt rows=sqlGrid->GetSelectedRows();
180
#define EXTRAEXTENT_HEIGHT 6
181
#define EXTRAEXTENT_WIDTH 6
183
void frmEditGrid::OnLabelDoubleClick(wxGridEvent& event)
185
#if wxCHECK_VERSION(2, 5, 0)
186
// at the moment, not implemented for 2.4
187
int maxHeight, maxWidth;
188
sqlGrid->GetClientSize(&maxWidth, &maxHeight);
189
int row=event.GetRow();
190
int col=event.GetCol();
192
int extent, extentWant=0;
196
for (col=0 ; col < sqlGrid->GetNumberCols() ; col++)
198
extent = sqlGrid->GetBestSize(row, col).GetHeight();
199
if (extent > extentWant)
203
extentWant += EXTRAEXTENT_HEIGHT;
204
extentWant = wxMax(extentWant, sqlGrid->GetRowMinimalAcceptableHeight());
205
extentWant = wxMin(extentWant, maxHeight*3/4);
206
int currentHeight=sqlGrid->GetRowHeight(row);
208
if (currentHeight >= maxHeight*3/4 || currentHeight == extentWant)
209
extentWant = sqlGrid->GetRowMinimalAcceptableHeight();
210
else if (currentHeight < maxHeight/4)
211
extentWant = wxMin(maxHeight/4, extentWant);
212
else if (currentHeight < maxHeight/2)
213
extentWant = wxMin(maxHeight/2, extentWant);
214
else if (currentHeight < maxHeight*3/4)
215
extentWant = wxMin(maxHeight*3/4, extentWant);
217
if (extentWant != currentHeight)
219
sqlGrid->BeginBatch();
220
if(sqlGrid->IsCellEditControlShown())
222
sqlGrid->HideCellEditControl();
223
sqlGrid->SaveEditControlValue();
226
sqlGrid->SetRowHeight(row, extentWant);
232
for (row=0 ; row < sqlGrid->GetNumberRows() ; row++)
234
if (sqlGrid->GetTable()->CheckInCache(row))
236
extent = sqlGrid->GetBestSize(row, col).GetWidth();
237
if (extent > extentWant)
242
extentWant += EXTRAEXTENT_WIDTH;
243
extentWant = wxMax(extentWant, sqlGrid->GetColMinimalAcceptableWidth());
244
extentWant = wxMin(extentWant, maxWidth*3/4);
245
int currentWidth=sqlGrid->GetColumnWidth(col);
247
if (currentWidth >= maxWidth*3/4 || currentWidth == extentWant)
248
extentWant = sqlGrid->GetColMinimalAcceptableWidth();
249
else if (currentWidth < maxWidth/4)
250
extentWant = wxMin(maxWidth/4, extentWant);
251
else if (currentWidth < maxWidth/2)
252
extentWant = wxMin(maxWidth/2, extentWant);
253
else if (currentWidth < maxWidth*3/4)
254
extentWant = wxMin(maxWidth*3/4, extentWant);
256
if (extentWant != currentWidth)
258
sqlGrid->BeginBatch();
259
if(sqlGrid->IsCellEditControlShown())
261
sqlGrid->HideCellEditControl();
262
sqlGrid->SaveEditControlValue();
264
sqlGrid->SetColumnWidth(col, extentWant);
273
void frmEditGrid::OnCellChange(wxGridEvent& event)
275
sqlTable *table=sqlGrid->GetTable();
278
if (table->LastRow() >= 0)
280
if (table->LastRow() != event.GetRow())
288
toolBar->EnableTool(MNU_SAVE, false);
289
toolBar->EnableTool(MNU_UNDO, false);
297
void frmEditGrid::OnCopy(wxCommandEvent &ev)
299
wxArrayInt rows=sqlGrid->GetSelectedRows();
304
for (i=0 ; i < rows.GetCount() ; i++)
306
str.Append(sqlGrid->GetTable()->GetExportLine(rows.Item(i)));
308
if (rows.GetCount() > 1)
309
str.Append(END_OF_LINE);
311
if (wxTheClipboard->Open())
313
wxTheClipboard->SetData(new wxTextDataObject(str));
314
wxTheClipboard->Close();
317
SetStatusText(wxString::Format(_("%d rows copied to clipboard."), rows.GetCount()));
321
void frmEditGrid::OnHelp(wxCommandEvent &ev)
323
DisplayHelp(this, wxT("editgrid"), viewdata_xpm);
327
void frmEditGrid::OnKey(wxKeyEvent &event)
329
int curcol=sqlGrid->GetGridCursorCol();
330
int currow=sqlGrid->GetGridCursorRow();
331
int keycode=event.GetKeyCode();
337
// the control will catch these :-(
341
sqlGrid->SetGridCursor(currow-1, curcol);
345
sqlGrid->SetGridCursor(currow+1, curcol);
350
if (!sqlGrid->IsCurrentCellReadOnly())
352
sqlGrid->EnableCellEditControl();
353
sqlGrid->ShowCellEditControl();
355
wxGridCellEditor *edit=sqlGrid->GetCellEditor(currow, curcol);
358
wxControl *ctl=edit->GetControl();
361
wxTextCtrl *txt=wxDynamicCast(ctl, wxTextCtrl);
363
txt->SetValue(wxEmptyString);
371
// check for shift etc.
372
if (event.ControlDown() || event.ShiftDown())
374
// Inject a RETURN into the control
375
wxGridCellEditor *edit=sqlGrid->GetCellEditor(currow, curcol);
378
wxControl *ctl=edit->GetControl();
381
wxTextCtrl *txt=wxDynamicCast(ctl, wxTextCtrl);
382
// if (txt && txt->IsMultiLine())
383
if (txt) // && txt->IsMultiLine())
386
txt->GetSelection(&from, &to);
387
txt->Replace(from, to, END_OF_LINE);
396
if (curcol == sqlGrid->GetNumberCols()-1)
400
// locate first editable column
401
while (sqlGrid->IsReadOnly(currow, curcol) && curcol < sqlGrid->GetNumberCols())
403
// next line is completely read-only
404
if (curcol == sqlGrid->GetNumberCols())
412
sqlGrid->SetGridCursor(currow, curcol);
417
if (sqlGrid->IsEditable() && keycode >= WXK_SPACE && keycode < WXK_START)
419
if (sqlGrid->IsCurrentCellReadOnly())
422
toolBar->EnableTool(MNU_SAVE, true);
423
toolBar->EnableTool(MNU_UNDO, true);
431
void frmEditGrid::OnClose(wxCloseEvent& event)
433
if (toolBar->GetToolEnabled(MNU_SAVE))
435
int flag=wxYES_NO | wxICON_QUESTION;
439
wxMessageDialog msg(this, _("There is unsaved data in a row.\nDo you want to store to the database?"), _("Unsaved data"),
441
switch (msg.ShowModal())
459
void frmEditGrid::OnUndo(wxCommandEvent& event)
461
sqlGrid->DisableCellEditControl();
462
sqlGrid->GetTable()->UndoLine(sqlGrid->GetGridCursorRow());
463
sqlGrid->ForceRefresh();
467
void frmEditGrid::OnRefresh(wxCommandEvent& event)
469
sqlGrid->DisableCellEditControl();
474
void frmEditGrid::OnSave(wxCommandEvent& event)
476
sqlGrid->HideCellEditControl();
477
sqlGrid->SaveEditControlValue();
478
sqlGrid->DisableCellEditControl();
479
sqlGrid->GetTable()->StoreLine();
481
toolBar->EnableTool(MNU_SAVE, false);
482
toolBar->EnableTool(MNU_UNDO, false);
485
void frmEditGrid::OnOptions(wxCommandEvent& event)
487
optionsChanged = false;
488
dlgEditGridOptions *winOptions = new dlgEditGridOptions(this, connection, tableName, sqlGrid);
489
winOptions->ShowModal();
491
if (optionsChanged) Go();
495
int ArrayCmp(T *a, T *b)
506
void frmEditGrid::OnDelete(wxCommandEvent& event)
508
wxMessageDialog msg(this, _("Are you sure you wish to delete the selected row(s)?"), _("Delete rows?"), wxYES_NO | wxICON_QUESTION);
509
if (msg.ShowModal() != wxID_YES)
512
sqlGrid->BeginBatch();
513
wxArrayInt delrows=sqlGrid->GetSelectedRows();
514
int i=delrows.GetCount();
516
// Sort the grid so we always delete last->first, otherwise we
517
// could end up deleting anything because the array returned by
518
// GetSelectedRows is in the order that rows were selected by
520
delrows.Sort(ArrayCmp);
522
// don't care a lot about optimizing here; doing it line by line
523
// just as sqlTable::DeleteRows does
525
sqlGrid->DeleteRows(delrows.Item(i), 1);
530
SetStatusText(wxString::Format(_("%d rows."), sqlGrid->GetTable()->GetNumberStoredRows()), 0);
534
void frmEditGrid::OnEditorShown(wxGridEvent& event)
536
toolBar->EnableTool(MNU_SAVE, true);
537
toolBar->EnableTool(MNU_UNDO, true);
543
void frmEditGrid::OnGridSelectCells(wxGridRangeSelectEvent& event)
545
if (sqlGrid->GetEditable())
547
wxArrayInt rows=sqlGrid->GetSelectedRows();
549
bool enable=rows.GetCount() > 0;
552
wxCommandEvent nullEvent;
555
// check if a readonly line is selected
559
for (i=0 ; i < rows.GetCount() ; i++)
562
bool lineEnabled=false;
564
if (row == sqlGrid->GetNumberRows()-1)
566
// the (*) line may not be deleted/copied
570
for (col = 0 ; col < sqlGrid->GetNumberCols() ; col++)
572
if (!sqlGrid->IsReadOnly(row, col))
586
toolBar->EnableTool(MNU_DELETE, enable);
587
toolBar->EnableTool(MNU_COPY, enable);
593
void frmEditGrid::ShowForm(bool filter)
597
if (relkind == 'r' || relkind == 'v')
601
dlgEditGridOptions *winOptions = new dlgEditGridOptions(this, connection, tableName, sqlGrid);
602
abort = !(winOptions->ShowModal());
614
wxLogError(__("No Table or view."));
620
void frmEditGrid::Go()
622
SetStatusText(_("Refreshing data, please wait."), 0);
624
wxString qry=wxT("SELECT ");
627
qry += wxT("* FROM ") + tableName;
628
if (!rowFilter.IsEmpty())
630
qry += wxT(" WHERE ") + rowFilter;
632
if (!orderBy.IsEmpty())
634
qry += wxT(" ORDER BY ") + orderBy;
637
thread=new pgQueryThread(connection, qry);
638
if (thread->Create() != wxTHREAD_NO_ERROR)
646
while (thread && thread->IsRunning())
654
if (!thread->DataValid())
659
SetStatusText(wxString::Format(_("%d rows."), thread->DataSet()->NumRows()), 0);
661
sqlGrid->BeginBatch();
663
// to force the grid to create scrollbars, we make sure the size so small that scrollbars are needed
664
// later, we will resize the grid's parent to force the correct size (now including scrollbars, even if
665
// they are suppressed initially. Win32 won't need this.
666
sqlGrid->SetSize(10,10);
668
sqlGrid->SetTable(new sqlTable(connection, thread, tableName, relid, hasOids, primaryKeyColNumbers, relkind), true);
672
event.m_size = GetSize();
675
if (!hasOids && primaryKeyColNumbers.IsEmpty() && relkind == 'r')
676
frmHint::ShowHint(this, HINT_READONLY_NOPK, tableName);
680
frmEditGrid::~frmEditGrid()
682
wxLogInfo(wxT("Destroying SQL EditGrid"));
683
mainForm->RemoveFrame(this);
690
void frmEditGrid::Abort()
692
if (sqlGrid->GetTable())
694
sqlGrid->HideCellEditControl();
695
// thread is owned by table und will be destroyed there
696
sqlGrid->SetTable(0);
700
SetStatusText(_("aborting."), 0);
701
if (thread->IsRunning())
709
ctlSQLGrid::ctlSQLGrid(wxFrame *parent, wxWindowID id, const wxPoint& pos, const wxSize& size)
710
: wxGrid(parent, id, pos, size, wxWANTS_CHARS|wxVSCROLL|wxHSCROLL)
717
void ctlSQLGrid::ResizeEditor(int row, int col)
720
if (GetTable()->needsResizing(col))
722
wxGridCellAttr* attr = GetCellAttr(row, col);
723
wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
726
wxClientDC dc(GetGridWindow());
727
wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
730
int w=wxMax(size.GetWidth(), 15)+20;
731
int h=wxMax(size.GetHeight(), 15)+20;
734
wxGridCellEditor *editor=attr->GetEditor(this, row, col);
737
wxRect cellRect = CellToRect(m_currentCellCoords);
738
wxRect rect = cellRect;
742
// we might have scrolled
743
CalcUnscrolledPosition(0, 0, &w, &h);
744
rect.SetLeft(rect.GetLeft() - w);
745
rect.SetTop(rect.GetTop() - h);
747
// Clip rect to client size
748
GetClientSize(&w, &h);
749
rect.SetRight(wxMin(rect.GetRight(), w));
750
rect.SetBottom(wxMin(rect.GetBottom(), h));
752
// but not smaller than original cell
753
rect.SetWidth(wxMax(cellRect.GetWidth(), rect.GetWidth()));
754
rect.SetHeight(wxMax(cellRect.GetHeight(), rect.GetHeight()));
756
editor->SetSize(rect);
766
wxSize ctlSQLGrid::GetBestSize(int row, int col)
770
wxGridCellAttr* attr = GetCellAttr(row, col);
771
wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
774
wxClientDC dc(GetGridWindow());
775
size = renderer->GetBestSize(*this, *attr, dc, row, col);
786
#if wxCHECK_VERSION(2,5,0)
787
// problems are fixed
790
bool ctlSQLGrid::SetTable(wxGridTableBase *table, bool takeOwnership)
801
// stop all processing
814
done= wxGrid::SetTable(table, takeOwnership);
817
dc.SetFont(GetLabelFont());
819
for (col=0 ; col < m_numCols ; col++)
821
wxString str=GetColLabelValue(col);
822
dc.GetTextExtent(str.BeforeFirst('\n'), &wmax, &h);
823
int crPos=str.Find('\n');
826
dc.GetTextExtent(str.Mid(crPos+1), &w, &h);
830
wmax += 4; // looks better
834
SetColSize(col, wmax);
843
wxArrayInt ctlSQLGrid::GetSelectedRows() const
845
wxArrayInt rows, rows2;
847
wxGridCellCoordsArray tl=GetSelectionBlockTopLeft(), br=GetSelectionBlockBottomRight();
849
int maxCol=((ctlSQLGrid*)this)->GetNumberCols() -1;
851
for (i=0 ; i < tl.GetCount() ; i++)
853
wxGridCellCoords c1=tl.Item(i), c2=br.Item(i);
854
if (c1.GetCol() != 0 || c2.GetCol() != maxCol)
858
for (j=c1.GetRow() ; j <= c2.GetRow() ; j++)
862
rows2=wxGrid::GetSelectedRows();
865
rows2.Sort(ArrayCmp);
867
size_t i2=0, cellRowMax=rows.GetCount();
869
for (i=0 ; i < rows2.GetCount() ; i++)
871
int row=rows2.Item(i);
872
while (i2 < cellRowMax && rows.Item(i2) < row)
874
if (i2 == cellRowMax || row != rows.Item(i2))
882
class sqlGridTextEditor : public wxGridCellTextEditor
885
sqlGridTextEditor(bool multiLine=false, int len=0) { isMultiLine=multiLine; textlen=len; }
886
virtual wxGridCellEditor *Clone() const { return new sqlGridTextEditor(isMultiLine, textlen); }
887
void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
888
void BeginEdit(int row, int col, wxGrid* grid);
889
bool EndEdit(int row, int col, wxGrid* grid);
900
void sqlGridTextEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler)
904
flags = wxTE_RICH|wxTE_MULTILINE|wxTE_DONTWRAP;
906
m_control = new wxTextCtrl(parent, id, wxEmptyString,
907
wxDefaultPosition, wxDefaultSize, flags
911
Text()->SetMaxLength(textlen);
913
wxGridCellEditor::Create(parent, id, evtHandler);
917
void sqlGridTextEditor::BeginEdit(int row, int col, wxGrid *grid)
919
wxGridCellTextEditor::BeginEdit(row, col, grid);
920
((ctlSQLGrid*)grid)->ResizeEditor(row, col);
924
bool sqlGridTextEditor::EndEdit(int row, int col, wxGrid *grid)
926
bool changed = false;
927
wxString value = Text()->GetValue();
931
grid->GetTable()->SetValue(row, col, value);
933
// Text()->SetValue(wxEmptyString);
938
bool rc=wxGridCellTextEditor::EndEdit(row, col, grid);
940
if (Text()->IsRich())
942
m_control->wxWindowBase::Show(true);
943
m_control->Show(false);
951
class sqlGridNumericEditor : public wxGridCellTextEditor
954
sqlGridNumericEditor(int len=-1, int prec=-1) {numlen=len; numprec=prec; }
955
virtual wxGridCellEditor *Clone() const { return new sqlGridNumericEditor(numlen, numprec); }
956
virtual void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
958
virtual bool IsAcceptedKey(wxKeyEvent& event);
959
virtual void BeginEdit(int row, int col, wxGrid* grid);
960
virtual bool EndEdit(int row, int col, wxGrid* grid);
962
virtual void Reset() {DoReset(m_startValue); }
963
virtual void StartingKey(wxKeyEvent& event);
964
virtual void SetParameters(const wxString& params);
968
wxString m_startValue;
974
void sqlGridNumericEditor::StartingKey(wxKeyEvent& event)
976
int keycode = event.GetKeyCode();
982
case WXK_NUMPAD_DECIMAL:
992
case WXK_NUMPAD_SUBTRACT:
1007
if (wxIsdigit(keycode))
1013
wxGridCellTextEditor::StartingKey(event);
1020
bool sqlGridNumericEditor::IsAcceptedKey(wxKeyEvent& event)
1022
if ( wxGridCellEditor::IsAcceptedKey(event) )
1024
int keycode = event.GetKeyCode();
1028
case WXK_NUMPAD_DECIMAL:
1029
return (numprec != 0);
1033
case WXK_NUMPAD_ADD:
1036
case WXK_NUMPAD_SUBTRACT:
1050
return wxIsdigit(keycode) != 0;
1059
void sqlGridNumericEditor::BeginEdit(int row, int col, wxGrid* grid)
1061
m_startValue = grid->GetTable()->GetValue(row, col);
1064
wxString value = m_startValue;
1065
// localize value here
1072
bool sqlGridNumericEditor::EndEdit(int row, int col, wxGrid* grid)
1074
wxASSERT_MSG(m_control,
1075
wxT("The sqlGridNumericEditor must be Created first!"));
1077
bool changed = false;
1078
wxString value = Text()->GetValue();
1080
// de-localize value here
1082
if (value != m_startValue)
1086
grid->GetTable()->SetValue(row, col, value);
1088
m_startValue = wxEmptyString;
1089
Text()->SetValue(m_startValue);
1095
void sqlGridNumericEditor::SetParameters(const wxString& params)
1106
if ( params.BeforeFirst(_T(',')).ToLong(&tmp) )
1110
if ( params.AfterFirst(_T(',')).ToLong(&tmp) )
1114
// skip the error message below
1122
void sqlGridNumericEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler)
1124
m_control = new wxTextCtrl(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize);
1126
wxGridCellEditor::Create(parent, id, evtHandler);
1127
Text()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
1133
sqlTable::sqlTable(pgConn *conn, pgQueryThread *_thread, const wxString& tabName, const OID _relid, bool _hasOid, const wxString& _pkCols, char _relkind)
1136
primaryKeyColNumbers = _pkCols;
1149
addPool = new cacheLinePool(100); // arbitrary initial size
1153
nRows = thread->DataSet()->NumRows();
1154
nCols=thread->DataSet()->NumCols();
1156
columns = new sqlCellAttr[nCols];
1157
savedLine.cols = new wxString[nCols];
1160
pgSet *colSet=connection->ExecuteSet(
1161
wxT("SELECT n.nspname AS nspname, relname, t.typname, nt.nspname AS typnspname, ")
1162
wxT("attname, attnum, COALESCE(b.oid, t.oid) AS basetype, atthasdef, adsrc,\n")
1163
wxT(" CASE WHEN t.typbasetype::oid=0 THEN att.atttypmod else t.typtypmod END AS typmod,\n")
1164
wxT(" CASE WHEN t.typbasetype::oid=0 THEN att.attlen else t.typlen END AS typlen\n")
1165
wxT(" FROM pg_attribute att\n")
1166
wxT(" JOIN pg_type t ON t.oid=att.atttypid\n")
1167
wxT(" JOIN pg_namespace nt ON nt.oid=t.typnamespace\n")
1168
wxT(" JOIN pg_class c ON c.oid=attrelid\n")
1169
wxT(" JOIN pg_namespace n ON n.oid=relnamespace\n")
1170
wxT(" LEFT OUTER JOIN pg_type b ON b.oid=t.typbasetype\n")
1171
wxT(" LEFT OUTER JOIN pg_attrdef def ON adrelid=attrelid AND adnum=attnum\n")
1172
wxT(" WHERE attnum > 0 AND NOT attisdropped AND attrelid=") + NumToStr(relid) + wxT("::oid\n")
1173
wxT(" ORDER BY attnum"));
1177
bool canInsert=false;
1182
columns[0].name = wxT("oid");
1183
columns[0].numeric = true;
1184
columns[0].attr->SetReadOnly(true);
1185
columns[0].type = PGTYPCLASS_NUMERIC;
1188
for (i=(hasOids ? 1 : 0) ; i < nCols ; i++)
1190
wxGridCellEditor *editor=0;
1192
columns[i].name = colSet->GetVal(wxT("attname"));
1193
columns[i].typeName = colSet->GetVal(wxT("typname"));
1194
columns[i].typeNspName = colSet->GetVal(wxT("typnspname"));
1196
columns[i].type = (Oid)colSet->GetOid(wxT("basetype"));
1197
if ((columns[i].type == PGOID_TYPE_INT4 || columns[i].type == PGOID_TYPE_INT8)
1198
&& colSet->GetBool(wxT("atthasdef")))
1200
if (colSet->GetVal(wxT("adsrc")) == wxT("nextval('")
1201
+ colSet->GetVal(wxT("nspname")) + wxT(".") + colSet->GetVal(wxT("relname"))
1202
+ wxT("_") + columns[i].name + wxT("_seq'::text)"))
1204
if (columns[i].type == PGOID_TYPE_INT4)
1205
columns[i].type = (Oid)PGOID_TYPE_SERIAL;
1207
columns[i].type = (Oid)PGOID_TYPE_SERIAL8;
1210
columns[i].typlen=colSet->GetLong(wxT("typlen"));
1211
columns[i].typmod=colSet->GetLong(wxT("typmod"));
1213
switch (columns[i].type)
1215
case PGOID_TYPE_BOOL:
1216
columns[i].numeric = false;
1217
columns[i].attr->SetReadOnly(false);
1218
editor = new wxGridCellBoolEditor();
1220
case PGOID_TYPE_INT8:
1221
case PGOID_TYPE_SERIAL8:
1222
SetNumberEditor(i, 20);
1224
case PGOID_TYPE_INT2:
1225
SetNumberEditor(i, 5);
1227
case PGOID_TYPE_INT4:
1228
case PGOID_TYPE_SERIAL:
1229
SetNumberEditor(i, 10);
1231
case PGOID_TYPE_OID:
1232
case PGOID_TYPE_TID:
1233
case PGOID_TYPE_XID:
1234
case PGOID_TYPE_CID:
1235
SetNumberEditor(i, 10);
1237
case PGOID_TYPE_FLOAT4:
1238
case PGOID_TYPE_FLOAT8:
1239
columns[i].numeric = true;
1240
columns[i].attr->SetReadOnly(false);
1241
editor = new sqlGridNumericEditor();
1243
case PGOID_TYPE_MONEY:
1244
case PGOID_TYPE_NUMERIC:
1246
columns[i].numeric = true;
1247
columns[i].attr->SetReadOnly(false);
1248
int len=columns[i].size();
1249
int prec=columns[i].precision();
1252
editor = new sqlGridNumericEditor(len, prec);
1255
case PGOID_TYPE_BYTEA:
1256
case PGOID_TYPE_CHAR:
1257
case PGOID_TYPE_NAME:
1258
case PGOID_TYPE_TEXT:
1260
columns[i].numeric = false;
1261
columns[i].attr->SetReadOnly(false);
1262
columns[i].needResize = true;
1263
// Temorary fix for 1.4.0 - FIXME properly!!
1265
editor = new wxGridCellAutoWrapStringEditor();
1267
editor = new sqlGridTextEditor(true);
1272
columns[i].attr->SetEditor(editor);
1274
if (relkind != 'r' || (!hasOids && primaryKeyColNumbers.IsNull()))
1276
// for security reasons, we need oid or pk to enable updates. If none is available,
1277
// the table definition can be considered faulty.
1278
columns[i].attr->SetReadOnly(true);
1280
if (!columns[i].attr->IsReadOnly())
1283
wxStringTokenizer collist(primaryKeyColNumbers, wxT(","));
1285
long attnum=colSet->GetLong(wxT("attnum"));
1287
while (cn < attnum && collist.HasMoreTokens())
1289
cn=StrToLong(collist.GetNextToken());
1291
columns[i].isPrimaryKey = true;
1300
// um, we never should reach here because this would mean
1301
// that datatypes are unreadable.
1302
// *if* we reach here, namespace info is missing.
1303
for (i=0 ; i < nCols ; i++)
1305
columns[i].typeName = thread->DataSet()->ColType(i);
1306
columns[i].name = thread->DataSet()->ColName(i);
1312
dataPool=new cacheLinePool(nRows);
1313
lineIndex = new int[nRows];
1314
for (i=0 ; i < nRows ; i++)
1320
// an empty line waiting for inserts
1326
sqlTable::~sqlTable()
1339
int sqlTable::GetNumberCols()
1345
int sqlTable::GetNumberRows()
1347
return nRows + rowsAdded - rowsDeleted;
1351
int sqlTable::GetNumberStoredRows()
1353
return nRows + rowsStored - rowsDeleted;
1357
wxString sqlTable::GetExportLine(int row)
1360
cacheLine *line = GetLine(row);
1364
for (col=0 ; col < nCols ; col++)
1367
str.Append(settings->GetExportColSeparator());
1368
bool needQuote = settings->GetExportQuoting() > 1;
1370
// find out if string
1371
switch (columns[col].type)
1373
case PGTYPCLASS_NUMERIC:
1374
case PGTYPCLASS_BOOL:
1381
str.Append(settings->GetExportQuoteChar());
1383
str.Append(line->cols[col]);
1386
str.Append(settings->GetExportQuoteChar());
1393
wxString sqlTable::GetColLabelValue(int col)
1395
wxString label=columns[col].name + wxT("\n");
1396
if (columns[col].isPrimaryKey)
1397
label += wxT("[PK] ");
1399
switch (columns[col].type)
1401
case (Oid)PGOID_TYPE_SERIAL:
1402
label += wxT("serial");
1404
case (Oid)PGOID_TYPE_SERIAL8:
1405
label += wxT("bigserial");
1408
label += columns[col].typeName;
1415
wxString sqlTable::GetRowLabelValue(int row)
1418
if (row < nRows-rowsDeleted || GetLine(row)->stored)
1419
label.Printf(wxT("%d"), row+1);
1427
void sqlTable::SetNumberEditor(int col, int len)
1429
columns[col].numeric = true;
1430
columns[col].attr->SetReadOnly(false);
1431
columns[col].attr->SetEditor(new sqlGridNumericEditor(len, 0));
1435
bool sqlTable::CheckInCache(int row)
1437
if (row > nRows - rowsDeleted + rowsAdded)
1439
if (row >= nRows-rowsDeleted)
1442
return dataPool->IsFilled(row);
1446
cacheLine *sqlTable::GetLine(int row)
1449
if (row < nRows-rowsDeleted)
1450
line=dataPool->Get(lineIndex[row]);
1452
line=addPool->Get(row-(nRows-rowsDeleted));
1458
wxString sqlTable::MakeKey(cacheLine *line)
1462
where = wxT("OID = ") + line->cols[0];
1465
wxStringTokenizer collist(primaryKeyColNumbers, wxT(","));
1468
while (collist.HasMoreTokens())
1470
cn=StrToLong(collist.GetNextToken());
1472
wxString colval=line->cols[cn-1];
1473
if (colval.IsEmpty())
1474
return wxEmptyString;
1476
if (!where.IsEmpty())
1477
where += wxT(" AND ");
1478
where += qtIdent(columns[cn-1].name) + wxT(" = ")
1479
+ qtString(colval) + wxT("::") + qtIdent(columns[cn-1].typeNspName)
1480
+ wxT(".") + qtIdent(columns[cn-1].typeName);
1488
void sqlTable::UndoLine(int row)
1490
if (lastRow >= 0 && row >= 0)
1492
cacheLine *line=GetLine(row);
1496
for (i=0 ; i < nCols ; i++)
1497
line->cols[i] = savedLine.cols[i];
1498
wxToolBar *tb=((wxFrame*)GetView()->GetParent())->GetToolBar();
1501
tb->EnableTool(MNU_SAVE, false);
1502
tb->EnableTool(MNU_UNDO, false);
1510
void sqlTable::StoreLine()
1512
GetView()->BeginBatch();
1517
cacheLine *line=GetLine(lastRow);
1520
wxString colList, valList;
1526
for (i=(hasOids? 1 : 0) ; i<nCols ; i++)
1528
if (columns[i].type == PGOID_TYPE_BOOL) // bool
1529
line->cols[i] = (StrToBool(line->cols[i]) ? wxT("t") : wxT("f"));
1531
if (savedLine.cols[i] != line->cols[i])
1533
if (!valList.IsNull())
1534
valList += wxT(", ");
1535
valList += qtIdent(columns[i].name) + wxT("=") + columns[i].Quote(line->cols[i]);
1539
if (valList.IsEmpty())
1543
wxString key=MakeKey(&savedLine);
1544
wxASSERT(!key.IsEmpty());
1545
done=connection->ExecuteVoid(wxT(
1546
"UPDATE ") + tableName + wxT(
1547
" SET ") + valList + wxT(
1555
for (i=0 ; i<nCols ; i++)
1557
if (!columns[i].attr->IsReadOnly() && !line->cols[i].IsEmpty())
1559
if (!colList.IsNull())
1561
valList += wxT(", ");
1562
colList += wxT(", ");
1564
colList += qtIdent(columns[i].name);
1565
if (columns[i].type == PGOID_TYPE_BOOL)
1566
line->cols[i] = (StrToBool(line->cols[i]) ? wxT("t") : wxT("f"));
1567
valList += columns[i].Quote(line->cols[i]);
1571
if (!valList.IsEmpty())
1573
pgSet *set=connection->ExecuteSet(
1574
wxT("INSERT INTO ") + tableName
1575
+ wxT("(") + colList
1576
+ wxT(") VALUES (") + valList
1581
line->cols[0] = NumToStr((long)set->GetInsertedOid());
1586
((wxFrame*)GetView()->GetParent())->SetStatusText(wxString::Format(wxT("%d rows."), GetNumberStoredRows()));
1587
if (rowsAdded == rowsStored)
1588
GetView()->AppendRows();
1590
// Read back what we inserted to get default vals
1591
wxString key=MakeKey(line);
1595
// That's a problem: obviously, the key generated isn't present
1596
// because it's serial or default or otherwise generated in the backend
1598
// That's why the whole line is declared readonly.
1600
line->readOnly=true;
1604
set=connection->ExecuteSet(
1605
wxT("SELECT * FROM ") + tableName +
1606
wxT(" WHERE ") + key);
1609
for (i=(hasOids?1:0) ; i < nCols ; i++)
1611
line->cols[i] = set->GetVal(columns[i].name);
1621
line->stored = true;
1628
GetView()->EndBatch();
1632
void sqlTable::SetValue(int row, int col, const wxString &value)
1634
cacheLine *line=GetLine(row);
1638
// Bad problem, no line!
1649
line->cols = new wxString[nCols];
1651
// remember line contents for later reference in update ... where
1653
for (i=0 ; i < nCols ; i++)
1654
savedLine.cols[i] = line->cols[i];
1657
wxToolBar *tb=((wxFrame*)GetView()->GetParent())->GetToolBar();
1660
tb->EnableTool(MNU_SAVE, true);
1661
tb->EnableTool(MNU_UNDO, true);
1663
line->cols[col] = value;
1668
wxString sqlTable::GetValue(int row, int col)
1672
if (row < nRows-rowsDeleted)
1673
line=dataPool->Get(lineIndex[row]);
1675
line=addPool->Get(row-(nRows-rowsDeleted));
1679
// Bad problem, no line!
1685
line->cols = new wxString[nCols];
1686
if (row < nRows-rowsDeleted)
1690
wxLogError(__("Unexpected empty cache line: dataSet already closed."));
1694
line->stored = true;
1695
if (lineIndex[row] != thread->DataSet()->CurrentPos() -1)
1696
thread->DataSet()->Locate(lineIndex[row]+1);
1699
for (i=0 ; i < nCols ; i++)
1701
wxString val= thread->DataSet()->GetVal(i);
1704
if (!thread->DataSet()->IsNull(i))
1707
else if (val == wxT("''"))
1708
val = wxT("\\'\\'");
1709
line->cols[i] = val;
1713
if (rowsCached == nRows)
1720
if (columns[col].type == PGOID_TYPE_BOOL)
1721
line->cols[col] = (StrToBool(line->cols[col]) ? wxT("TRUE") : wxT("FALSE"));
1723
val = line->cols[col];
1729
bool sqlTable::CanGetValueAs(int row, int col, const wxString& typeName)
1731
return CanSetValueAs(row, col, typeName);
1735
bool sqlTable::CanSetValueAs(int row, int col, const wxString& typeName)
1737
if (typeName == wxGRID_VALUE_STRING)
1740
switch (columns[col].type)
1742
case PGOID_TYPE_BOOL:
1743
return typeName == wxGRID_VALUE_BOOL;
1750
bool sqlTable::GetValueAsBool(int row, int col)
1752
return StrToBool(GetValue(row, col));
1756
void sqlTable::SetValueAsBool(int row, int col, bool b)
1758
SetValue(row, col, (b ? wxT("TRUE") : wxT("FALSE")));
1762
bool sqlTable::AppendRows(size_t rows)
1765
GetLine(nRows + rowsAdded - rowsDeleted -1);
1767
wxGridTableMessage msg(this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, rows);
1768
GetView()->ProcessTableMessage(msg);
1774
bool sqlTable::DeleteRows(size_t pos, size_t rows)
1779
while (i < pos+rows)
1781
cacheLine *line=GetLine(pos);
1787
wxString key=MakeKey(line);
1788
wxASSERT(!key.IsEmpty());
1789
bool done=connection->ExecuteVoid(wxT(
1790
"DELETE FROM ") + tableName + wxT(" WHERE ") + key);
1794
if ((int)pos < nRows - rowsDeleted)
1797
if ((int)pos < nRows - rowsDeleted)
1798
memmove(lineIndex+pos, lineIndex+pos+1, sizeof(cacheLine*)*(nRows-rowsDeleted-pos));
1803
if (GetLine(pos)->stored)
1805
addPool->Delete(pos - (nRows-rowsDeleted));
1811
// last empty line won't be deleted, just cleared
1813
for (j=0 ; j < nCols ; j++)
1814
line->cols[j] = wxT("");
1819
if (rowsDone > 0 && GetView())
1821
wxGridTableMessage msg(this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, pos, rowsDone);
1822
GetView()->ProcessTableMessage(msg);
1824
return (rowsDone != 0);
1830
wxGridCellAttr* sqlTable::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind)
1832
cacheLine *line=GetLine(row);
1833
if (line && line->readOnly)
1835
wxGridCellAttr *attr=new wxGridCellAttr(columns[col].attr);
1836
attr->SetReadOnly();
1841
columns[col].attr->IncRef();
1842
return columns[col].attr;
1848
wxString sqlCellAttr::Quote(const wxString& value)
1851
if (value.IsEmpty())
1855
else if (value == wxT("\\'\\'"))
1856
str = qtString(wxT("''"));
1857
else if (value == wxT("''"))
1860
str=qtString(value);
1862
return str + wxT("::") + qtIdent(typeNspName) + wxT(".") + qtIdent(typeName);
1868
int sqlCellAttr::size()
1870
if (typlen == -1 && typmod > 0)
1872
return (typmod-4) >> 16;
1879
int sqlCellAttr::precision()
1881
if (typlen == -1 && typmod > 0)
1883
return (typmod-4) & 0x7fff;
1890
cacheLinePool::cacheLinePool(int initialLines)
1892
ptr=new cacheLine*[initialLines];
1895
anzLines=initialLines;
1896
memset(ptr, 0, sizeof(cacheLine*)*anzLines);
1901
wxLogError(__("Out of Memory for cacheLinePool"));
1907
cacheLinePool::~cacheLinePool()
1914
delete ptr[anzLines];
1922
void cacheLinePool::Delete(int lineNo)
1924
if (ptr && lineNo >= 0 && lineNo < anzLines)
1930
if (lineNo < anzLines-1)
1932
// beware: overlapping copy
1933
memmove(ptr+lineNo, ptr+lineNo+1, sizeof(cacheLine*) * (anzLines-lineNo-1));
1946
cacheLine *cacheLinePool::Get(int lineNo)
1948
if (lineNo < 0) return 0;
1950
if (lineNo >= anzLines)
1952
cacheLine **old=ptr;
1953
int oldAnz=anzLines;
1954
anzLines=lineNo+100;
1955
ptr=new cacheLine*[anzLines];
1959
wxLogError(__("Out of Memory for cacheLinePool"));
1965
memcpy(ptr, old, sizeof(cacheLine*)*oldAnz);
1968
memset(ptr+oldAnz, 0, anzLines-oldAnz);
1972
if (lineNo < anzLines)
1975
ptr[lineNo] = new cacheLine();
1982
bool cacheLinePool::IsFilled(int lineNo)
1984
return (lineNo < anzLines && ptr[lineNo]);
1988
bool editGridFactoryBase::CheckEnable(pgObject *obj)
1992
pgaFactory *factory=obj->GetFactory();
1993
return factory == &tableFactory || factory == &viewFactory;
1999
wxWindow *editGridFactoryBase::ViewData(frmMain *form, pgObject *obj, bool filter)
2001
pgDatabase *db=((pgSchemaObject*)obj)->GetDatabase();
2003
pgServer *server=db->GetServer();
2004
pgConn *conn= db->CreateConn();
2007
wxString txt = wxT("pgAdmin III Edit Data - ")
2008
+ server->GetDescription()
2009
+ wxT(" (") + server->GetName()
2010
+ wxT(":") + NumToStr((long)server->GetPort())
2011
+ wxT(") - ") + db->GetName()
2012
+ wxT(" - ") + obj->GetFullIdentifier();
2014
frmEditGrid *eg= new frmEditGrid(form, txt, conn, (pgSchemaObject*)obj);
2015
eg->ShowForm(filter);
2022
editGridFactory::editGridFactory(menuFactoryList *list, wxMenu *mnu, wxToolBar *toolbar) : editGridFactoryBase(list)
2024
mnu->Append(id, _("View &Data"), _("View the data in the selected object."));
2025
toolbar->AddTool(id, _("View Data"), wxBitmap(viewdata_xpm), _("View the data in the selected object."), wxITEM_NORMAL);
2029
wxWindow *editGridFactory::StartDialog(frmMain *form, pgObject *obj)
2031
return ViewData(form, obj, false);
2035
#include "images/viewfiltereddata.xpm"
2036
editGridFilteredFactory::editGridFilteredFactory(menuFactoryList *list, wxMenu *mnu, wxToolBar *toolbar) : editGridFactoryBase(list)
2038
mnu->Append(id, _("View F&iltered Data"), _("Apply a filter and view the data in the selected object."));
2039
toolbar->AddTool(id, _("View Filtered Data"), wxBitmap(viewfiltereddata_xpm), _("Apply a filter and view the data in the selected object."), wxITEM_NORMAL);
2043
wxWindow *editGridFilteredFactory::StartDialog(frmMain *form, pgObject *obj)
2045
return ViewData(form, obj, true);