/* * File: LBoundGrid.cpp * Author: Saleem Edah-Tally - nmset@yandex.com * License : LGPL version 2.1 * Copyright Saleem Edah-Tally, 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()); } } 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 cellAttr; cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col); if (cellAttr.get() == NULL) return NULL; wxObjectDataPtr 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 (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 cellAttr; cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col); if (cellAttr.get() == NULL) return NULL; wxObjectDataPtr ed; ed = cellAttr->GetEditor(this, row, col); if (ed.get() == NULL) return NULL; return dynamic_cast (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 cellAttr; cellAttr = m_stringTable->GetAttr(row, col, wxGridCellAttr::Col); if (cellAttr.get() == NULL) return false; wxObjectDataPtr 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 (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()); } 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 cellAttr; cellAttr = m_stringTable->GetAttr(row, c, wxGridCellAttr::Col); if (cellAttr.get() == NULL) continue; wxObjectDataPtr ed; ed = cellAttr->GetEditor(this, row, c); if (ed.get() == NULL) continue; LGridColEditor * gce = dynamic_cast (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 (gce->ProvideFormEditor(pan0)); txtCtrl = static_cast (fEditor); break; case LGridColEditor::CHECK: fEditor = static_cast (gce->ProvideFormEditor(pan0)); break; case LGridColEditor::COMBO: fEditor = static_cast (gce->ProvideFormEditor(pan0)); break; case LGridColEditor::DATE: fEditor = static_cast (gce->ProvideFormEditor(pan0)); break; case LGridColEditor::SPIN: fEditor = static_cast (gce->ProvideFormEditor(pan0)); break; case LGridColEditor::JSON_GRID: fEditor = static_cast (gce->ProvideFormEditor(pan0)); break; case LGridColEditor::XML_GRID: fEditor = static_cast (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 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 (m_rs->GetRegisteredControls().Item(0)); m_rs->UnRegisterControl(ctrl); ctrl->SetNull(); } } void LBoundGrid::ResetColEditor(const int col) { wxObjectDataPtr cellAttr; cellAttr = m_stringTable->GetAttr(GetGridCursorRow(), col, wxGridCellAttr::Col); if (cellAttr == NULL) return; wxObjectDataPtr 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) { }