Files
l7/LBoundGrid.cpp
SET f7e210a356 Filter localizable strings.
Only user facing strings.
2022-10-15 18:54:17 +02:00

882 lines
32 KiB
C++

/*
* File: LBoundGrid.cpp
* Author: SET - nmset@yandex.com
* License : LGPL version 2.1
* Copyright SET, M. D. - © 2014
*
* Created on 9 juin 2014, 16:40
*/
#include "LBoundGrid.h"
#include "LGridCheckEditor.h"
#include "LGridCheckRenderer.h"
#include "LGridComboEditor.h"
#include "LGridComboRenderer.h"
#include "LGridDateEditor.h"
#include "LGridDateRenderer.h"
#include "LGridTextEditor.h"
#include "LGridTextRenderer.h"
#include "LGridSpinEditor.h"
#include "LGridSpinRenderer.h"
#include "special/LGridJsonCellRenderer.h"
#include "special/LGridJsonCellEditor.h"
#include "special/LGridXmlCellRenderer.h"
#include "special/LGridXmlCellEditor.h"
LBoundGrid::LBoundGrid(wxWindow* parent, wxWindowID id)
: wxGrid(parent, id, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL)
{
m_rs = NULL;
m_formClientSizer = NULL;
m_gridFormEVH = NULL;
m_scaleFactor = (wxGetDisplayPPI().GetY() / 96.0);
SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
m_stringTable = new wxGridStringTable();
m_stringTable->CanHaveAttributes();
HideRowLabels();
SetUseNativeColLabels();
SetTable(m_stringTable);
SetSelectionMode(wxGridSelectRows);
CreateMenu();
// These bindings must not propagate to the grid of BaseGridPicker !!!
Bind(wxEVT_GRID_RANGE_SELECT, &LBoundGrid::ForceSingleLineRange, this, GetId(), GetId());
Bind(wxEVT_GRID_SELECT_CELL, &LBoundGrid::CellSelected, this, GetId(), GetId());
Bind(wxEVT_GRID_CELL_RIGHT_CLICK, &LBoundGrid::ShowMenu, this, GetId(), GetId());
}
LBoundGrid::~LBoundGrid()
{
delete m_menu;
delete m_formClientSizer;
ClearGrid();
}
void LBoundGrid::SetResultSet(LResultSet* newResultSet)
{
m_rs = newResultSet;
if (m_rs == NULL) return;
wxASSERT_MSG(m_rs->GetConnection() != NULL, _T("GetConnection() = NULL."));
if (!m_menu) return;
if (m_rs->GetConnection()->IsReadOnly() || m_rs->IsReadOnly())
{
m_menu->FindItemByPosition(0)->Enable(false);
m_menu->FindItemByPosition(2)->Enable(false);
}
else
{
m_menu->FindItemByPosition(0)->Enable(true);
m_menu->FindItemByPosition(2)->Enable(true);
}
}
void LBoundGrid::ClearGrid()
{
Unbind(wxEVT_GRID_RANGE_SELECT, &LBoundGrid::ForceSingleLineRange, this, GetId(), GetId());
Unbind(wxEVT_GRID_SELECT_CELL, &LBoundGrid::CellSelected, this, GetId(), GetId());
if (GetNumberRows()) DeleteRows(0, GetNumberRows());
if (GetNumberCols()) DeleteCols(0, GetNumberCols());
}
void LBoundGrid::FillGrid()
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
wxASSERT_MSG(m_rs->GetConnection() != NULL, _T("GetConnection() = NULL."));
wxGridUpdateLocker locker(this);
// Starting again, the grid's resultset must free itself from any registered controls.
RestoreEditorControls();
Unbind(wxEVT_GRID_RANGE_SELECT, &LBoundGrid::ForceSingleLineRange, this, GetId(), GetId());
Unbind(wxEVT_GRID_SELECT_CELL, &LBoundGrid::CellSelected, this, GetId(), GetId());
// Remember some states
const wxGridSizesInfo colSizes = GetColSizes();
const int col = GetGridCursorCol() > -1 ? GetGridCursorCol() : 0;
// Create enough cells
if (GetNumberRows()) DeleteRows(0, GetNumberRows());
const bool firstTime = (m_stringTable->GetNumberCols() == 0);
const int nbCols = m_rs->GetColumnCount();
if (firstTime) m_stringTable->AppendCols(nbCols);
const int nbRows = m_rs->GetRowCount();
m_stringTable->AppendRows(nbRows);
// Add an insert row
if (!m_rs->GetConnection()->IsReadOnly() && !m_rs->IsReadOnly())
{
m_stringTable->AppendRows(1);
}
// Grid header labels default to database table column names.
if (firstTime)
{
for (unsigned int c = 0; c < nbCols; c++)
{
m_stringTable->SetColLabelValue(c, m_rs->GetColumnName(c));
}
}
// Set each cell's value.
for (unsigned int r = 0; r < nbRows; r++)
{
for (unsigned int c = 0; c < nbCols; c++)
{
m_stringTable->SetValue(r, c, (m_rs->GetData(r, c)).As<wxString>());
}
}
Bind(wxEVT_GRID_SELECT_CELL, &LBoundGrid::CellSelected, this, GetId(), GetId());
Bind(wxEVT_GRID_RANGE_SELECT, &LBoundGrid::ForceSingleLineRange, this, GetId(), GetId());
// Restore
SetColSizes(colSizes);
// synchronize with the resultset
const int targetRow = m_rs->GetRow() > -1 ? m_rs->GetRow() : 0;
if (GetNumberRows())
{
// Triggers CellSelected event. If no data, we are on insert row, we must call LResultSet::AddNew()
GoToCell(targetRow, col);
SelectRow(targetRow);
}
}
void LBoundGrid::CreateCheckBoxColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
bool readOnly,
bool isDualstate,
wxString newNullLabel)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridCheckEditor * ed = new LGridCheckEditor(newColName, isDualstate, newNullLabel);
colAtt->SetEditor(ed);
LGridCheckRenderer * rn = new LGridCheckRenderer(isDualstate, newNullLabel);
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateComboBoxColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridComboEditor * ed = new LGridComboEditor(newColName);
colAtt->SetEditor(ed);
LGridComboRenderer * rn = new LGridComboRenderer;
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateDateColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridDateEditor * ed = new LGridDateEditor(newColName);
colAtt->SetEditor(ed);
LGridDateRenderer * rn = new LGridDateRenderer();
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
colAtt->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateTextColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
bool newMultiline,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridTextEditor * ed = new LGridTextEditor(newColName, newMultiline);
colAtt->SetEditor(ed);
LGridTextRenderer * rn = new LGridTextRenderer();
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateSpinColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
int newMin,
int newMax,
int newInitial,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridSpinEditor * ed = new LGridSpinEditor(newColName, newMin, newMax, newInitial);
colAtt->SetEditor(ed);
LGridSpinRenderer * rn = new LGridSpinRenderer();
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
colAtt->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateJsonGridColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
const wxString& intentLabel,
const wxArrayString& types,
wxSize popupSize,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridJsonCellEditor * ed = new LGridJsonCellEditor(newColName, intentLabel, types, popupSize);
colAtt->SetEditor(ed);
LGridJsonCellRenderer * rn = new LGridJsonCellRenderer();
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
colAtt->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
void LBoundGrid::CreateXmlGridColumn(const wxString& newColName,
const wxString& newLabel,
unsigned int width,
const wxString& intentLabel,
const wxArrayString& types,
wxSize popupSize,
bool readOnly)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
const int col = m_rs->GetColumnIndex(newColName);
wxASSERT_MSG(col > -1, _T("Invalid column name : ") + newColName);
wxGridCellAttr * colAtt = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (colAtt == NULL) colAtt = new wxGridCellAttr();
LGridXmlCellEditor * ed = new LGridXmlCellEditor(newColName, intentLabel, types, popupSize);
colAtt->SetEditor(ed);
LGridXmlCellRenderer * rn = new LGridXmlCellRenderer();
colAtt->SetRenderer(rn);
colAtt->SetReadOnly(readOnly);
colAtt->SetAlignment(wxALIGN_RIGHT, wxALIGN_CENTRE);
m_stringTable->SetColAttr(colAtt, col);
m_stringTable->SetColLabelValue(col, newLabel);
SetColSize(col, width);
}
const wxString LBoundGrid::GetColName(const unsigned int col)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
InitEditor(GetGridCursorRow(), col, false);
LGridColEditor * gce = GetColEditor(GetGridCursorRow(), col);
if (gce == NULL) return wxEmptyString;
return gce->GetColumnName();
}
int LBoundGrid::GetColIndex(const wxString& colName)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
for (unsigned int col = 0; col < GetNumberCols(); col++)
{
LGridColEditor * gce = GetColEditor(GetGridCursorRow(), col);
if (gce == NULL) continue;
if (gce->GetColumnName().Lower() == colName.Lower()) return col;
}
return -1;
}
LBoundControl* LBoundGrid::GetBoundControl(const unsigned int row,
const unsigned int col,
bool keepRegistered)
{
wxASSERT_MSG(row >= 0 && row < GetNumberRows(), _T("Invalid row parameter"));
wxASSERT_MSG(col >= 0 && col < GetNumberCols(), _T("Invalid col parameter"));
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col);
if (cellAttr.get() == NULL) return NULL;
wxObjectDataPtr<wxGridCellEditor> ed;
ed = cellAttr->GetEditor(this, row, col);
if (ed.get() == NULL) return NULL;
ed->BeginEdit(row, col, this);
ed->Show(false);
LGridColEditor* gce = dynamic_cast<LGridColEditor*> (ed.get()); // CROSS CASTING
if (!gce) return NULL; // APP CAN ADD AN UNBOUND COLUMN
if (!keepRegistered)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
m_rs->UnRegisterControl(gce->GetBoundControl());
}
return gce->GetBoundControl();
}
LBoundControl* LBoundGrid::GetBoundControl(const unsigned int row,
const wxString& colName,
bool keepRegistered)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
wxASSERT_MSG(row >= 0 && row < GetNumberRows(), _T("Invalid row parameter"));
for (int col = 0; col < GetNumberCols(); col++)
{
LGridColEditor * gce = GetColEditor(row, col);
if (gce == NULL) continue;
if (gce->GetColumnName().Lower() == colName.Lower())
{
InitEditor(row, col, keepRegistered);
return gce->GetBoundControl();
}
}
return NULL;
}
LGridColEditor* LBoundGrid::GetColEditor(const unsigned int row, const unsigned int col) const
{
wxASSERT_MSG(row >= 0 && row < GetNumberRows(), _T("Invalid row parameter"));
wxASSERT_MSG(col >= 0 && col < GetNumberCols(), _T("Invalid col parameter"));
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col);
if (cellAttr.get() == NULL) return NULL;
wxObjectDataPtr<wxGridCellEditor> ed;
ed = cellAttr->GetEditor(this, row, col);
if (ed.get() == NULL) return NULL;
return dynamic_cast<LGridColEditor*> (ed.get());
}
wxControl* LBoundGrid::GetFormEditor(const unsigned int col) const
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
wxASSERT_MSG(col >= 0 && col < GetNumberCols(), _T("Invalid col parameter"));
int row = m_rs->GetRow();
if (m_rs->IsInserting()) row = m_rs->GetRowCount();
LGridColEditor* gce = GetColEditor(row, col);
if (!gce) return NULL;
return gce->GetFormEditor();
}
wxControl* LBoundGrid::GetFormEditor(const wxString& colName)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
int row = m_rs->GetRow();
if (m_rs->IsInserting()) row = m_rs->GetRowCount();
for (int col = 0; col < GetNumberCols(); col++)
{
LGridColEditor * gce = GetColEditor(row, col);
if (gce == NULL) continue;
if (gce->GetColumnName().Lower() == colName.Lower())
return gce->GetFormEditor();
}
return NULL;
}
bool LBoundGrid::InitEditor(const unsigned int row,
const unsigned int col,
bool keepRegistered)
{
wxASSERT_MSG(row >= 0 && row < GetNumberRows(), _T("Invalid row parameter"));
wxASSERT_MSG(col >= 0 && col < GetNumberCols(), _T("Invalid col parameter"));
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col);
if (cellAttr.get() == NULL) return false;
wxObjectDataPtr<wxGridCellEditor> ed;
ed = cellAttr->GetEditor(this, row, col);
if (ed.get() == NULL) return false;
// That's what we want to do.
ed->BeginEdit(row, col, this);
ed->Show(false);
if (!keepRegistered)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
LGridColEditor* gce = dynamic_cast<LGridColEditor*> (ed.get());
if (!gce) return false;
m_rs->UnRegisterControl(gce->GetBoundControl());
}
return true;
}
void LBoundGrid::InitAllEditors(const unsigned int row, bool keepRegistered)
{
wxASSERT_MSG(row >= 0 && row < GetNumberRows(), _T("Invalid row parameter"));
if (m_stringTable == NULL) return;
if (GetNumberCols() > 0)
{
for (int col = 0; col < GetNumberCols(); col++)
{
InitEditor(row, col, keepRegistered);
}
}
}
void LBoundGrid::AddMenuSeparator()
{
m_menu->AppendSeparator();
}
void LBoundGrid::AddMenuItem(wxMenuItem* newItem)
{
wxASSERT_MSG(newItem != NULL, _T("Menu item is NULL"));
if (m_menu->FindItem(newItem->GetItemLabelText()) != wxNOT_FOUND) return;
m_menu->Append(newItem);
}
void LBoundGrid::AddSubMenu(wxMenu* newSubMenu, const wxString& label)
{
wxASSERT_MSG(newSubMenu != NULL, _T("Submenu is NULL"));
if (m_menu->FindItem(newSubMenu->GetTitle()) != wxNOT_FOUND) return;
m_menu->AppendSubMenu(newSubMenu, label);
}
void LBoundGrid::RemoveMenuItem(const wxString& newItemLabelText)
{
if (m_menu->FindItem(newItemLabelText) == wxNOT_FOUND) return;
m_menu->Remove(m_menu->FindChildItem(m_menu->FindItem(newItemLabelText)));
}
void LBoundGrid::RemoveAllExternalMenuItems()
{
if (m_menu->GetMenuItemCount() < 8) return;
do
{
m_menu->Remove(m_menu->GetMenuItems()[7]);
}
while (m_menu->GetMenuItemCount() > 7);
}
void LBoundGrid::EnableMenuItem(int id, bool enable)
{
if (m_menu->FindChildItem(id)) m_menu->Enable(id, enable);
}
/*
* Only one single row must be selected at a time.
* Even if multiple selections do not affect the grid row and col cursor values,
* selecting only one row may avoid confusion.
* And the library is not designed to account for edits on multiple rows.
*/
void LBoundGrid::ForceSingleLineRange(wxGridRangeSelectEvent& evt)
{
if ((evt.Selecting()))
{
wxASSERT_MSG(GetSelectionMode() == wxGridSelectRows, "Selection mode is not wxGridSelectRows.");
if (evt.ControlDown() == true
|| evt.GetTopRow() != evt.GetBottomRow())
{
SelectRow(GetGridCursorRow());
evt.Veto();
}
}
evt.Skip();
}
void LBoundGrid::CellSelected(wxGridEvent& evt)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
// The insert row is somehow un-natural, it exists in the grid but not in the resultset, we manage it separately.
if (!m_rs->IsInserting())
{
// The user wants to change row.
if ((evt.GetRow() != m_rs->GetRow()))
{
// If there have been no edits in therow we are leaving, we must unregister all controls in the resultset.
if (!m_rs->IsDirty() && m_rs->HasData()) RestoreEditorControls();
// The user doesn't want to go on insert row.
if (evt.GetRow() < m_rs->GetRowCount())
{
// Absolute() will fail if there are edits
if (!m_rs->Absolute(evt.GetRow()))
{
evt.Veto(); // Doesn't seem to do anything
// Absolute() failed, get back to where we were.
GoToCell(m_rs->GetRow(), GetGridCursorCol());
SelectRow(m_rs->GetRow());
}
}
else
{
// The user wants to go on insert row, call AddNe(). Will fail if there are edits.
if (!m_rs->AddNew())
{
evt.Veto();
// AddNew() failed, get back to where we were.
GoToCell(m_rs->GetRow(), GetGridCursorCol());
SelectRow(m_rs->GetRow());
}
}
}
}
else
// We are inserting, on insert row.
{
// The user want to go above (if there is saved data).
if (evt.GetRow() < m_rs->GetRowCount())
{
// There are no edits on the insert row.
if (!m_rs->IsDirty())
{
RestoreEditorControls();
m_rs->Cancel();
// Synchronize the resultset with the grid.
m_rs->Absolute(evt.GetRow());
}
else
{
// There are edits on the insert row.
m_rs->InformInserting();
evt.Veto();
// Get back.
GoToCell(m_rs->GetRowCount(), GetGridCursorCol());
SelectRow(m_rs->GetRowCount());
}
}
}
evt.Skip();
}
void LBoundGrid::CreateMenu()
{
m_menu = new wxMenu(0);
wxMenuItem * item = new wxMenuItem(m_menu, ID_MNU_SAVE, _("Save"));
m_menu->Append(item);
item = new wxMenuItem(m_menu, ID_MNU_CANCEL, _("Cancel"));
m_menu->Append(item);
item = new wxMenuItem(m_menu, ID_MNU_DELETE, _("Delete"));
m_menu->Append(item);
item = new wxMenuItem(m_menu);
m_menu->Append(item);
item = new wxMenuItem(m_menu, ID_MNU_REFRESH, _("Refresh"));
m_menu->Append(item);
item = new wxMenuItem(m_menu);
m_menu->Append(item);
item = new wxMenuItem(m_menu, ID_MNU_FORMVIEW, _("Form view"));
m_menu->Append(item);
m_menu->Bind(wxEVT_COMMAND_MENU_SELECTED, &LBoundGrid::MenuAction, this);
}
void LBoundGrid::ShowMenu(wxGridEvent& evt)
{
GetGridWindow()->PopupMenu(m_menu);
}
void LBoundGrid::MenuAction(wxCommandEvent& evt)
{
switch (evt.GetId())
{
case ID_MNU_SAVE:
if (m_rs)
{
int row = m_rs->GetRow();
if (m_rs->IsInserting()) row = m_rs->GetRowCount();
InitPKEditor(row);
if (m_rs->Save())
{
RestoreEditorControls();
FillGrid();
}
}
break;
case ID_MNU_CANCEL:
if (m_rs)
{
HideCellEditControl();
const unsigned int nbCols = m_rs->GetColumnCount();
int row = m_rs->GetRow();
if (m_rs->IsInserting()) row = m_rs->GetRowCount();
for (int c = 0; c < nbCols; c++)
{
wxAny data = wxEmptyString;
if (!m_rs->IsInserting())
data = (m_rs->GetData(row, c));
m_stringTable->SetValue(row, c, data.As<wxString>());
}
m_rs->Cancel();
RestoreEditorControls();
row = m_rs->HasData() ? m_rs->GetRow() : 0;
GoToCell(row, GetGridCursorCol());
SelectRow(row);
ForceRefresh();
}
break;
case ID_MNU_REFRESH:
// If there's no data, the grid is on insert row and AddNew() has been called.
if (!m_rs->HasData()) m_rs->Cancel();
RestoreEditorControls();
m_rs->Refresh();
FillGrid();
break;
case ID_MNU_DELETE:
if (m_rs)
{
if (m_rs->GetRowCount() < 1) return;
InitPKEditor(m_rs->GetRow());
if (m_rs->Delete())
{
RestoreEditorControls();
FillGrid();
}
}
break;
case ID_MNU_FORMVIEW:
ShowFormView();
break;
}
GetGridWindow()->SetFocus();
}
bool LBoundGrid::InitPKEditor(const int row)
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
for (int c = 0; c < GetNumberCols(); c++)
{
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(row, c, wxGridCellAttr::Col);
if (cellAttr.get() == NULL) continue;
wxObjectDataPtr<wxGridCellEditor> ed;
ed = cellAttr->GetEditor(this, row, c);
if (ed.get() == NULL) continue;
LGridColEditor * gce = dynamic_cast<LGridColEditor*> (ed.get());
if (gce == NULL) continue;
if (gce->GetColumnName().Lower() == m_rs->GetPKColumnName().Lower())
{
ed->BeginEdit(row, c, this);
ed->Show(false);
return true;
}
}
return false;
}
void LBoundGrid::ShowFormView()
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
wxDialog * dlg = new wxDialog(this, wxID_ANY, _("Form view"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxRESIZE_BORDER);
unsigned int row = m_rs->GetRow();
if (m_rs->IsInserting()) row = m_rs->GetRowCount();
// We want to ignore hidden columns.
unsigned int visibleCol = 0;
// Multiline wxTextCtrl must be able to expand vertically.
bool hasMultiline = false;
// ScrolledWindow/VerticalSizer/Panel/VerticalSizer/FlexGridSizer
// /ClientSizer
wxScrolledWindow * swMain = new wxScrolledWindow(dlg, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxHSCROLL | wxVSCROLL);
swMain->SetScrollbars(1, 1, 0, 0);
wxBoxSizer* vsz0 = new wxBoxSizer(wxVERTICAL);
swMain->SetSizer(vsz0);
wxPanel * pan0 = new wxPanel(swMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER | wxHSCROLL | wxVSCROLL | wxTAB_TRAVERSAL);
vsz0->Add(pan0, 1, wxEXPAND);
wxBoxSizer * vsz = new wxBoxSizer(wxVERTICAL);
wxFlexGridSizer * flxsz = new wxFlexGridSizer(0, 2, 0, 0);
pan0->SetSizer(vsz);
vsz->Add(flxsz, 1, wxGROW | wxGROW | wxALL);
flxsz->AddGrowableCol(1);
m_formClientSizer = new wxBoxSizer(wxHORIZONTAL);
vsz->Add(m_formClientSizer, 0, wxGROW, 1);
for (unsigned int col = 0; col < GetNumberCols(); col++)
{
if (GetColSize(col) == 0) continue;
InitEditor(row, col, true);
LGridColEditor * gce = GetColEditor(row, col);
if (gce == NULL) continue;
int type = gce->GetType();
// Receive the form editor from LGridColEditor::ProvideFormEditor
wxControl * fEditor = NULL;
// One more pointer to a text control
wxTextCtrl * txtCtrl = NULL;
switch (type)
{
case LGridColEditor::TEXT:
fEditor = static_cast<wxTextCtrl*> (gce->ProvideFormEditor(pan0));
txtCtrl = static_cast<wxTextCtrl*> (fEditor);
break;
case LGridColEditor::CHECK:
fEditor = static_cast<wxCheckBox*> (gce->ProvideFormEditor(pan0));
break;
case LGridColEditor::COMBO:
fEditor = static_cast<wxComboBox*> (gce->ProvideFormEditor(pan0));
break;
case LGridColEditor::DATE:
fEditor = static_cast<wxDatePickerCtrl*> (gce->ProvideFormEditor(pan0));
break;
case LGridColEditor::SPIN:
fEditor = static_cast<wxSpinCtrl*> (gce->ProvideFormEditor(pan0));
break;
case LGridColEditor::JSON_GRID:
fEditor = static_cast<JsonGridPickerCtrl*> (gce->ProvideFormEditor(pan0));
break;
case LGridColEditor::XML_GRID:
fEditor = static_cast<XmlGridPickerCtrl*> (gce->ProvideFormEditor(pan0));
break;
}
// A label corresponding to the grid column header
wxStaticText * lbl = new wxStaticText(pan0, wxID_ANY, m_stringTable->GetColLabelValue(col), wxDefaultPosition, wxDefaultSize, 0);
flxsz->Add(lbl, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 0);
if (txtCtrl && txtCtrl->IsMultiLine())
{
flxsz->Add(fEditor, 0, wxGROW | wxGROW | wxALL, 0);
txtCtrl = NULL;
// The multiline text control must expand.
flxsz->AddGrowableRow(visibleCol);
hasMultiline = true;
}
else
{
flxsz->Add(fEditor, 0, wxGROW | wxALIGN_CENTER_VERTICAL | wxALL, 0);
}
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col);
if (cellAttr.get() != NULL)
{
// read only columns must not be edited
fEditor->Enable(!cellAttr->IsReadOnly());
}
fEditor->Show(true);
// visibleCol <> col
visibleCol++;
}
wxSize dSize = wxDefaultSize;
if (hasMultiline)
{
// Some arbitrary values
wxSize screen = wxGetDisplaySize();
dSize.SetWidth(screen.GetWidth() * 0.4);
dSize.SetHeight(screen.GetHeight() * 0.75);
}
// Let the application do something with the dialog before we show it
if (m_gridFormEVH) m_gridFormEVH->BeforeFormShown(this, m_formClientSizer);
vsz->SetSizeHints(pan0); // here
dlg->SetSize(dSize);
dlg->CentreOnScreen();
swMain->FitInside();
int res = dlg->ShowModal();
// Sync back any edits. Here we don't care about visible columns.
for (int col = 0; col < GetNumberCols(); col++)
{
LGridColEditor * dce = GetColEditor(row, col);
if (dce == NULL) continue;
dce->SyncBack(row, col, this);
}
// Let the application complete whatever.
if (m_gridFormEVH) m_gridFormEVH->AfterFormHidden(this, m_formClientSizer);
SetFocus();
dlg->Destroy();
wxDELETE(dlg); // ??
m_formClientSizer = NULL;
// Last information. But it's not idle time yet; this may be a source of unexpected trouble.
if (m_gridFormEVH) m_gridFormEVH->AfterFormDestroyed(this);
}
void LBoundGrid::RestoreEditorControls()
{
wxASSERT_MSG(m_rs != NULL, _T("RS = NULL"));
while (m_rs->GetRegisteredControls().GetCount())
{
LBoundControl * ctrl = static_cast<LBoundControl*> (m_rs->GetRegisteredControls().Item(0));
m_rs->UnRegisterControl(ctrl);
ctrl->SetNull();
}
}
void LBoundGrid::ResetColEditor(const int col)
{
wxObjectDataPtr<wxGridCellAttr> cellAttr;
cellAttr = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col);
if (cellAttr == NULL) return;
wxObjectDataPtr<wxGridCellEditor> ed;
ed = cellAttr->GetEditor(this, GetGridCursorRow(), col);
// This deletes the editor (m_control).
if (ed.get()) ed->Reset();
}
void LBoundGrid::SetDefaultRowSize(int height, bool resizeExistingRows)
{
wxGrid::SetDefaultRowSize(((wxDouble) height) * m_scaleFactor, resizeExistingRows);
}
void LBoundGrid::SetColMinimalAcceptableWidth(int width)
{
wxGrid::SetColMinimalAcceptableWidth(((wxDouble) width) * m_scaleFactor);
}
void LBoundGrid::SetColMinimalWidth(int col, int width)
{
wxGrid::SetColMinimalWidth(col, ((wxDouble) width) * m_scaleFactor);
}
void LBoundGrid::SetColSize(int col, int width)
{
wxGrid::SetColSize(col, ((wxDouble) width) * m_scaleFactor);
}
void LBoundGrid::SetDefaultColSize(int width, bool resizeExistingCols)
{
wxGrid::SetDefaultColSize(((wxDouble) width) * m_scaleFactor, resizeExistingCols);
}
void LBoundGrid::SetRowLabelSize(int width)
{
wxGrid::SetRowLabelSize(((wxDouble) width) * m_scaleFactor);
}
void LBoundGrid::SetRowMinimalAcceptableHeight(int height)
{
wxGrid::SetRowMinimalAcceptableHeight(((wxDouble) height) * m_scaleFactor);
}
void LBoundGrid::SetRowMinimalHeight(int row, int height)
{
wxGrid::SetRowMinimalHeight(row, ((wxDouble) height) * m_scaleFactor);
}
void LBoundGrid::SetRowSize(int row, int height)
{
wxGrid::SetRowSize(row, ((wxDouble) height) * m_scaleFactor);
}
///////////////////////////////////////////////////////////////////////////
LBoundGridFormEvent::LBoundGridFormEvent()
{
}
LBoundGridFormEvent::~LBoundGridFormEvent()
{
}
void LBoundGridFormEvent::BeforeFormShown(const LBoundGrid * origin, wxBoxSizer* clientSizer)
{
}
void LBoundGridFormEvent::AfterFormHidden(const LBoundGrid * origin, wxBoxSizer* clientSizer)
{
}
void LBoundGridFormEvent::AfterFormDestroyed(const LBoundGrid * origin)
{
}