X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d16c04bb0a2058f78b20b2d0fe85c7d4941ea55f..2912e35f1774b140b44cb9f9568aa5ab8122cd02:/src/generic/grid.cpp?ds=inline diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 9e0952c3b5..dd06aeda0b 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -41,14 +41,15 @@ #include "wx/log.h" #include "wx/textctrl.h" #include "wx/checkbox.h" + #include "wx/combobox.h" + #include "wx/valtext.h" #endif -// this include needs to be outside precomp for BCC #include "wx/textfile.h" +#include "wx/spinctrl.h" #include "wx/grid.h" - // ---------------------------------------------------------------------------- // array classes // ---------------------------------------------------------------------------- @@ -249,6 +250,46 @@ public: m_colAttrs; }; + +// ---------------------------------------------------------------------------- +// data structures used for the data type registry +// ---------------------------------------------------------------------------- + +struct wxGridDataTypeInfo { + wxGridDataTypeInfo(const wxString& typeName, + wxGridCellRenderer* renderer, + wxGridCellEditor* editor) + : m_typeName(typeName), m_renderer(renderer), m_editor(editor) + { } + + ~wxGridDataTypeInfo() { delete m_renderer; delete m_editor; } + + wxString m_typeName; + wxGridCellRenderer* m_renderer; + wxGridCellEditor* m_editor; +}; + + +WX_DEFINE_ARRAY(wxGridDataTypeInfo*, wxGridDataTypeInfoArray); + + +class WXDLLEXPORT wxGridTypeRegistry { +public: + ~wxGridTypeRegistry(); + void RegisterDataType(const wxString& typeName, + wxGridCellRenderer* renderer, + wxGridCellEditor* editor); + int FindDataType(const wxString& typeName); + wxGridCellRenderer* GetRenderer(int index); + wxGridCellEditor* GetEditor(int index); + +private: + wxGridDataTypeInfoArray m_typeinfo; +}; + + + + // ---------------------------------------------------------------------------- // conditional compilation // ---------------------------------------------------------------------------- @@ -267,6 +308,10 @@ public: static size_t gs_nAttrCacheMisses = 0; #endif // DEBUG_ATTR_CACHE +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + wxGridCellCoords wxGridNoCellCoords( -1, -1 ); wxRect wxGridNoCellRect( -1, -1, -1, -1 ); @@ -274,6 +319,10 @@ wxRect wxGridNoCellRect( -1, -1, -1, -1 ); // TODO: fixed so far - make configurable later (and also different for x/y) static const size_t GRID_SCROLL_LINE = 10; +// the size of hash tables used a bit everywhere (the max number of elements +// in these hash tables is the number of rows/columns) +static const int GRID_HASH_SIZE = 100; + // ============================================================================ // implementation // ============================================================================ @@ -334,23 +383,14 @@ void wxGridCellEditor::Show(bool show, wxGridCellAttr *attr) // set the colours/fonts if we have any if ( attr ) { - if ( attr->HasTextColour() ) - { - m_colFgOld = m_control->GetForegroundColour(); - m_control->SetForegroundColour(attr->GetTextColour()); - } + m_colFgOld = m_control->GetForegroundColour(); + m_control->SetForegroundColour(attr->GetTextColour()); - if ( attr->HasBackgroundColour() ) - { - m_colBgOld = m_control->GetBackgroundColour(); - m_control->SetBackgroundColour(attr->GetBackgroundColour()); - } + m_colBgOld = m_control->GetBackgroundColour(); + m_control->SetBackgroundColour(attr->GetBackgroundColour()); - if ( attr->HasFont() ) - { - m_fontOld = m_control->GetFont(); - m_control->SetFont(attr->GetFont()); - } + m_fontOld = m_control->GetFont(); + m_control->SetFont(attr->GetFont()); // can't do anything more in the base class version, the other // attributes may only be used by the derived classes @@ -430,18 +470,49 @@ void wxGridCellTextEditor::PaintBackground(const wxRect& WXUNUSED(rectCell), // flicker } +void wxGridCellTextEditor::SetSize(const wxRect& rectOrig) +{ + wxRect rect(rectOrig); + + // Make the edit control large enough to allow for internal + // margins + // + // TODO: remove this if the text ctrl sizing is improved esp. for + // unix + // +#if defined(__WXGTK__) + rect.Inflate(rect.x ? 1 : 0, rect.y ? 1 : 0); +#else // !GTK + int extra = row && col ? 2 : 1; +#if defined(__WXMOTIF__) + extra *= 2; +#endif + rect.SetLeft( wxMax(0, rect.x - extra) ); + rect.SetTop( wxMax(0, rect.y - extra) ); + rect.SetRight( rect.GetRight() + 2*extra ); + rect.SetBottom( rect.GetBottom() + 2*extra ); +#endif // GTK/!GTK + + wxGridCellEditor::SetSize(rect); +} + void wxGridCellTextEditor::BeginEdit(int row, int col, wxGrid* grid) { wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be Created first!")); m_startValue = grid->GetTable()->GetValue(row, col); - Text()->SetValue(m_startValue); + + DoBeginEdit(m_startValue); +} + +void wxGridCellTextEditor::DoBeginEdit(const wxString& startValue) +{ + Text()->SetValue(startValue); Text()->SetInsertionPointEnd(); Text()->SetFocus(); } - bool wxGridCellTextEditor::EndEdit(int row, int col, bool saveValue, wxGrid* grid) { @@ -468,7 +539,12 @@ void wxGridCellTextEditor::Reset() wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be Created first!")); - Text()->SetValue(m_startValue); + DoReset(m_startValue); +} + +void wxGridCellTextEditor::DoReset(const wxString& startValue) +{ + Text()->SetValue(startValue); Text()->SetInsertionPointEnd(); } @@ -512,6 +588,187 @@ void wxGridCellTextEditor::HandleReturn(wxKeyEvent& event) #endif } +// ---------------------------------------------------------------------------- +// wxGridCellNumberEditor +// ---------------------------------------------------------------------------- + +wxGridCellNumberEditor::wxGridCellNumberEditor(int min, int max) +{ + m_min = min; + m_max = max; +} + +void wxGridCellNumberEditor::Create(wxWindow* parent, + wxWindowID id, + wxEvtHandler* evtHandler) +{ + if ( HasRange() ) + { + // create a spin ctrl + m_control = new wxSpinCtrl(parent, -1, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + wxSP_ARROW_KEYS, + m_min, m_max); + + wxGridCellEditor::Create(parent, id, evtHandler); + } + else + { + // just a text control + wxGridCellTextEditor::Create(parent, id, evtHandler); + +#if wxUSE_VALIDATORS + Text()->SetValidator(new wxTextValidator(wxFILTER_NUMERIC)); +#endif // wxUSE_VALIDATORS + } +} + +void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid) +{ + // first get the value + wxGridTableBase *table = grid->GetTable(); + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) + { + m_valueOld = table->GetValueAsLong(row, col); + } + else + { + wxFAIL_MSG( _T("this cell doesn't have numeric value") ); + + return; + } + + if ( HasRange() ) + { + Spin()->SetValue(m_valueOld); + } + else + { + DoBeginEdit(GetString()); + } +} + +bool wxGridCellNumberEditor::EndEdit(int row, int col, bool saveValue, + wxGrid* grid) +{ + bool changed; + long value; + + if ( HasRange() ) + { + value = Spin()->GetValue(); + changed = value != m_valueOld; + } + else + { + changed = Text()->GetValue().ToLong(&value) && (value != m_valueOld); + } + + if ( changed ) + { + grid->GetTable()->SetValueAsLong(row, col, value); + } + + return changed; +} + +void wxGridCellNumberEditor::Reset() +{ + if ( HasRange() ) + { + Spin()->SetValue(m_valueOld); + } + else + { + DoReset(GetString()); + } +} + +void wxGridCellNumberEditor::StartingKey(wxKeyEvent& event) +{ + if ( !HasRange() ) + { + long keycode = event.KeyCode(); + if ( isdigit(keycode) || keycode == '+' || keycode == '-' ) + { + wxGridCellTextEditor::StartingKey(event); + + // skip Skip() below + return; + } + } + + event.Skip(); +} +// ---------------------------------------------------------------------------- +// wxGridCellFloatEditor +// ---------------------------------------------------------------------------- + +void wxGridCellFloatEditor::Create(wxWindow* parent, + wxWindowID id, + wxEvtHandler* evtHandler) +{ + wxGridCellTextEditor::Create(parent, id, evtHandler); + +#if wxUSE_VALIDATORS + Text()->SetValidator(new wxTextValidator(wxFILTER_NUMERIC)); +#endif // wxUSE_VALIDATORS +} + +void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid) +{ + // first get the value + wxGridTableBase *table = grid->GetTable(); + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) + { + m_valueOld = table->GetValueAsDouble(row, col); + } + else + { + wxFAIL_MSG( _T("this cell doesn't have float value") ); + + return; + } + + DoBeginEdit(GetString()); +} + +bool wxGridCellFloatEditor::EndEdit(int row, int col, bool saveValue, + wxGrid* grid) +{ + double value; + if ( Text()->GetValue().ToDouble(&value) && (value != m_valueOld) ) + { + grid->GetTable()->SetValueAsDouble(row, col, value); + + return TRUE; + } + else + { + return FALSE; + } +} + +void wxGridCellFloatEditor::Reset() +{ + DoReset(GetString()); +} + +void wxGridCellFloatEditor::StartingKey(wxKeyEvent& event) +{ + long keycode = event.KeyCode(); + if ( isdigit(keycode) || + keycode == '+' || keycode == '-' || keycode == '.' ) + { + wxGridCellTextEditor::StartingKey(event); + + // skip Skip() below + return; + } + + event.Skip(); +} + // ---------------------------------------------------------------------------- // wxGridCellBoolEditor // ---------------------------------------------------------------------------- @@ -544,10 +801,10 @@ void wxGridCellBoolEditor::SetSize(const wxRect& r) void wxGridCellBoolEditor::Show(bool show, wxGridCellAttr *attr) { - wxGridCellEditor::Show(show, attr); + m_control->Show(show); + if ( show ) { - // VZ: normally base class already does it, but it doesn't work (FIXME) wxColour colBg = attr ? attr->GetBackgroundColour() : *wxLIGHT_GREY; CBox()->SetBackgroundColour(colBg); } @@ -558,7 +815,10 @@ void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid) wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be Created first!")); - m_startValue = !!grid->GetTable()->GetValue(row, col); // FIXME-DATA + if (grid->GetTable()->CanGetValueAs(row, col, wxT("bool"))) + m_startValue = grid->GetTable()->GetValueAsBool(row, col); + else + m_startValue = !!grid->GetTable()->GetValue(row, col); CBox()->SetValue(m_startValue); CBox()->SetFocus(); } @@ -577,8 +837,10 @@ bool wxGridCellBoolEditor::EndEdit(int row, int col, if ( changed ) { - // FIXME-DATA - grid->GetTable()->SetValue(row, col, value ? _T("1") : wxEmptyString); + if (grid->GetTable()->CanGetValueAs(row, col, wxT("bool"))) + grid->GetTable()->SetValueAsBool(row, col, value); + else + grid->GetTable()->SetValue(row, col, value ? _T("1") : wxEmptyString); } return changed; @@ -597,6 +859,84 @@ void wxGridCellBoolEditor::StartingClick() CBox()->SetValue(!CBox()->GetValue()); } +// ---------------------------------------------------------------------------- +// wxGridCellChoiceEditor +// ---------------------------------------------------------------------------- + +wxGridCellChoiceEditor::wxGridCellChoiceEditor(size_t count, + const wxChar* choices[], + bool allowOthers) + : m_allowOthers(allowOthers) +{ + m_choices.Alloc(count); + for ( size_t n = 0; n < count; n++ ) + { + m_choices.Add(choices[n]); + } +} + +void wxGridCellChoiceEditor::Create(wxWindow* parent, + wxWindowID id, + wxEvtHandler* evtHandler) +{ + size_t count = m_choices.GetCount(); + wxString *choices = new wxString[count]; + for ( size_t n = 0; n < count; n++ ) + { + choices[n] = m_choices[n]; + } + + m_control = new wxComboBox(parent, id, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + count, choices, + m_allowOthers ? 0 : wxCB_READONLY); + + delete [] choices; + + wxGridCellEditor::Create(parent, id, evtHandler); +} + +void wxGridCellChoiceEditor::PaintBackground(const wxRect& WXUNUSED(rectCell), + wxGridCellAttr * WXUNUSED(attr)) +{ + // as we fill the entire client area, don't do anything here to minimize + // flicker +} + +void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) +{ + wxASSERT_MSG(m_control, + wxT("The wxGridCellEditor must be Created first!")); + + m_startValue = grid->GetTable()->GetValue(row, col); + + Combo()->SetValue(m_startValue); + Combo()->SetInsertionPointEnd(); + Combo()->SetFocus(); +} + +bool wxGridCellChoiceEditor::EndEdit(int row, int col, + bool saveValue, + wxGrid* grid) +{ + wxString value = Combo()->GetValue(); + bool changed = value != m_startValue; + + if ( changed ) + grid->GetTable()->SetValue(row, col, value); + + m_startValue = wxEmptyString; + Combo()->SetValue(m_startValue); + + return changed; +} + +void wxGridCellChoiceEditor::Reset() +{ + Combo()->SetValue(m_startValue); + Combo()->SetInsertionPointEnd(); +} + // ---------------------------------------------------------------------------- // wxGridCellEditorEvtHandler // ---------------------------------------------------------------------------- @@ -669,20 +1009,19 @@ void wxGridCellRenderer::Draw(wxGrid& grid, dc.DrawRectangle(rect); } +wxGridCellRenderer::~wxGridCellRenderer() +{ +} + // ---------------------------------------------------------------------------- // wxGridCellStringRenderer // ---------------------------------------------------------------------------- -void wxGridCellStringRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rectCell, - int row, int col, - bool isSelected) +void wxGridCellStringRenderer::SetTextColoursAndFont(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + bool isSelected) { - wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); - - // now we only have to draw the text dc.SetBackgroundMode( wxTRANSPARENT ); // TODO some special colours for attr.IsReadOnly() case? @@ -697,67 +1036,223 @@ void wxGridCellStringRenderer::Draw(wxGrid& grid, dc.SetTextBackground( attr.GetBackgroundColour() ); dc.SetTextForeground( attr.GetTextColour() ); } + dc.SetFont( attr.GetFont() ); +} + +wxSize wxGridCellStringRenderer::DoGetBestSize(wxGridCellAttr& attr, + wxDC& dc, + const wxString& text) +{ + wxCoord x, y; + dc.SetFont(attr.GetFont()); + dc.GetTextExtent(text, &x, &y); + + return wxSize(x, y); +} + +wxSize wxGridCellStringRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, grid.GetCellValue(row, col)); +} + +void wxGridCellStringRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + // now we only have to draw the text + SetTextColoursAndFont(grid, attr, dc, isSelected); int hAlign, vAlign; attr.GetAlignment(&hAlign, &vAlign); wxRect rect = rectCell; - rect.x++; - rect.y++; - rect.width -= 2; - rect.height -= 2; + rect.Inflate(-1); grid.DrawTextRectangle(dc, grid.GetCellValue(row, col), rect, hAlign, vAlign); } // ---------------------------------------------------------------------------- -// wxGridCellBoolRenderer +// wxGridCellNumberRenderer // ---------------------------------------------------------------------------- -void wxGridCellBoolRenderer::Draw(wxGrid& grid, - wxGridCellAttr& attr, - wxDC& dc, - const wxRect& rect, - int row, int col, - bool isSelected) +wxString wxGridCellNumberRenderer::GetString(wxGrid& grid, int row, int col) { - wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + wxGridTableBase *table = grid.GetTable(); + wxString text; + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_NUMBER) ) + { + text.Printf(_T("%ld"), table->GetValueAsLong(row, col)); + } + //else: leave the string empty or put 0 into it? + + return text; +} + +void wxGridCellNumberRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + SetTextColoursAndFont(grid, attr, dc, isSelected); + + // draw the text right aligned by default + int hAlign, vAlign; + attr.GetAlignment(&hAlign, &vAlign); + hAlign = wxRIGHT; + + wxRect rect = rectCell; + rect.Inflate(-1); + + grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); +} + +wxSize wxGridCellNumberRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, GetString(grid, row, col)); +} + +// ---------------------------------------------------------------------------- +// wxGridCellFloatRenderer +// ---------------------------------------------------------------------------- + +wxGridCellFloatRenderer::wxGridCellFloatRenderer(int width, int precision) +{ + SetWidth(width); + SetPrecision(precision); +} + +wxString wxGridCellFloatRenderer::GetString(wxGrid& grid, int row, int col) +{ + wxGridTableBase *table = grid.GetTable(); + wxString text; + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) + { + if ( !m_format ) + { + m_format.Printf(_T("%%%d.%d%%f"), m_width, m_precision); + } + + text.Printf(m_format, table->GetValueAsDouble(row, col)); + } + //else: leave the string empty or put 0 into it? + + return text; +} + +void wxGridCellFloatRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rectCell, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rectCell, row, col, isSelected); + + SetTextColoursAndFont(grid, attr, dc, isSelected); + + // draw the text right aligned by default + int hAlign, vAlign; + attr.GetAlignment(&hAlign, &vAlign); + hAlign = wxRIGHT; + + wxRect rect = rectCell; + rect.Inflate(-1); + + grid.DrawTextRectangle(dc, GetString(grid, row, col), rect, hAlign, vAlign); +} + +wxSize wxGridCellFloatRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + int row, int col) +{ + return DoGetBestSize(attr, dc, GetString(grid, row, col)); +} - // between checkmark and box - static const wxCoord margin = 4; +// ---------------------------------------------------------------------------- +// wxGridCellBoolRenderer +// ---------------------------------------------------------------------------- - // get checkbox size - static wxCoord s_checkSize = 0; - if ( s_checkSize == 0 ) +wxSize wxGridCellBoolRenderer::ms_sizeCheckMark; + +// between checkmark and box +static const wxCoord wxGRID_CHECKMARK_MARGIN = 4; + +wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, + wxGridCellAttr& WXUNUSED(attr), + wxDC& WXUNUSED(dc), + int WXUNUSED(row), + int WXUNUSED(col)) +{ + // compute it only once (no locks for MT safeness in GUI thread...) + if ( !ms_sizeCheckMark.x ) { - // compute it only once (no locks for MT safeness in GUI thread...) + // get checkbox size + wxCoord checkSize = 0; wxCheckBox *checkbox = new wxCheckBox(&grid, -1, wxEmptyString); wxSize size = checkbox->GetBestSize(); - s_checkSize = size.y + margin; + checkSize = size.y + wxGRID_CHECKMARK_MARGIN; - // FIXME wxGTK::wxCheckBox::GetBestSize() is really weird... + // FIXME wxGTK::wxCheckBox::GetBestSize() gives "wrong" result #ifdef __WXGTK__ - s_checkSize -= size.y / 2; + checkSize -= size.y / 2; #endif delete checkbox; + + ms_sizeCheckMark.x = ms_sizeCheckMark.y = checkSize; } + return ms_sizeCheckMark; +} + +void wxGridCellBoolRenderer::Draw(wxGrid& grid, + wxGridCellAttr& attr, + wxDC& dc, + const wxRect& rect, + int row, int col, + bool isSelected) +{ + wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected); + // draw a check mark in the centre (ignoring alignment - TODO) + wxSize size = GetBestSize(grid, attr, dc, row, col); wxRect rectMark; - rectMark.x = rect.x + rect.width/2 - s_checkSize/2; - rectMark.y = rect.y + rect.height/2 - s_checkSize/2; - rectMark.width = rectMark.height = s_checkSize; + rectMark.x = rect.x + rect.width/2 - size.x/2; + rectMark.y = rect.y + rect.height/2 - size.y/2; + rectMark.width = size.x; + rectMark.height = size.y; dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetPen(wxPen(attr.GetTextColour(), 1, wxSOLID)); dc.DrawRectangle(rectMark); - rectMark.Inflate(-margin); + rectMark.Inflate(-wxGRID_CHECKMARK_MARGIN); - if ( !!grid.GetTable()->GetValue(row, col) ) // FIXME-DATA + bool value; + if (grid.GetTable()->CanGetValueAs(row, col, wxT("bool"))) + value = grid.GetTable()->GetValueAsBool(row, col); + else + value = !!grid.GetTable()->GetValue(row, col); + + if ( value ) { dc.SetTextForeground(attr.GetTextColour()); dc.DrawCheckMark(rectMark); @@ -830,12 +1325,21 @@ void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const } -wxGridCellRenderer* wxGridCellAttr::GetRenderer() const +// GetRenderer and GetEditor use a slightly different decision path about +// which to use. If a non-default attr object has one then it is used, +// otherwise the default editor or renderer passed in is used. It should be +// the default for the data type of the cell. If it is NULL (because the +// table has a type that the grid does not have in its registry,) then the +// grid's default editor or renderer is used. + +wxGridCellRenderer* wxGridCellAttr::GetRenderer(wxGridCellRenderer* def) const { - if (HasRenderer()) + if ((m_defGridAttr != this || def == NULL) && HasRenderer()) return m_renderer; + else if (def) + return def; else if (m_defGridAttr != this) - return m_defGridAttr->GetRenderer(); + return m_defGridAttr->GetRenderer(NULL); else { wxFAIL_MSG(wxT("Missing default cell attribute")); @@ -843,12 +1347,14 @@ wxGridCellRenderer* wxGridCellAttr::GetRenderer() const } } -wxGridCellEditor* wxGridCellAttr::GetEditor() const +wxGridCellEditor* wxGridCellAttr::GetEditor(wxGridCellEditor* def) const { - if (HasEditor()) + if ((m_defGridAttr != this || def == NULL) && HasEditor()) return m_editor; + else if (def) + return def; else if (m_defGridAttr != this) - return m_defGridAttr->GetEditor(); + return m_defGridAttr->GetEditor(NULL); else { wxFAIL_MSG(wxT("Missing default cell attribute")); @@ -1149,14 +1655,64 @@ void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) } } +// ---------------------------------------------------------------------------- +// wxGridTypeRegistry +// ---------------------------------------------------------------------------- + +wxGridTypeRegistry::~wxGridTypeRegistry() +{ + for (size_t i=0; im_typeName) { + found = i; + break; + } + } + + return found; +} + +wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index) +{ + wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer; + return renderer; +} + +wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index) +{ + wxGridCellEditor* editor = m_typeinfo[index]->m_editor; + return editor; +} + // ---------------------------------------------------------------------------- // wxGridTableBase // ---------------------------------------------------------------------------- -////////////////////////////////////////////////////////////////////// -// -// Abstract base class for grid data (the model) -// IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject ) @@ -1177,6 +1733,16 @@ void wxGridTableBase::SetAttrProvider(wxGridCellAttrProvider *attrProvider) m_attrProvider = attrProvider; } +bool wxGridTableBase::CanHaveAttributes() +{ + if ( ! GetAttrProvider() ) + { + // use the default attr provider by default + SetAttrProvider(new wxGridCellAttrProvider); + } + return TRUE; +} + wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col) { if ( m_attrProvider ) @@ -1295,7 +1861,8 @@ bool wxGridTableBase::DeleteCols( size_t pos, size_t numCols ) wxString wxGridTableBase::GetRowLabelValue( int row ) { wxString s; - s << row; + s << row + 1; // RD: Starting the rows at zero confuses users, no matter + // how much it makes sense to us geeks. return s; } @@ -1326,6 +1893,65 @@ wxString wxGridTableBase::GetColLabelValue( int col ) } +wxString wxGridTableBase::GetTypeName( int WXUNUSED(row), int WXUNUSED(col) ) +{ + return wxGRID_VALUE_STRING; +} + +bool wxGridTableBase::CanGetValueAs( int WXUNUSED(row), int WXUNUSED(col), + const wxString& typeName ) +{ + return typeName == wxGRID_VALUE_STRING; +} + +bool wxGridTableBase::CanSetValueAs( int row, int col, const wxString& typeName ) +{ + return CanGetValueAs(row, col, typeName); +} + +long wxGridTableBase::GetValueAsLong( int WXUNUSED(row), int WXUNUSED(col) ) +{ + return 0; +} + +double wxGridTableBase::GetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col) ) +{ + return 0.0; +} + +bool wxGridTableBase::GetValueAsBool( int WXUNUSED(row), int WXUNUSED(col) ) +{ + return FALSE; +} + +void wxGridTableBase::SetValueAsLong( int WXUNUSED(row), int WXUNUSED(col), + long WXUNUSED(value) ) +{ +} + +void wxGridTableBase::SetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col), + double WXUNUSED(value) ) +{ +} + +void wxGridTableBase::SetValueAsBool( int WXUNUSED(row), int WXUNUSED(col), + bool WXUNUSED(value) ) +{ +} + + +void* wxGridTableBase::GetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col), + const wxString& WXUNUSED(typeName) ) +{ + return NULL; +} + +void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col), + const wxString& WXUNUSED(typeName), + void* WXUNUSED(value) ) +{ +} + ////////////////////////////////////////////////////////////////////// // @@ -1411,11 +2037,11 @@ wxString wxGridStringTable::GetValue( int row, int col ) return m_data[row][col]; } -void wxGridStringTable::SetValue( int row, int col, const wxString& s ) +void wxGridStringTable::SetValue( int row, int col, const wxString& value ) { // TODO: bounds checking // - m_data[row][col] = s; + m_data[row][col] = value; } bool wxGridStringTable::IsEmptyCell( int row, int col ) @@ -1965,10 +2591,41 @@ void wxGridWindow::OnKeyDown( wxKeyEvent& event ) if ( !m_owner->ProcessEvent( event ) ) event.Skip(); } -void wxGridWindow::OnEraseBackground(wxEraseEvent&) -{ } +// We are trapping erase background events to reduce flicker under MSW +// and GTK but this can leave junk in the space beyond the last row and +// col. So here we paint these spaces if they are visible. +// +void wxGridWindow::OnEraseBackground(wxEraseEvent& event) +{ + int cw, ch; + GetClientSize( &cw, &ch ); + + int right, bottom; + m_owner->CalcUnscrolledPosition( cw, ch, &right, &bottom ); + + wxRect rightRect; + rightRect = m_owner->CellToRect( 0, m_owner->GetNumberCols()-1 ); + + wxRect bottomRect; + bottomRect = m_owner->CellToRect( m_owner->GetNumberRows()-1, 0 ); + + if ( right > rightRect.GetRight() || bottom > bottomRect.GetBottom() ) + { + int left, top; + m_owner->CalcUnscrolledPosition( 0, 0, &left, &top ); + + wxClientDC dc( this ); + m_owner->PrepareDC( dc ); + dc.SetBrush( wxBrush(m_owner->GetDefaultCellBackgroundColour(), wxSOLID) ); + dc.SetPen( *wxTRANSPARENT_PEN ); + if ( right > rightRect.GetRight() ) + dc.DrawRectangle( rightRect.GetRight()+1, top, right - rightRect.GetRight(), ch ); + if ( bottom > bottomRect.GetBottom() ) + dc.DrawRectangle( left, bottomRect.GetBottom()+1, cw, bottom - bottomRect.GetBottom() ); + } +} ////////////////////////////////////////////////////////////////////// @@ -1989,7 +2646,8 @@ wxGrid::wxGrid( wxWindow *parent, const wxSize& size, long style, const wxString& name ) - : wxScrolledWindow( parent, id, pos, size, style, name ) + : wxScrolledWindow( parent, id, pos, size, style, name ), + m_colMinWidths(wxKEY_INTEGER, GRID_HASH_SIZE) { Create(); } @@ -2010,6 +2668,8 @@ wxGrid::~wxGrid() if (m_ownTable) delete m_table; + + delete m_typeRegistry; } @@ -2029,7 +2689,16 @@ void wxGrid::Create() m_defaultCellAttr = new wxGridCellAttr; m_defaultCellAttr->SetDefAttr(m_defaultCellAttr); - // RD: Should we fill the default attrs now or is waiting until Init() okay? + + // Set default cell attributes + m_defaultCellAttr->SetFont(GetFont()); + m_defaultCellAttr->SetAlignment(wxLEFT, wxTOP); + m_defaultCellAttr->SetTextColour( + wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT)); + m_defaultCellAttr->SetBackgroundColour( + wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); + m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer); + m_defaultCellAttr->SetEditor(new wxGridCellTextEditor); m_numRows = 0; @@ -2039,6 +2708,20 @@ void wxGrid::Create() m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT; + // data type registration: register all standard data types + // TODO: may be allow the app to selectively disable some of them? + m_typeRegistry = new wxGridTypeRegistry; + RegisterDataType(wxGRID_VALUE_STRING, + new wxGridCellStringRenderer, + new wxGridCellTextEditor); + RegisterDataType(wxGRID_VALUE_BOOL, + new wxGridCellBoolRenderer, + new wxGridCellBoolEditor); + RegisterDataType(wxGRID_VALUE_NUMBER, + new wxGridCellNumberRenderer, + new wxGridCellNumberEditor); + + // subwindow components that make up the wxGrid m_cornerLabelWin = new wxGridCornerLabelWindow( this, -1, wxDefaultPosition, @@ -2118,8 +2801,6 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership ) void wxGrid::Init() { - int i; - if ( m_numRows <= 0 ) m_numRows = WXGRID_DEFAULT_NUMBER_ROWS; @@ -2163,42 +2844,13 @@ void wxGrid::Init() m_defaultRowHeight += 4; #endif - m_rowHeights.Alloc( m_numRows ); - m_rowBottoms.Alloc( m_numRows ); - int rowBottom = 0; - for ( i = 0; i < m_numRows; i++ ) - { - m_rowHeights.Add( m_defaultRowHeight ); - rowBottom += m_defaultRowHeight; - m_rowBottoms.Add( rowBottom ); - } - - m_colWidths.Alloc( m_numCols ); - m_colRights.Alloc( m_numCols ); - int colRight = 0; - for ( i = 0; i < m_numCols; i++ ) - { - m_colWidths.Add( m_defaultColWidth ); - colRight += m_defaultColWidth; - m_colRights.Add( colRight ); - } - - // Set default cell attributes - m_defaultCellAttr->SetFont(GetFont()); - m_defaultCellAttr->SetAlignment(wxLEFT, wxTOP); - m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer); - m_defaultCellAttr->SetEditor(new wxGridCellTextEditor); - m_defaultCellAttr->SetTextColour( - wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT)); - m_defaultCellAttr->SetBackgroundColour( - wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); - - m_gridLineColour = wxColour( 128, 128, 255 ); m_gridLinesEnabled = TRUE; m_cursorMode = WXGRID_CURSOR_SELECT_CELL; m_winCapture = (wxWindow *)NULL; + m_canDragRowSize = TRUE; + m_canDragColSize = TRUE; m_dragLastPos = -1; m_dragRowOrCol = -1; m_isDragging = FALSE; @@ -2220,9 +2872,84 @@ void wxGrid::Init() m_inOnKeyDown = FALSE; m_batchCount = 0; +} + +// ---------------------------------------------------------------------------- +// the idea is to call these functions only when necessary because they create +// quite big arrays which eat memory mostly unnecessary - in particular, if +// default widths/heights are used for all rows/columns, we may not use these +// arrays at all +// +// with some extra code, it should be possible to only store the +// widths/heights different from default ones but this will be done later... +// ---------------------------------------------------------------------------- + +void wxGrid::InitRowHeights() +{ + m_rowHeights.Empty(); + m_rowBottoms.Empty(); + + m_rowHeights.Alloc( m_numRows ); + m_rowBottoms.Alloc( m_numRows ); + + int rowBottom = 0; + for ( int i = 0; i < m_numRows; i++ ) + { + m_rowHeights.Add( m_defaultRowHeight ); + rowBottom += m_defaultRowHeight; + m_rowBottoms.Add( rowBottom ); + } +} + +void wxGrid::InitColWidths() +{ + m_colWidths.Empty(); + m_colRights.Empty(); + + m_colWidths.Alloc( m_numCols ); + m_colRights.Alloc( m_numCols ); + int colRight = 0; + for ( int i = 0; i < m_numCols; i++ ) + { + m_colWidths.Add( m_defaultColWidth ); + colRight += m_defaultColWidth; + m_colRights.Add( colRight ); + } +} + +int wxGrid::GetColWidth(int col) const +{ + return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col]; +} + +int wxGrid::GetColLeft(int col) const +{ + return m_colRights.IsEmpty() ? col * m_defaultColWidth + : m_colRights[col] - m_colWidths[col]; +} +int wxGrid::GetColRight(int col) const +{ + return m_colRights.IsEmpty() ? (col + 1) * m_defaultColWidth + : m_colRights[col]; +} + +int wxGrid::GetRowHeight(int row) const +{ + return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row]; } +int wxGrid::GetRowTop(int row) const +{ + return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight + : m_rowBottoms[row] - m_rowHeights[row]; +} + +int wxGrid::GetRowBottom(int row) const +{ + return m_rowBottoms.IsEmpty() ? (row + 1) * m_defaultRowHeight + : m_rowBottoms[row]; +} void wxGrid::CalcDimensions() { @@ -2231,8 +2958,8 @@ void wxGrid::CalcDimensions() if ( m_numRows > 0 && m_numCols > 0 ) { - int right = m_colRights[ m_numCols-1 ] + 50; - int bottom = m_rowBottoms[ m_numRows-1 ] + 50; + int right = GetColRight( m_numCols-1 ) + 50; + int bottom = GetRowBottom( m_numRows-1 ) + 50; // TODO: restore the scroll position that we had before sizing // @@ -2271,6 +2998,18 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) { int i; + // if we were using the default widths/heights so far, we must change them + // now + if ( m_colWidths.IsEmpty() ) + { + InitColWidths(); + } + + if ( m_rowHeights.IsEmpty() ) + { + InitRowHeights(); + } + switch ( msg.GetId() ) { case wxGRIDTABLE_NOTIFY_ROWS_INSERTED: @@ -2476,13 +3215,13 @@ void wxGrid::CalcRowLabelsExposed( wxRegion& reg ) // find the row labels within these bounds // int row; - int rowTop; for ( row = 0; row < m_numRows; row++ ) { - if ( m_rowBottoms[row] < top ) continue; + if ( GetRowBottom(row) < top ) + continue; - rowTop = m_rowBottoms[row] - m_rowHeights[row]; - if ( rowTop > bottom ) break; + if ( GetRowTop(row) > bottom ) + break; m_rowLabelsExposed.Add( row ); } @@ -2525,13 +3264,13 @@ void wxGrid::CalcColLabelsExposed( wxRegion& reg ) // find the cells within these bounds // int col; - int colLeft; for ( col = 0; col < m_numCols; col++ ) { - if ( m_colRights[col] < left ) continue; + if ( GetColRight(col) < left ) + continue; - colLeft = m_colRights[col] - m_colWidths[col]; - if ( colLeft > right ) break; + if ( GetColLeft(col) > right ) + break; m_colLabelsExposed.Add( col ); } @@ -2577,29 +3316,31 @@ void wxGrid::CalcCellsExposed( wxRegion& reg ) // find the cells within these bounds // int row, col; - int colLeft, rowTop; for ( row = 0; row < m_numRows; row++ ) { - if ( m_rowBottoms[row] <= top ) continue; + if ( GetRowBottom(row) <= top ) + continue; - rowTop = m_rowBottoms[row] - m_rowHeights[row]; - if ( rowTop > bottom ) break; + if ( GetRowTop(row) > bottom ) + break; m_rowsExposed.Add( row ); for ( col = 0; col < m_numCols; col++ ) { - if ( m_colRights[col] <= left ) continue; + if ( GetColRight(col) <= left ) + continue; - colLeft = m_colRights[col] - m_colWidths[col]; - if ( colLeft > right ) break; + if ( GetColLeft(col) > right ) + break; - if ( m_colsExposed.Index( col ) == wxNOT_FOUND ) m_colsExposed.Add( col ); + if ( m_colsExposed.Index( col ) == wxNOT_FOUND ) + m_colsExposed.Add( col ); m_cellsExposed.Add( wxGridCellCoords( row, col ) ); } } - iter++ ; + iter++; } } @@ -2626,10 +3367,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) wxClientDC dc( m_gridWin ); PrepareDC( dc ); - y = wxMax( y, - m_rowBottoms[m_dragRowOrCol] - - m_rowHeights[m_dragRowOrCol] + - WXGRID_MIN_ROW_HEIGHT ); + y = wxMax( y, GetRowTop(m_dragRowOrCol) + WXGRID_MIN_ROW_HEIGHT ); dc.SetLogicalFunction(wxINVERT); if ( m_dragLastPos >= 0 ) { @@ -2689,7 +3427,8 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) { // starting to drag-resize a row // - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); + if ( CanDragRowSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin); } } @@ -2759,7 +3498,8 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { // don't capture the mouse yet - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, FALSE); + if ( CanDragRowSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, FALSE); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) @@ -2792,10 +3532,9 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) wxClientDC dc( m_gridWin ); PrepareDC( dc ); - x = wxMax( x, - m_colRights[m_dragRowOrCol] - - m_colWidths[m_dragRowOrCol] + - WXGRID_MIN_COL_WIDTH ); + + x = wxMax( x, GetColLeft(m_dragRowOrCol) + + GetColMinimalWidth(m_dragRowOrCol)); dc.SetLogicalFunction(wxINVERT); if ( m_dragLastPos >= 0 ) { @@ -2855,7 +3594,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) { // starting to drag-resize a col // - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin); + if ( CanDragColSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin); } } @@ -2925,7 +3665,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { // don't capture the cursor yet - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, FALSE); + if ( CanDragColSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, FALSE); } } else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL ) @@ -3103,10 +3844,7 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) wxClientDC dc( m_gridWin ); PrepareDC( dc ); - y = wxMax( y, - m_rowBottoms[m_dragRowOrCol] - - m_rowHeights[m_dragRowOrCol] + - WXGRID_MIN_ROW_HEIGHT ); + y = wxMax( y, GetRowTop(m_dragRowOrCol) + WXGRID_MIN_ROW_HEIGHT ); dc.SetLogicalFunction(wxINVERT); if ( m_dragLastPos >= 0 ) { @@ -3123,9 +3861,8 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) wxClientDC dc( m_gridWin ); PrepareDC( dc ); - x = wxMax( x, - m_colRights[m_dragRowOrCol] - - m_colWidths[m_dragRowOrCol] + WXGRID_MIN_COL_WIDTH ); + x = wxMax( x, GetColLeft(m_dragRowOrCol) + + GetColMinimalWidth(m_dragRowOrCol) ); dc.SetLogicalFunction(wxINVERT); if ( m_dragLastPos >= 0 ) { @@ -3184,7 +3921,7 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) EnableCellEditControl(); wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords); - attr->GetEditor()->StartingClick(); + attr->GetEditor(GetDefaultEditorForCell(coords.GetRow(), coords.GetCol()))->StartingClick(); attr->DecRef(); m_waitForSlowClick = FALSE; @@ -3310,7 +4047,8 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW); + if ( CanDragRowSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW); } return; @@ -3322,7 +4060,8 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL); + if ( CanDragColSize() ) + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL); } return; @@ -3355,7 +4094,7 @@ void wxGrid::DoEndDragResizeRow() dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos ); HideCellEditControl(); - int rowTop = m_rowBottoms[m_dragRowOrCol] - m_rowHeights[m_dragRowOrCol]; + int rowTop = GetRowTop(m_dragRowOrCol); SetRowSize( m_dragRowOrCol, wxMax( m_dragLastPos - rowTop, WXGRID_MIN_ROW_HEIGHT ) ); @@ -3393,9 +4132,10 @@ void wxGrid::DoEndDragResizeCol() dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch ); HideCellEditControl(); - int colLeft = m_colRights[m_dragRowOrCol] - m_colWidths[m_dragRowOrCol]; + int colLeft = GetColLeft(m_dragRowOrCol); SetColSize( m_dragRowOrCol, - wxMax( m_dragLastPos - colLeft, WXGRID_MIN_COL_WIDTH ) ); + wxMax( m_dragLastPos - colLeft, + GetColMinimalWidth(m_dragRowOrCol) ) ); if ( !GetBatchCount() ) { @@ -3944,8 +4684,10 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) if ( !IsCellEditControlEnabled() && CanEnableCellControl() ) { EnableCellEditControl(); - wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords); - attr->GetEditor()->StartingKey(event); + int row = m_currentCellCoords.GetRow(); + int col = m_currentCellCoords.GetCol(); + wxGridCellAttr* attr = GetCellAttr(row, col); + attr->GetEditor(GetDefaultEditorForCell(row, col))->StartingKey(event); attr->DecRef(); } else @@ -4075,7 +4817,7 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) int row = coords.GetRow(); int col = coords.GetCol(); - if ( m_colWidths[col] <= 0 || m_rowHeights[row] <= 0 ) + if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) return; // we draw the cell border ourselves @@ -4089,21 +4831,24 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) bool isCurrent = coords == m_currentCellCoords; wxRect rect; - rect.x = m_colRights[col] - m_colWidths[col]; - rect.y = m_rowBottoms[row] - m_rowHeights[row]; - rect.width = m_colWidths[col] - 1; - rect.height = m_rowHeights[row] - 1; + rect.x = GetColLeft(col); + rect.y = GetRowTop(row); + rect.width = GetColWidth(col) - 1; + rect.height = GetRowHeight(row) - 1; // if the editor is shown, we should use it and not the renderer if ( isCurrent && IsCellEditControlEnabled() ) { - attr->GetEditor()->PaintBackground(rect, attr); + attr->GetEditor(GetDefaultEditorForCell(row, col))-> + PaintBackground(rect, attr); } else { // but all the rest is drawn by the cell renderer and hence may be // customized - attr->GetRenderer()->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords)); + attr->GetRenderer(GetDefaultRendererForCell(row,col))-> + Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords)); + } attr->DecRef(); @@ -4114,14 +4859,14 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); - if ( m_colWidths[col] <= 0 || m_rowHeights[row] <= 0 ) + if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) return; wxRect rect; - rect.x = m_colRights[col] - m_colWidths[col]; - rect.y = m_rowBottoms[row] - m_rowHeights[row]; - rect.width = m_colWidths[col] - 1; - rect.height = m_rowHeights[row] - 1; + rect.x = GetColLeft(col); + rect.y = GetRowTop(row); + rect.width = GetColWidth(col) - 1; + rect.height = GetRowHeight(row) - 1; // hmmm... what could we do here to show that the cell is disabled? // for now, I just draw a thinner border than for the other ones, but @@ -4153,28 +4898,35 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) #endif // 0 } + void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords ) { - if ( m_colWidths[coords.GetCol()] <=0 || - m_rowHeights[coords.GetRow()] <= 0 ) return; - - dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) ); int row = coords.GetRow(); int col = coords.GetCol(); + if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) + return; + + dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) ); // right hand border // - dc.DrawLine( m_colRights[col], m_rowBottoms[row] - m_rowHeights[row], - m_colRights[col], m_rowBottoms[row] ); + dc.DrawLine( GetColRight(col), GetRowTop(row), + GetColRight(col), GetRowBottom(row) ); // bottom border // - dc.DrawLine( m_colRights[col] - m_colWidths[col], m_rowBottoms[row], - m_colRights[col], m_rowBottoms[row] ); + dc.DrawLine( GetColLeft(col), GetRowBottom(row), + GetColRight(col), GetRowBottom(row) ); } void wxGrid::DrawHighlight(wxDC& dc) { + if ( IsCellEditControlEnabled() ) + { + // don't show highlight when the edit control is shown + return; + } + // if the active cell was repainted, repaint its highlight too because it // might have been damaged by the grid lines size_t count = m_cellsExposed.GetCount(); @@ -4223,8 +4975,8 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg ) // avoid drawing grid lines past the last row and col // - right = wxMin( right, m_colRights[m_numCols-1] ); - bottom = wxMin( bottom, m_rowBottoms[m_numRows-1] ); + right = wxMin( right, GetColRight(m_numCols - 1) ); + bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) ); dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) ); @@ -4233,13 +4985,16 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg ) int i; for ( i = 0; i < m_numRows; i++ ) { - if ( m_rowBottoms[i]-1 > bottom ) + int bot = GetRowBottom(i) - 1; + + if ( bot > bottom ) { break; } - else if ( m_rowBottoms[i]-1 >= top ) + + if ( bot >= top ) { - dc.DrawLine( left, m_rowBottoms[i]-1, right, m_rowBottoms[i]-1 ); + dc.DrawLine( left, bot, right, bot ); } } @@ -4248,13 +5003,15 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & reg ) // for ( i = 0; i < m_numCols; i++ ) { - if ( m_colRights[i]-1 > right ) + int colRight = GetColRight(i) - 1; + if ( colRight > right ) { break; } - else if ( m_colRights[i]-1 >= left ) + + if ( colRight >= left ) { - dc.DrawLine( m_colRights[i]-1, top, m_colRights[i]-1, bottom ); + dc.DrawLine( colRight, top, colRight, bottom ); } } } @@ -4276,19 +5033,20 @@ void wxGrid::DrawRowLabels( wxDC& dc ) void wxGrid::DrawRowLabel( wxDC& dc, int row ) { - if ( m_rowHeights[row] <= 0 ) return; + if ( GetRowHeight(row) <= 0 ) + return; - int rowTop = m_rowBottoms[row] - m_rowHeights[row]; + int rowTop = GetRowTop(row), + rowBottom = GetRowBottom(row) - 1; dc.SetPen( *wxBLACK_PEN ); dc.DrawLine( m_rowLabelWidth-1, rowTop, - m_rowLabelWidth-1, m_rowBottoms[row]-1 ); + m_rowLabelWidth-1, rowBottom ); - dc.DrawLine( 0, m_rowBottoms[row]-1, - m_rowLabelWidth-1, m_rowBottoms[row]-1 ); + dc.DrawLine( 0, rowBottom, m_rowLabelWidth-1, rowBottom ); dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( 0, rowTop, 0, m_rowBottoms[row]-1 ); + dc.DrawLine( 0, rowTop, 0, rowBottom ); dc.DrawLine( 0, rowTop, m_rowLabelWidth-1, rowTop ); dc.SetBackgroundMode( wxTRANSPARENT ); @@ -4300,9 +5058,9 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) wxRect rect; rect.SetX( 2 ); - rect.SetY( m_rowBottoms[row] - m_rowHeights[row] + 2 ); + rect.SetY( GetRowTop(row) + 2 ); rect.SetWidth( m_rowLabelWidth - 4 ); - rect.SetHeight( m_rowHeights[row] - 4 ); + rect.SetHeight( GetRowHeight(row) - 4 ); DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); } @@ -4323,20 +5081,22 @@ void wxGrid::DrawColLabels( wxDC& dc ) void wxGrid::DrawColLabel( wxDC& dc, int col ) { - if ( m_colWidths[col] <= 0 ) return; + if ( GetColWidth(col) <= 0 ) + return; - int colLeft = m_colRights[col] - m_colWidths[col]; + int colLeft = GetColLeft(col), + colRight = GetColRight(col) - 1; dc.SetPen( *wxBLACK_PEN ); - dc.DrawLine( m_colRights[col]-1, 0, - m_colRights[col]-1, m_colLabelHeight-1 ); + dc.DrawLine( colRight, 0, + colRight, m_colLabelHeight-1 ); dc.DrawLine( colLeft, m_colLabelHeight-1, - m_colRights[col]-1, m_colLabelHeight-1 ); + colRight, m_colLabelHeight-1 ); dc.SetPen( *wxWHITE_PEN ); dc.DrawLine( colLeft, 0, colLeft, m_colLabelHeight-1 ); - dc.DrawLine( colLeft, 0, m_colRights[col]-1, 0 ); + dc.DrawLine( colLeft, 0, colRight, 0 ); dc.SetBackgroundMode( wxTRANSPARENT ); dc.SetTextForeground( GetLabelTextColour() ); @@ -4350,9 +5110,9 @@ void wxGrid::DrawColLabel( wxDC& dc, int col ) GetColLabelAlignment( &hAlign, &vAlign ); wxRect rect; - rect.SetX( m_colRights[col] - m_colWidths[col] + 2 ); + rect.SetX( colLeft + 2 ); rect.SetY( 2 ); - rect.SetWidth( m_colWidths[col] - 4 ); + rect.SetWidth( GetColWidth(col) - 4 ); rect.SetHeight( m_colLabelHeight - 4 ); DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign ); } @@ -4553,11 +5313,6 @@ bool wxGrid::IsCellEditControlEnabled() const return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : FALSE; } -wxWindow *wxGrid::GetGridWindow() const -{ - return m_gridWin; -} - void wxGrid::ShowCellEditControl() { if ( IsCellEditControlEnabled() ) @@ -4574,61 +5329,25 @@ void wxGrid::ShowCellEditControl() // convert to scrolled coords // - int left, top, right, bottom; - CalcScrolledPosition( rect.GetLeft(), rect.GetTop(), &left, &top ); - CalcScrolledPosition( rect.GetRight(), rect.GetBottom(), &right, &bottom ); - - // cell is shifted by one pixel - left--; - top--; - right--; - bottom--; + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); - // Make the edit control large enough to allow for internal - // margins - // - // TODO: remove this if the text ctrl sizing is improved esp. for - // unix - // - int extra; -#if defined(__WXMOTIF__) - if ( row == 0 || col == 0 ) - { - extra = 2; - } - else - { - extra = 4; - } -#else - if ( row == 0 || col == 0 ) - { - extra = 1; - } - else - { - extra = 2; - } -#endif + // done in PaintBackground() +#if 0 + // erase the highlight and the cell contents because the editor + // might not cover the entire cell + wxClientDC dc( m_gridWin ); + PrepareDC( dc ); + dc.SetBrush(*wxLIGHT_GREY_BRUSH); //wxBrush(attr->GetBackgroundColour(), wxSOLID)); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(rect); +#endif // 0 -#if defined(__WXGTK__) - int top_diff = 0; - int left_diff = 0; - if (left != 0) left_diff++; - if (top != 0) top_diff++; - rect.SetLeft( left + left_diff ); - rect.SetTop( top + top_diff ); - rect.SetRight( rect.GetRight() - left_diff ); - rect.SetBottom( rect.GetBottom() - top_diff ); -#else - rect.SetLeft( wxMax(0, left - extra) ); - rect.SetTop( wxMax(0, top - extra) ); - rect.SetRight( rect.GetRight() + 2*extra ); - rect.SetBottom( rect.GetBottom() + 2*extra ); -#endif + // cell is shifted by one pixel + rect.x--; + rect.y--; wxGridCellAttr* attr = GetCellAttr(row, col); - wxGridCellEditor* editor = attr->GetEditor(); + wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col)); if ( !editor->IsCreated() ) { editor->Create(m_gridWin, -1, @@ -4636,6 +5355,7 @@ void wxGrid::ShowCellEditControl() } editor->SetSize( rect ); + editor->Show( TRUE, attr ); editor->BeginEdit(row, col, this); attr->DecRef(); @@ -4652,7 +5372,7 @@ void wxGrid::HideCellEditControl() int col = m_currentCellCoords.GetCol(); wxGridCellAttr* attr = GetCellAttr(row, col); - attr->GetEditor()->Show( FALSE ); + attr->GetEditor(GetDefaultEditorForCell(row, col))->Show( FALSE ); attr->DecRef(); m_gridWin->SetFocus(); } @@ -4674,7 +5394,8 @@ void wxGrid::SaveEditControlValue() int col = m_currentCellCoords.GetCol(); wxGridCellAttr* attr = GetCellAttr(row, col); - bool changed = attr->GetEditor()->EndEdit(row, col, TRUE, this); + wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col)); + bool changed = editor->EndEdit(row, col, TRUE, this); attr->DecRef(); @@ -4717,10 +5438,11 @@ int wxGrid::YToRow( int y ) for ( i = 0; i < m_numRows; i++ ) { - if ( y < m_rowBottoms[i] ) return i; + if ( y < GetRowBottom(i) ) + return i; } - return m_numRows; //-1; + return -1; } @@ -4730,10 +5452,11 @@ int wxGrid::XToCol( int x ) for ( i = 0; i < m_numCols; i++ ) { - if ( x < m_colRights[i] ) return i; + if ( x < GetColRight(i) ) + return i; } - return m_numCols; //-1; + return -1; } @@ -4746,12 +5469,11 @@ int wxGrid::YToEdgeOfRow( int y ) for ( i = 0; i < m_numRows; i++ ) { - if ( m_rowHeights[i] > WXGRID_LABEL_EDGE_ZONE ) + if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE ) { - d = abs( y - m_rowBottoms[i] ); - { - if ( d < WXGRID_LABEL_EDGE_ZONE ) return i; - } + d = abs( y - GetRowBottom(i) ); + if ( d < WXGRID_LABEL_EDGE_ZONE ) + return i; } } @@ -4768,12 +5490,11 @@ int wxGrid::XToEdgeOfCol( int x ) for ( i = 0; i < m_numCols; i++ ) { - if ( m_colWidths[i] > WXGRID_LABEL_EDGE_ZONE ) + if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE ) { - d = abs( x - m_colRights[i] ); - { - if ( d < WXGRID_LABEL_EDGE_ZONE ) return i; - } + d = abs( x - GetColRight(i) ); + if ( d < WXGRID_LABEL_EDGE_ZONE ) + return i; } } @@ -4788,10 +5509,10 @@ wxRect wxGrid::CellToRect( int row, int col ) if ( row >= 0 && row < m_numRows && col >= 0 && col < m_numCols ) { - rect.x = m_colRights[col] - m_colWidths[col]; - rect.y = m_rowBottoms[row] - m_rowHeights[row]; - rect.width = m_colWidths[col]; - rect.height = m_rowHeights[ row ]; + rect.x = GetColLeft(col); + rect.y = GetRowTop(row); + rect.width = GetColWidth(col); + rect.height = GetRowHeight(row); } return rect; @@ -4866,10 +5587,12 @@ void wxGrid::MakeCellVisible( int row, int col ) ypos = r.GetTop(); for ( i = row-1; i >= 0; i-- ) { - if ( h + m_rowHeights[i] > ch ) break; + int rowHeight = GetRowHeight(i); + if ( h + rowHeight > ch ) + break; - h += m_rowHeights[i]; - ypos -= m_rowHeights[i]; + h += rowHeight; + ypos -= rowHeight; } // we divide it later by GRID_SCROLL_LINE, make sure that we don't @@ -4888,10 +5611,12 @@ void wxGrid::MakeCellVisible( int row, int col ) xpos = r.GetLeft(); for ( i = col-1; i >= 0; i-- ) { - if ( w + m_colWidths[i] > cw ) break; + int colWidth = GetColWidth(i); + if ( w + colWidth > cw ) + break; - w += m_colWidths[i]; - xpos -= m_colWidths[i]; + w += colWidth; + xpos -= colWidth; } // see comment for ypos above @@ -4997,7 +5722,7 @@ bool wxGrid::MovePageUp() int cw, ch; m_gridWin->GetClientSize( &cw, &ch ); - int y = m_rowBottoms[ row ] - m_rowHeights[ row ]; + int y = GetRowTop(row); int newRow = YToRow( y - ch + 1 ); if ( newRow == -1 ) { @@ -5027,7 +5752,7 @@ bool wxGrid::MovePageDown() int cw, ch; m_gridWin->GetClientSize( &cw, &ch ); - int y = m_rowBottoms[ row ] - m_rowHeights[ row ]; + int y = GetRowTop(row); int newRow = YToRow( y + ch ); if ( newRow == -1 ) { @@ -5519,7 +6244,7 @@ int wxGrid::GetRowSize( int row ) { wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") ); - return m_rowHeights[row]; + return GetRowHeight(row); } int wxGrid::GetDefaultColSize() @@ -5531,7 +6256,7 @@ int wxGrid::GetColSize( int col ) { wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") ); - return m_colWidths[col]; + return GetColWidth(col); } // ============================================================================ @@ -5602,12 +6327,12 @@ void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) wxGridCellRenderer *wxGrid::GetDefaultRenderer() const { - return m_defaultCellAttr->GetRenderer(); + return m_defaultCellAttr->GetRenderer(NULL); } wxGridCellEditor *wxGrid::GetDefaultEditor() const { - return m_defaultCellAttr->GetEditor(); + return m_defaultCellAttr->GetEditor(NULL); } // ---------------------------------------------------------------------------- @@ -5648,7 +6373,7 @@ void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) { wxGridCellAttr* attr = GetCellAttr(row, col); - wxGridCellRenderer* renderer = attr->GetRenderer(); + wxGridCellRenderer* renderer = attr->GetRenderer(GetDefaultRendererForCell(row,col)); attr->DecRef(); return renderer; } @@ -5656,7 +6381,7 @@ wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) { wxGridCellAttr* attr = GetCellAttr(row, col); - wxGridCellEditor* editor = attr->GetEditor(); + wxGridCellEditor* editor = attr->GetEditor(GetDefaultEditorForCell(row, col)); attr->DecRef(); return editor; } @@ -5680,19 +6405,7 @@ bool wxGrid::CanHaveAttributes() return FALSE; } - // RD: Maybe m_table->CanHaveAttributes() would be better in case the - // table is providing the attributes itself??? In which case - // I don't think the grid should create a Provider object for the - // table but the table should be smart enough to do that on its own. - if ( !m_table->GetAttrProvider() ) - { - // use the default attr provider by default - // (another choice would be to just return FALSE thus forcing the user - // to it himself) - m_table->SetAttrProvider(new wxGridCellAttrProvider); - } - - return TRUE; + return m_table->CanHaveAttributes(); } void wxGrid::ClearAttrCache() @@ -5882,24 +6595,79 @@ void wxGrid::SetReadOnly(int row, int col, bool isReadOnly) } } +// ---------------------------------------------------------------------------- +// Data type registration +// ---------------------------------------------------------------------------- + +void wxGrid::RegisterDataType(const wxString& typeName, + wxGridCellRenderer* renderer, + wxGridCellEditor* editor) +{ + m_typeRegistry->RegisterDataType(typeName, renderer, editor); +} + + +wxGridCellEditor* wxGrid::GetDefaultEditorForCell(int row, int col) const +{ + wxString typeName = m_table->GetTypeName(row, col); + return GetDefaultEditorForType(typeName); +} + +wxGridCellRenderer* wxGrid::GetDefaultRendererForCell(int row, int col) const +{ + wxString typeName = m_table->GetTypeName(row, col); + return GetDefaultRendererForType(typeName); +} + +wxGridCellEditor* +wxGrid::GetDefaultEditorForType(const wxString& typeName) const +{ + int index = m_typeRegistry->FindDataType(typeName); + if (index == -1) { + // Should we force the failure here or let it fallback to string handling??? + // wxFAIL_MSG(wxT("Unknown data type name")); + return NULL; + } + return m_typeRegistry->GetEditor(index); +} + +wxGridCellRenderer* +wxGrid::GetDefaultRendererForType(const wxString& typeName) const +{ + int index = m_typeRegistry->FindDataType(typeName); + if (index == -1) { + // Should we force the failure here or let it fallback to string handling??? + // wxFAIL_MSG(wxT("Unknown data type name")); + return NULL; + } + return m_typeRegistry->GetRenderer(index); +} + + // ---------------------------------------------------------------------------- // row/col size // ---------------------------------------------------------------------------- +void wxGrid::EnableDragRowSize( bool enable ) +{ + m_canDragRowSize = enable; +} + + +void wxGrid::EnableDragColSize( bool enable ) +{ + m_canDragColSize = enable; +} + + void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows ) { m_defaultRowHeight = wxMax( height, WXGRID_MIN_ROW_HEIGHT ); if ( resizeExistingRows ) { - int row; - int bottom = 0; - for ( row = 0; row < m_numRows; row++ ) - { - m_rowHeights[row] = m_defaultRowHeight; - bottom += m_defaultRowHeight; - m_rowBottoms[row] = bottom; - } + InitRowHeights(); + CalcDimensions(); } } @@ -5908,12 +6676,17 @@ void wxGrid::SetRowSize( int row, int height ) { wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") ); - int i; + if ( m_rowHeights.IsEmpty() ) + { + // need to really create the array + InitRowHeights(); + } int h = wxMax( 0, height ); int diff = h - m_rowHeights[row]; m_rowHeights[row] = h; + int i; for ( i = row; i < m_numRows; i++ ) { m_rowBottoms[i] += diff; @@ -5927,14 +6700,8 @@ void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols ) if ( resizeExistingCols ) { - int col; - int right = 0; - for ( col = 0; col < m_numCols; col++ ) - { - m_colWidths[col] = m_defaultColWidth; - right += m_defaultColWidth; - m_colRights[col] = right; - } + InitColWidths(); + CalcDimensions(); } } @@ -5943,12 +6710,19 @@ void wxGrid::SetColSize( int col, int width ) { wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") ); - int i; + // should we check that it's bigger than GetColMinimalWidth(col) here? + + if ( m_colWidths.IsEmpty() ) + { + // need to really create the array + InitColWidths(); + } int w = wxMax( 0, width ); int diff = w - m_colWidths[col]; m_colWidths[col] = w; + int i; for ( i = col; i < m_numCols; i++ ) { m_colRights[i] += diff; @@ -5957,6 +6731,73 @@ void wxGrid::SetColSize( int col, int width ) } +void wxGrid::SetColMinimalWidth( int col, int width ) +{ + m_colMinWidths.Put(col, (wxObject *)width); +} + +int wxGrid::GetColMinimalWidth(int col) const +{ + wxObject *obj = m_colMinWidths.Get(m_dragRowOrCol); + return obj ? (int)obj : WXGRID_MIN_COL_WIDTH; +} + +void wxGrid::AutoSizeColumn( int col, bool setAsMin ) +{ + wxClientDC dc(m_gridWin); + + wxCoord width, widthMax = 0; + for ( int row = 0; row < m_numRows; row++ ) + { + wxGridCellAttr* attr = GetCellAttr(row, col); + wxGridCellRenderer* renderer = attr->GetRenderer(GetDefaultRendererForCell(row,col)); + if ( renderer ) + { + width = renderer->GetBestSize(*this, *attr, dc, row, col).x; + if ( width > widthMax ) + { + widthMax = width; + } + } + + attr->DecRef(); + } + + // now also compare with the column label width + dc.SetFont( GetLabelFont() ); + dc.GetTextExtent( GetColLabelValue(col), &width, NULL ); + if ( width > widthMax ) + { + widthMax = width; + } + + if ( !widthMax ) + { + // empty column - give default width (notice that if widthMax is less + // than default width but != 0, it's ok) + widthMax = m_defaultColWidth; + } + else + { + // leave some space around text + widthMax += 10; + } + + SetColSize(col, widthMax); + if ( setAsMin ) + { + SetColMinimalWidth(col, widthMax); + } +} + +void wxGrid::AutoSizeColumns( bool setAsMin ) +{ + for ( int col = 0; col < m_numCols; col++ ) + { + AutoSizeColumn(col, setAsMin); + } +} + // // ------ cell value accessor functions //