X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2121eb69fa8d49f4484f7170159e61f2e8885de4..6d0d84554854a19db991ba64e7e034e1fc898ef0:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 68c6411e8f..bdd93204b5 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -18,6 +18,8 @@ #if wxUSE_GRID +#include "wx/grid.h" + #ifndef WX_PRECOMP #include "wx/utils.h" #include "wx/dcclient.h" @@ -37,9 +39,10 @@ #include "wx/tokenzr.h" #include "wx/renderer.h" -#include "wx/grid.h" #include "wx/generic/gridsel.h" +const wxChar wxGridNameStr[] = wxT("grid"); + #if defined(__WXMOTIF__) #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier) #else @@ -67,6 +70,37 @@ struct wxGridCellWithAttr wxGridCellWithAttr(int row, int col, wxGridCellAttr *attr_) : coords(row, col), attr(attr_) { + wxASSERT( attr ); + } + + wxGridCellWithAttr(const wxGridCellWithAttr& other) + : coords(other.coords), + attr(other.attr) + { + attr->IncRef(); + } + + wxGridCellWithAttr& operator=(const wxGridCellWithAttr& other) + { + coords = other.coords; + if (attr != other.attr) + { + attr->DecRef(); + attr = other.attr; + attr->IncRef(); + } + return *this; + } + + void ChangeAttr(wxGridCellAttr* new_attr) + { + if (attr != new_attr) + { + // "Delete" (i.e. DecRef) the old attribute. + attr->DecRef(); + attr = new_attr; + // Take ownership of the new attribute, i.e. no IncRef. + } } ~wxGridCellWithAttr() @@ -76,10 +110,6 @@ struct wxGridCellWithAttr wxGridCellCoords coords; wxGridCellAttr *attr; - -// Cannot do this: -// DECLARE_NO_COPY_CLASS(wxGridCellWithAttr) -// without rewriting the macros, which require a public copy constructor. }; WX_DECLARE_OBJARRAY_WITH_DECL(wxGridCellWithAttr, wxGridCellWithAttrArray, @@ -117,22 +147,48 @@ DEFINE_EVENT_TYPE(wxEVT_GRID_EDITOR_CREATED) // private classes // ---------------------------------------------------------------------------- -class WXDLLIMPEXP_ADV wxGridRowLabelWindow : public wxWindow +// common base class for various grid subwindows +class WXDLLIMPEXP_ADV wxGridSubwindow : public wxWindow +{ +public: + wxGridSubwindow() { m_owner = NULL; } + wxGridSubwindow(wxGrid *owner, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + int additionalStyle = 0, + const wxString& name = wxPanelNameStr) + : wxWindow(owner, id, pos, size, + wxBORDER_NONE | additionalStyle, + name) + { + m_owner = owner; + } + + virtual bool AcceptsFocus() const { return false; } + + wxGrid *GetOwner() { return m_owner; } + +protected: + void OnMouseCaptureLost(wxMouseCaptureLostEvent& event); + + wxGrid *m_owner; + + DECLARE_EVENT_TABLE() + DECLARE_NO_COPY_CLASS(wxGridSubwindow) +}; + +class WXDLLIMPEXP_ADV wxGridRowLabelWindow : public wxGridSubwindow { public: - wxGridRowLabelWindow() { m_owner = (wxGrid *)NULL; } + wxGridRowLabelWindow() { } wxGridRowLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ); private: - wxGrid *m_owner; - void OnPaint( wxPaintEvent& event ); void OnMouseEvent( wxMouseEvent& event ); void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); DECLARE_DYNAMIC_CLASS(wxGridRowLabelWindow) DECLARE_EVENT_TABLE() @@ -140,22 +196,17 @@ private: }; -class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxWindow +class WXDLLIMPEXP_ADV wxGridColLabelWindow : public wxGridSubwindow { public: - wxGridColLabelWindow() { m_owner = (wxGrid *)NULL; } + wxGridColLabelWindow() { } wxGridColLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ); private: - wxGrid *m_owner; - void OnPaint( wxPaintEvent& event ); void OnMouseEvent( wxMouseEvent& event ); void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); DECLARE_DYNAMIC_CLASS(wxGridColLabelWindow) DECLARE_EVENT_TABLE() @@ -163,21 +214,16 @@ private: }; -class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxWindow +class WXDLLIMPEXP_ADV wxGridCornerLabelWindow : public wxGridSubwindow { public: - wxGridCornerLabelWindow() { m_owner = (wxGrid *)NULL; } + wxGridCornerLabelWindow() { } wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ); private: - wxGrid *m_owner; - void OnMouseEvent( wxMouseEvent& event ); void OnMouseWheel( wxMouseEvent& event ); - void OnKeyDown( wxKeyEvent& event ); - void OnKeyUp( wxKeyEvent& ); - void OnChar( wxKeyEvent& ); void OnPaint( wxPaintEvent& event ); DECLARE_DYNAMIC_CLASS(wxGridCornerLabelWindow) @@ -185,12 +231,11 @@ private: DECLARE_NO_COPY_CLASS(wxGridCornerLabelWindow) }; -class WXDLLIMPEXP_ADV wxGridWindow : public wxWindow +class WXDLLIMPEXP_ADV wxGridWindow : public wxGridSubwindow { public: wxGridWindow() { - m_owner = NULL; m_rowLabelWin = NULL; m_colLabelWin = NULL; } @@ -199,14 +244,12 @@ public: wxGridRowLabelWindow *rowLblWin, wxGridColLabelWindow *colLblWin, wxWindowID id, const wxPoint &pos, const wxSize &size ); - virtual ~wxGridWindow() {} void ScrollWindow( int dx, int dy, const wxRect *rect ); - wxGrid* GetOwner() { return m_owner; } + virtual bool AcceptsFocus() const { return true; } private: - wxGrid *m_owner; wxGridRowLabelWindow *m_rowLabelWin; wxGridColLabelWindow *m_colLabelWin; @@ -469,7 +512,7 @@ void wxGridCellEditor::PaintBackground(const wxRect& rectCell, gridWindow->GetOwner()->PrepareDC(dc); dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); + dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); dc.DrawRectangle(rectCell); // redraw the control we just painted over @@ -621,17 +664,24 @@ void wxGridCellTextEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) { + DoCreate(parent, id, evtHandler); +} + +void wxGridCellTextEditor::DoCreate(wxWindow* parent, + wxWindowID id, + wxEvtHandler* evtHandler, + long style) +{ + style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER; + m_control = new wxTextCtrl(parent, id, wxEmptyString, - wxDefaultPosition, wxDefaultSize -#if defined(__WXMSW__) - , wxTE_PROCESS_TAB | wxTE_AUTO_SCROLL | wxNO_BORDER -#endif - ); + wxDefaultPosition, wxDefaultSize, + style); // set max length allowed in the textctrl, if the parameter was set - if (m_maxChars != 0) + if ( m_maxChars != 0 ) { - ((wxTextCtrl*)m_control)->SetMaxLength(m_maxChars); + Text()->SetMaxLength(m_maxChars); } wxGridCellEditor::Create(parent, id, evtHandler); @@ -903,7 +953,6 @@ void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid) bool wxGridCellNumberEditor::EndEdit(int row, int col, wxGrid* grid) { - bool changed; long value = 0; wxString text; @@ -911,26 +960,40 @@ bool wxGridCellNumberEditor::EndEdit(int row, int col, if ( HasRange() ) { value = Spin()->GetValue(); - changed = value != m_valueOld; - if (changed) - text = wxString::Format(wxT("%ld"), value); + if ( value == m_valueOld ) + return false; + + text.Printf(wxT("%ld"), value); } - else -#endif + else // using unconstrained input +#endif // wxUSE_SPINCTRL { + const wxString textOld(grid->GetCellValue(row, col)); text = Text()->GetValue(); - changed = (text.empty() || text.ToLong(&value)) && (value != m_valueOld); - } + if ( text.empty() ) + { + if ( textOld.empty() ) + return false; + } + else // non-empty text now (maybe 0) + { + if ( !text.ToLong(&value) ) + return false; - if ( changed ) - { - if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER)) - grid->GetTable()->SetValueAsLong(row, col, value); - else - grid->GetTable()->SetValue(row, col, text); + // if value == m_valueOld == 0 but old text was "" and new one is + // "0" something still did change + if ( value == m_valueOld && (value || !textOld.empty()) ) + return false; + } } - return changed; + wxGridTableBase * const table = grid->GetTable(); + if ( table->CanSetValueAs(row, col, wxGRID_VALUE_NUMBER) ) + table->SetValueAsLong(row, col, value); + else + table->SetValue(row, col, text); + + return true; } void wxGridCellNumberEditor::Reset() @@ -1063,7 +1126,7 @@ void wxGridCellFloatEditor::Create(wxWindow* parent, void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid) { // first get the value - wxGridTableBase *table = grid->GetTable(); + wxGridTableBase * const table = grid->GetTable(); if ( table->CanGetValueAs(row, col, wxGRID_VALUE_FLOAT) ) { m_valueOld = table->GetValueAsDouble(row, col); @@ -1071,35 +1134,53 @@ void wxGridCellFloatEditor::BeginEdit(int row, int col, wxGrid* grid) else { m_valueOld = 0.0; - wxString sValue = table->GetValue(row, col); - if (! sValue.ToDouble(&m_valueOld) && ! sValue.empty()) + + const wxString value = table->GetValue(row, col); + if ( !value.empty() ) { - wxFAIL_MSG( _T("this cell doesn't have float value") ); - return; + if ( !value.ToDouble(&m_valueOld) ) + { + wxFAIL_MSG( _T("this cell doesn't have float value") ); + return; + } } } DoBeginEdit(GetString()); } -bool wxGridCellFloatEditor::EndEdit(int row, int col, - wxGrid* grid) +bool wxGridCellFloatEditor::EndEdit(int row, int col, wxGrid* grid) { - double value = 0.0; - wxString text(Text()->GetValue()); + const wxString text(Text()->GetValue()), + textOld(grid->GetCellValue(row, col)); - if ( (text.empty() || text.ToDouble(&value)) && - !wxIsSameDouble(value, m_valueOld) ) + double value; + if ( !text.empty() ) { - if (grid->GetTable()->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT)) - grid->GetTable()->SetValueAsDouble(row, col, value); - else - grid->GetTable()->SetValue(row, col, text); + if ( !text.ToDouble(&value) ) + return false; + } + else // new value is empty string + { + if ( textOld.empty() ) + return false; // nothing changed - return true; + value = 0.; } - return false; + // the test for empty strings ensures that we don't skip the value setting + // when "" is replaced by "0" or vice versa as "" numeric value is also 0. + if ( wxIsSameDouble(value, m_valueOld) && !text.empty() && !textOld.empty() ) + return false; // nothing changed + + wxGridTableBase * const table = grid->GetTable(); + + if ( table->CanSetValueAs(row, col, wxGRID_VALUE_FLOAT) ) + table->SetValueAsDouble(row, col, value); + else + table->SetValue(row, col, text); + + return true; } void wxGridCellFloatEditor::Reset() @@ -1230,6 +1311,9 @@ bool wxGridCellFloatEditor::IsAcceptedKey(wxKeyEvent& event) // wxGridCellBoolEditor // ---------------------------------------------------------------------------- +// the default values for GetValue() +wxString wxGridCellBoolEditor::ms_stringValues[2] = { _T(""), _T("1") }; + void wxGridCellBoolEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) @@ -1335,7 +1419,19 @@ void wxGridCellBoolEditor::BeginEdit(int row, int col, wxGrid* grid) else { wxString cellval( grid->GetTable()->GetValue(row, col) ); - m_startValue = !( !cellval || (cellval == wxT("0")) ); + + if ( cellval == ms_stringValues[false] ) + m_startValue = false; + else if ( cellval == ms_stringValues[true] ) + m_startValue = true; + else + { + // do not try to be smart here and convert it to true or false + // because we'll still overwrite it with something different and + // this risks to be very surprising for the user code, let them + // know about it + wxFAIL_MSG( _T("invalid value for a cell with bool editor!") ); + } } CBox()->SetValue(m_startValue); @@ -1355,10 +1451,11 @@ bool wxGridCellBoolEditor::EndEdit(int row, int col, if ( changed ) { - if (grid->GetTable()->CanGetValueAs(row, col, wxGRID_VALUE_BOOL)) - grid->GetTable()->SetValueAsBool(row, col, value); + wxGridTableBase * const table = grid->GetTable(); + if ( table->CanGetValueAs(row, col, wxGRID_VALUE_BOOL) ) + table->SetValueAsBool(row, col, value); else - grid->GetTable()->SetValue(row, col, value ? _T("1") : wxEmptyString); + table->SetValue(row, col, GetValue()); } return changed; @@ -1413,12 +1510,23 @@ void wxGridCellBoolEditor::StartingKey(wxKeyEvent& event) } } - -// return the value as "1" for true and the empty string for false wxString wxGridCellBoolEditor::GetValue() const { - bool bSet = CBox()->GetValue(); - return bSet ? _T("1") : wxEmptyString; + return ms_stringValues[CBox()->GetValue()]; +} + +/* static */ void +wxGridCellBoolEditor::UseStringValues(const wxString& valueTrue, + const wxString& valueFalse) +{ + ms_stringValues[false] = valueFalse; + ms_stringValues[true] = valueTrue; +} + +/* static */ bool +wxGridCellBoolEditor::IsTrueValue(const wxString& value) +{ + return value == ms_stringValues[true]; } #endif // wxUSE_CHECKBOX @@ -1462,10 +1570,16 @@ void wxGridCellChoiceEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler) { + int style = wxTE_PROCESS_ENTER | + wxTE_PROCESS_TAB | + wxBORDER_NONE; + + if ( !m_allowOthers ) + style |= wxCB_READONLY; m_control = new wxComboBox(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, m_choices, - m_allowOthers ? 0 : wxCB_READONLY); + style); wxGridCellEditor::Create(parent, id, evtHandler); } @@ -1497,20 +1611,8 @@ void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) m_startValue = grid->GetTable()->GetValue(row, col); - if (m_allowOthers) - { - Combo()->SetValue(m_startValue); - } - else - { - // find the right position, or default to the first if not found - int pos = Combo()->FindString(m_startValue); - if (pos == wxNOT_FOUND) - pos = 0; - Combo()->SetSelection(pos); - } + Reset(); // this updates combo box to correspond to m_startValue - Combo()->SetInsertionPointEnd(); Combo()->SetFocus(); if (evtHandler) @@ -1537,8 +1639,19 @@ bool wxGridCellChoiceEditor::EndEdit(int row, int col, void wxGridCellChoiceEditor::Reset() { - Combo()->SetValue(m_startValue); - Combo()->SetInsertionPointEnd(); + if (m_allowOthers) + { + Combo()->SetValue(m_startValue); + Combo()->SetInsertionPointEnd(); + } + else // the combobox is read-only + { + // find the right position, or default to the first if not found + int pos = Combo()->FindString(m_startValue); + if (pos == wxNOT_FOUND) + pos = 0; + Combo()->SetSelection(pos); + } } void wxGridCellChoiceEditor::SetParameters(const wxString& params) @@ -1744,23 +1857,28 @@ void wxGridCellRenderer::Draw(wxGrid& grid, int WXUNUSED(row), int WXUNUSED(col), bool isSelected) { - dc.SetBackgroundMode( wxSOLID ); + dc.SetBackgroundMode( wxBRUSHSTYLE_SOLID ); // grey out fields if the grid is disabled if ( grid.IsEnabled() ) { if ( isSelected ) { - dc.SetBrush( wxBrush(grid.GetSelectionBackground(), wxSOLID) ); + wxColour clr; + if ( grid.HasFocus() ) + clr = grid.GetSelectionBackground(); + else + clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); + dc.SetBrush( wxBrush(clr, wxBRUSHSTYLE_SOLID) ); } else { - dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxSOLID) ); + dc.SetBrush( wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID) ); } } else { - dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxSOLID)); + dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE), wxBRUSHSTYLE_SOLID)); } dc.SetPen( *wxTRANSPARENT_PEN ); @@ -1776,7 +1894,7 @@ void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, wxDC& dc, bool isSelected) { - dc.SetBackgroundMode( wxTRANSPARENT ); + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); // TODO some special colours for attr.IsReadOnly() case? @@ -1785,7 +1903,12 @@ void wxGridCellStringRenderer::SetTextColoursAndFont(const wxGrid& grid, { if ( isSelected ) { - dc.SetTextBackground( grid.GetSelectionBackground() ); + wxColour clr; + if ( grid.HasFocus() ) + clr = grid.GetSelectionBackground(); + else + clr = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); + dc.SetTextBackground( clr ); dc.SetTextForeground( grid.GetSelectionForeground() ); } else @@ -2146,8 +2269,7 @@ wxSize wxGridCellBoolRenderer::GetBestSize(wxGrid& grid, wxSize size = checkbox->GetBestSize(); wxCoord checkSize = size.y + 2 * wxGRID_CHECKMARK_MARGIN; - // FIXME wxGTK::wxCheckBox::GetBestSize() gives "wrong" result -#if defined(__WXGTK__) || defined(__WXMOTIF__) +#if defined(__WXMOTIF__) checkSize -= size.y / 2; #endif @@ -2176,7 +2298,7 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, if ( size.x >= minSize || size.y >= minSize ) { // and even leave (at least) 1 pixel margin - size.x = size.y = minSize - 2; + size.x = size.y = minSize; } // draw a border around checkmark @@ -2214,29 +2336,14 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, else { wxString cellval( grid.GetTable()->GetValue(row, col) ); - value = !( !cellval || (cellval == wxT("0")) ); + value = wxGridCellBoolEditor::IsTrueValue(cellval); } - if ( value ) - { - wxRect rectMark = rectBorder; + int flags = 0; + if (value) + flags |= wxCONTROL_CHECKED; -#ifdef __WXMSW__ - // MSW DrawCheckMark() is weird (and should probably be changed...) - rectMark.Inflate(-wxGRID_CHECKMARK_MARGIN / 2); - rectMark.x++; - rectMark.y++; -#else - rectMark.Inflate(-wxGRID_CHECKMARK_MARGIN); -#endif - - dc.SetTextForeground(attr.GetTextColour()); - dc.DrawCheckMark(rectMark); - } - - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.SetPen(wxPen(attr.GetTextColour(), 1, wxSOLID)); - dc.DrawRectangle(rectBorder); + wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); } // ---------------------------------------------------------------------------- @@ -2289,6 +2396,7 @@ wxGridCellAttr *wxGridCellAttr::Clone() const if ( IsReadOnly() ) attr->SetReadOnly(); + attr->SetOverflow( m_overflow == Overflow ); attr->SetKind( m_attrkind ); return attr; @@ -2440,7 +2548,7 @@ void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const // 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(wxGrid* grid, int row, int col) const +wxGridCellRenderer* wxGridCellAttr::GetRenderer(const wxGrid* grid, int row, int col) const { wxGridCellRenderer *renderer = NULL; @@ -2484,7 +2592,7 @@ wxGridCellRenderer* wxGridCellAttr::GetRenderer(wxGrid* grid, int row, int col) } // same as above, except for s/renderer/editor/g -wxGridCellEditor* wxGridCellAttr::GetEditor(wxGrid* grid, int row, int col) const +wxGridCellEditor* wxGridCellAttr::GetEditor(const wxGrid* grid, int row, int col) const { wxGridCellEditor *editor = NULL; @@ -2533,21 +2641,25 @@ wxGridCellEditor* wxGridCellAttr::GetEditor(wxGrid* grid, int row, int col) cons void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col) { + // Note: contrary to wxGridRowOrColAttrData::SetAttr, we must not + // touch attribute's reference counting explicitly, since this + // is managed by class wxGridCellWithAttr int n = FindIndex(row, col); if ( n == wxNOT_FOUND ) { - // add the attribute - m_attrs.Add(new wxGridCellWithAttr(row, col, attr)); + if ( attr ) + { + // add the attribute + m_attrs.Add(new wxGridCellWithAttr(row, col, attr)); + } + //else: nothing to do } - else + else // we already have an attribute for this cell { - // free the old attribute - m_attrs[(size_t)n].attr->DecRef(); - if ( attr ) { // change the attribute - m_attrs[(size_t)n].attr = attr; + m_attrs[(size_t)n].ChangeAttr(attr); } else { @@ -2596,8 +2708,6 @@ void wxGridCellAttrData::UpdateAttrRows( size_t pos, int numRows ) else { // ...or remove the attribute - // No need to DecRef the attribute itself since this is - // done be wxGridCellWithAttr's destructor! m_attrs.RemoveAt(n); n--; count--; @@ -2632,8 +2742,6 @@ void wxGridCellAttrData::UpdateAttrCols( size_t pos, int numCols ) else { // ...or remove the attribute - // No need to DecRef the attribute itself since this is - // done be wxGridCellWithAttr's destructor! m_attrs.RemoveAt(n); n--; count--; @@ -2664,7 +2772,7 @@ int wxGridCellAttrData::FindIndex(int row, int col) const wxGridRowOrColAttrData::~wxGridRowOrColAttrData() { - size_t count = m_attrs.Count(); + size_t count = m_attrs.GetCount(); for ( size_t n = 0; n < count; n++ ) { m_attrs[n]->DecRef(); @@ -2690,22 +2798,31 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) int i = m_rowsOrCols.Index(rowOrCol); if ( i == wxNOT_FOUND ) { - // add the attribute - m_rowsOrCols.Add(rowOrCol); - m_attrs.Add(attr); + if ( attr ) + { + // add the attribute - no need to do anything to reference count + // since we take ownership of the attribute. + m_rowsOrCols.Add(rowOrCol); + m_attrs.Add(attr); + } + // nothing to remove } else { size_t n = (size_t)i; + if ( m_attrs[n] == attr ) + // nothing to do + return; if ( attr ) { - // change the attribute + // change the attribute, handling reference count manually, + // taking ownership of the new attribute. m_attrs[n]->DecRef(); m_attrs[n] = attr; } else { - // remove this attribute + // remove this attribute, handling reference count manually m_attrs[n]->DecRef(); m_rowsOrCols.RemoveAt(n); m_attrs.RemoveAt(n); @@ -2905,7 +3022,7 @@ void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols ) wxGridTypeRegistry::~wxGridTypeRegistry() { - size_t count = m_typeinfo.Count(); + size_t count = m_typeinfo.GetCount(); for ( size_t i = 0; i < count; i++ ) delete m_typeinfo[i]; } @@ -3106,7 +3223,8 @@ void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col) { if ( m_attrProvider ) { - attr->SetKind(wxGridCellAttr::Cell); + if ( attr ) + attr->SetKind(wxGridCellAttr::Cell); m_attrProvider->SetAttr(attr, row, col); } else @@ -3613,7 +3731,12 @@ bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols ) if ( !m_colLabels.IsEmpty() ) { - m_colLabels.RemoveAt( colID, numCols ); + // m_colLabels stores just as many elements as it needs, e.g. if only + // the label of the first column had been set it would have only one + // element and not numCols, so account for it + int nToRm = m_colLabels.size() - colID; + if ( nToRm > 0 ) + m_colLabels.RemoveAt( colID, nToRm ); } for ( row = 0; row < curNumRows; row++ ) @@ -3705,21 +3828,27 @@ void wxGridStringTable::SetColLabelValue( int col, const wxString& value ) ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// +BEGIN_EVENT_TABLE(wxGridSubwindow, wxWindow) + EVT_MOUSE_CAPTURE_LOST(wxGridSubwindow::OnMouseCaptureLost) +END_EVENT_TABLE() + +void wxGridSubwindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event)) +{ + m_owner->CancelMouseCapture(); +} + IMPLEMENT_DYNAMIC_CLASS( wxGridRowLabelWindow, wxWindow ) -BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxWindow ) +BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridRowLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent ) - EVT_KEY_DOWN( wxGridRowLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridRowLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridRowLabelWindow::OnChar ) END_EVENT_TABLE() wxGridRowLabelWindow::wxGridRowLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ) - : wxWindow( parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE | wxFULL_REPAINT_ON_RESIZE ) + : wxGridSubwindow(parent, id, pos, size) { m_owner = parent; } @@ -3753,44 +3882,20 @@ void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event ) m_owner->GetEventHandler()->ProcessEvent( event ); } -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridRowLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridRowLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridRowLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - ////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNAMIC_CLASS( wxGridColLabelWindow, wxWindow ) -BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxWindow ) +BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxGridSubwindow ) EVT_PAINT( wxGridColLabelWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent ) - EVT_KEY_DOWN( wxGridColLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridColLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridColLabelWindow::OnChar ) END_EVENT_TABLE() wxGridColLabelWindow::wxGridColLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ) - : wxWindow( parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE | wxFULL_REPAINT_ON_RESIZE ) + : wxGridSubwindow(parent, id, pos, size) { m_owner = parent; } @@ -3827,44 +3932,20 @@ void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event ) m_owner->GetEventHandler()->ProcessEvent( event ); } -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridColLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridColLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridColLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - ////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNAMIC_CLASS( wxGridCornerLabelWindow, wxWindow ) -BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxWindow ) +BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow ) EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent ) EVT_PAINT( wxGridCornerLabelWindow::OnPaint ) - EVT_KEY_DOWN( wxGridCornerLabelWindow::OnKeyDown ) - EVT_KEY_UP( wxGridCornerLabelWindow::OnKeyUp ) - EVT_CHAR( wxGridCornerLabelWindow::OnChar ) END_EVENT_TABLE() wxGridCornerLabelWindow::wxGridCornerLabelWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ) - : wxWindow( parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE | wxFULL_REPAINT_ON_RESIZE ) + : wxGridSubwindow(parent, id, pos, size) { m_owner = parent; } @@ -3878,7 +3959,8 @@ void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) GetClientSize( &client_width, &client_height ); // VZ: any reason for this ifdef? (FIXME) -#ifdef __WXGTK__ +#if 0 +def __WXGTK__ wxRect rect; rect.SetX( 1 ); rect.SetY( 1 ); @@ -3887,7 +3969,7 @@ void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) wxRendererNative::Get().DrawHeaderButton( this, dc, rect, 0 ); #else // !__WXGTK__ - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); + dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); dc.DrawLine( client_width - 1, client_height - 1, client_width - 1, 0 ); dc.DrawLine( client_width - 1, client_height - 1, 0, client_height - 1 ); dc.DrawLine( 0, 0, client_width, 0 ); @@ -3909,32 +3991,11 @@ void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event ) m_owner->GetEventHandler()->ProcessEvent(event); } -// This seems to be required for wxMotif otherwise the mouse -// cursor must be in the cell edit control to get key events -// -void wxGridCornerLabelWindow::OnKeyDown( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridCornerLabelWindow::OnKeyUp( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - -void wxGridCornerLabelWindow::OnChar( wxKeyEvent& event ) -{ - if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) - event.Skip(); -} - ////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNAMIC_CLASS( wxGridWindow, wxWindow ) -BEGIN_EVENT_TABLE( wxGridWindow, wxWindow ) +BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow ) EVT_PAINT( wxGridWindow::OnPaint ) EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel ) EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent ) @@ -3952,10 +4013,9 @@ wxGridWindow::wxGridWindow( wxGrid *parent, wxWindowID id, const wxPoint &pos, const wxSize &size ) - : wxWindow( - parent, id, pos, size, - wxWANTS_CHARS | wxBORDER_NONE | wxCLIP_CHILDREN | wxFULL_REPAINT_ON_RESIZE, - wxT("grid window") ) + : wxGridSubwindow(parent, id, pos, size, + wxWANTS_CHARS | wxCLIP_CHILDREN, + wxT("grid window") ) { m_owner = parent; m_rowLabelWin = rowLblWin; @@ -4025,6 +4085,29 @@ void wxGridWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) ) void wxGridWindow::OnFocus(wxFocusEvent& event) { + // and if we have any selection, it has to be repainted, because it + // uses different colour when the grid is not focused: + if ( m_owner->IsSelection() ) + { + Refresh(); + } + else + { + // NB: Note that this code is in "else" branch only because the other + // branch refreshes everything and so there's no point in calling + // Refresh() again, *not* because it should only be done if + // !IsSelection(). If the above code is ever optimized to refresh + // only selected area, this needs to be moved out of the "else" + // branch so that it's always executed. + + // current cell cursor {dis,re}appears on focus change: + const wxGridCellCoords cursorCoords(m_owner->GetGridCursorRow(), + m_owner->GetGridCursorCol()); + const wxRect cursor = + m_owner->BlockToDeviceRect(cursorCoords, cursorCoords); + Refresh(true, &cursor); + } + if ( !m_owner->GetEventHandler()->ProcessEvent( event ) ) event.Skip(); } @@ -4112,12 +4195,7 @@ END_EVENT_TABLE() wxGrid::wxGrid() { - // in order to make sure that a size event is not - // trigerred in a unfinished state - m_cornerLabelWin = NULL; - m_rowLabelWin = NULL; - m_colLabelWin = NULL; - m_gridWin = NULL; + InitVars(); } wxGrid::wxGrid( wxWindow *parent, @@ -4126,12 +4204,9 @@ wxGrid::wxGrid( wxWindow *parent, const wxSize& size, long style, const wxString& name ) - : wxScrolledWindow( parent, id, pos, size, (style | wxWANTS_CHARS), name ), - m_colMinWidths(GRID_HASH_SIZE), - m_rowMinHeights(GRID_HASH_SIZE) { - Create(); - SetBestFittingSize(size); + InitVars(); + Create(parent, id, pos, size, style, name); } bool wxGrid::Create(wxWindow *parent, wxWindowID id, @@ -4146,7 +4221,8 @@ bool wxGrid::Create(wxWindow *parent, wxWindowID id, m_rowMinHeights = wxLongToLongHashMap(GRID_HASH_SIZE); Create(); - SetBestFittingSize(size); + SetInitialSize(size); + CalcDimensions(); return true; } @@ -4166,8 +4242,12 @@ wxGrid::~wxGrid() total ? (gs_nAttrCacheHits*100) / total : 0); #endif - if (m_ownTable) + // if we own the table, just delete it, otherwise at least don't leave it + // with dangling view pointer + if ( m_ownTable ) delete m_table; + else if ( m_table && m_table->GetView() == this ) + m_table->SetView(NULL); delete m_typeRegistry; delete m_selection; @@ -4183,15 +4263,8 @@ wxGrid::~wxGrid() void wxGrid::Create() { - // set to true by CreateGrid - m_created = false; - // create the type registry m_typeRegistry = new wxGridTypeRegistry; - m_selection = NULL; - - m_table = (wxGridTableBase *) NULL; - m_ownTable = false; m_cellEditCtrlEnabled = false; @@ -4317,24 +4390,33 @@ wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, wxGrid::wxGridSelectionModes selmode ) { + bool checkSelection = false; if ( m_created ) { // stop all processing m_created = false; - if (m_ownTable) + if (m_table) { - wxGridTableBase *t = m_table; + m_table->SetView(0); + if( m_ownTable ) + delete m_table; m_table = NULL; - delete t; } delete m_selection; - - m_table = NULL; m_selection = NULL; + + m_ownTable = false; m_numRows = 0; m_numCols = 0; + checkSelection = true; + + // kill row and column size arrays + m_colWidths.Empty(); + m_colRights.Empty(); + m_rowHeights.Empty(); + m_rowBottoms.Empty(); } if (table) @@ -4346,7 +4428,28 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, m_table->SetView( this ); m_ownTable = takeOwnership; m_selection = new wxGridSelection( this, selmode ); - + if (checkSelection) + { + // If the newly set table is smaller than the + // original one current cell and selection regions + // might be invalid, + m_selectingKeyboard = wxGridNoCellCoords; + m_currentCellCoords = + wxGridCellCoords(wxMin(m_numRows, m_currentCellCoords.GetRow()), + wxMin(m_numCols, m_currentCellCoords.GetCol())); + if (m_selectingTopLeft.GetRow() >= m_numRows || + m_selectingTopLeft.GetCol() >= m_numCols) + { + m_selectingTopLeft = wxGridNoCellCoords; + m_selectingBottomRight = wxGridNoCellCoords; + } + else + m_selectingBottomRight = + wxGridCellCoords(wxMin(m_numRows, + m_selectingBottomRight.GetRow()), + wxMin(m_numCols, + m_selectingBottomRight.GetCol())); + } CalcDimensions(); m_created = true; @@ -4355,6 +4458,24 @@ bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership, return m_created; } +void wxGrid::InitVars() +{ + m_created = false; + + m_cornerLabelWin = NULL; + m_rowLabelWin = NULL; + m_colLabelWin = NULL; + m_gridWin = NULL; + + m_table = NULL; + m_ownTable = false; + + m_selection = NULL; + m_defaultCellAttr = NULL; + m_typeRegistry = NULL; + m_winCapture = NULL; +} + void wxGrid::Init() { m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH; @@ -4418,6 +4539,7 @@ void wxGrid::Init() m_dragRowOrCol = -1; m_isDragging = false; m_startDragPos = wxDefaultPosition; + m_nativeColumnLabels = false; m_waitForSlowClick = false; @@ -4449,8 +4571,9 @@ void wxGrid::Init() // 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... +// with some extra code, it should be possible to only store the widths/heights +// different from default ones (resulting in space savings for huge grids) but +// this is not done currently // ---------------------------------------------------------------------------- void wxGrid::InitRowHeights() @@ -4461,10 +4584,9 @@ void wxGrid::InitRowHeights() m_rowHeights.Alloc( m_numRows ); m_rowBottoms.Alloc( m_numRows ); - int rowBottom = 0; - m_rowHeights.Add( m_defaultRowHeight, m_numRows ); + int rowBottom = 0; for ( int i = 0; i < m_numRows; i++ ) { rowBottom += m_defaultRowHeight; @@ -4479,10 +4601,10 @@ void wxGrid::InitColWidths() m_colWidths.Alloc( m_numCols ); m_colRights.Alloc( m_numCols ); - int colRight = 0; m_colWidths.Add( m_defaultColWidth, m_numCols ); + int colRight = 0; for ( int i = 0; i < m_numCols; i++ ) { colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth; @@ -4526,17 +4648,12 @@ int wxGrid::GetRowBottom(int row) const void wxGrid::CalcDimensions() { - int cw, ch; - GetClientSize( &cw, &ch ); - - if ( m_rowLabelWin->IsShown() ) - cw -= m_rowLabelWidth; - if ( m_colLabelWin->IsShown() ) - ch -= m_colLabelHeight; + // compute the size of the scrollable area + int w = m_numCols > 0 ? GetColRight(GetColAt(m_numCols - 1)) : 0; + int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0; - // grid total size - int w = m_numCols > 0 ? GetColRight(GetColAt( m_numCols - 1 )) + m_extraWidth + 1 : 0; - int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) + m_extraHeight + 1 : 0; + w += m_extraWidth; + h += m_extraHeight; // take into account editor if shown if ( IsCellEditControlShown() ) @@ -4573,7 +4690,8 @@ void wxGrid::CalcDimensions() // do set scrollbar parameters SetScrollbars( m_scrollLineX, m_scrollLineY, - GetScrollX(w), GetScrollY(h), x, y, + GetScrollX(w), GetScrollY(h), + x, y, GetBatchCount() != 0); // if our OnSize() hadn't been called (it would if we have scrollbars), we @@ -4591,17 +4709,53 @@ void wxGrid::CalcWindowSizes() int cw, ch; GetClientSize( &cw, &ch ); + // this block of code tries to work around the following problem: the grid + // could have been just resized to have enough space to show the full grid + // window contents without the scrollbars, but its client size could be + // not big enough because the grid has the scrollbars right now and so the + // scrollbars would remain even though we don't need them any more + // + // to prevent this from happening, check if we have enough space for + // everything without the scrollbars and explicitly disable them then + wxSize size = GetSize() - GetWindowBorderSize(); + if ( size != wxSize(cw, ch) ) + { + // check if we have enough space for grid window after accounting for + // the fixed size elements + size.x -= m_rowLabelWidth; + size.y -= m_colLabelHeight; + + const wxSize vsize = m_gridWin->GetVirtualSize(); + + if ( size.x >= vsize.x && size.y >= vsize.y ) + { + // yes, we do, so remove the scrollbars and use the new client size + // (which should be the same as full window size - borders now) + SetScrollbars(0, 0, 0, 0); + GetClientSize(&cw, &ch); + } + } + + // the grid may be too small to have enough space for the labels yet, don't + // size the windows to negative sizes in this case + int gw = cw - m_rowLabelWidth; + int gh = ch - m_colLabelHeight; + if (gw < 0) + gw = 0; + if (gh < 0) + gh = 0; + if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() ) m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight ); if ( m_colLabelWin && m_colLabelWin->IsShown() ) - m_colLabelWin->SetSize( m_rowLabelWidth, 0, cw - m_rowLabelWidth, m_colLabelHeight ); + m_colLabelWin->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight ); if ( m_rowLabelWin && m_rowLabelWin->IsShown() ) - m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, ch - m_colLabelHeight ); + m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh ); if ( m_gridWin && m_gridWin->IsShown() ) - m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, cw - m_rowLabelWidth, ch - m_colLabelHeight ); + m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, gw, gh ); } // this is called when the grid table sends a message @@ -4982,7 +5136,7 @@ bool wxGrid::Redimension( wxGridTableMessage& msg ) return result; } -wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) +wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const { wxRegionIterator iter( reg ); wxRect r; @@ -5033,7 +5187,7 @@ wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) return rowlabels; } -wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) +wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const { wxRegionIterator iter( reg ); wxRect r; @@ -5087,7 +5241,7 @@ wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) return colLabels; } -wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) +wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const { wxRegionIterator iter( reg ); wxRect r; @@ -5709,6 +5863,21 @@ void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event ) } } +void wxGrid::CancelMouseCapture() +{ + // cancel operation currently in progress, whatever it is + if ( m_winCapture ) + { + m_isDragging = false; + m_cursorMode = WXGRID_CURSOR_SELECT_CELL; + m_winCapture->SetCursor( *wxSTANDARD_CURSOR ); + m_winCapture = NULL; + + // remove traces of whatever we drew on screen + Refresh(); + } +} + void wxGrid::ChangeCursorMode(CursorMode mode, wxWindow *win, bool captureMouse) @@ -5828,13 +5997,6 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) SaveEditControlValue(); } - // Have we captured the mouse yet? - if (! m_winCapture) - { - m_winCapture = m_gridWin; - m_winCapture->CaptureMouse(); - } - if ( coords != wxGridNoCellCoords ) { if ( event.CmdDown() ) @@ -5854,6 +6016,7 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) coords.GetRow(), coords.GetCol(), event ); + return; } } else @@ -5875,8 +6038,17 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) // scrolling is way to fast, at least on MSW - also on GTK. } } + // Have we captured the mouse yet? + if (! m_winCapture) + { + m_winCapture = m_gridWin; + m_winCapture->CaptureMouse(); + } + + } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) + else if ( event.LeftIsDown() && + m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ) { int cw, ch, left, dummy; m_gridWin->GetClientSize( &cw, &ch ); @@ -5894,7 +6066,8 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) dc.DrawLine( left, y, left+cw, y ); m_dragLastPos = y; } - else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) + else if ( event.LeftIsDown() && + m_cursorMode == WXGRID_CURSOR_RESIZE_COL ) { int cw, ch, dummy, top; m_gridWin->GetClientSize( &cw, &ch ); @@ -6145,7 +6318,7 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { if ( CanDragRowSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false); } } else if ( dragCol >= 0 ) @@ -6155,7 +6328,7 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL ) { if ( CanDragColSize() && CanDragGridSize() ) - ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL); + ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false); } } else // Neither on a row or col edge @@ -6637,7 +6810,7 @@ int wxGrid::SendEvent( const wxEventType type, pos.y += GetColLabelSize(); if ( mouseEv.GetEventObject() == GetGridColLabelWindow() ) pos.x += GetRowLabelSize(); - + wxGridEvent gridEvt( GetId(), type, this, @@ -6718,7 +6891,7 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) { // Don't do anything if between Begin/EndBatch... // EndBatch() will do all this on the last nested one anyway. - if (! GetBatchCount()) + if ( m_created && !GetBatchCount() ) { // Refresh to get correct scrolled position: wxScrolledWindow::Refresh(eraseb, rect); @@ -6803,14 +6976,13 @@ void wxGrid::Refresh(bool eraseb, const wxRect* rect) } } -void wxGrid::OnSize( wxSizeEvent& event ) +void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event)) { - // position the child windows - CalcWindowSizes(); - - // don't call CalcDimensions() from here, the base class handles the size - // changes itself - event.Skip(); + if (m_targetWindow != this) // check whether initialisation has been done + { + // update our children window positions and scrollbars + CalcDimensions(); + } } void wxGrid::OnKeyDown( wxKeyEvent& event ) @@ -6838,7 +7010,7 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) else if (event.GetKeyCode() == WXK_LEFT) event.m_keyCode = WXK_RIGHT; } - + // try local handlers switch ( event.GetKeyCode() ) { @@ -7066,8 +7238,10 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) return; } +#if !defined(__WXMAC__) wxClientDC dc( m_gridWin ); PrepareDC( dc ); +#endif if ( m_currentCellCoords != wxGridNoCellCoords ) { @@ -7090,15 +7264,21 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) // Otherwise refresh redraws the highlight! m_currentCellCoords = coords; +#if defined(__WXMAC__) + m_gridWin->Refresh(true /*, & r */); +#else DrawGridCellArea( dc, cells ); DrawAllGridLines( dc, r ); +#endif } } m_currentCellCoords = coords; wxGridCellAttr *attr = GetCellAttr( coords ); +#if !defined(__WXMAC__) DrawCellHighlight( dc, attr ); +#endif attr->DecRef(); } @@ -7431,7 +7611,7 @@ void wxGrid::DrawGridSpace( wxDC& dc ) int left, top; CalcUnscrolledPosition( 0, 0, &left, &top ); - dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxSOLID) ); + dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxBRUSHSTYLE_SOLID) ); dc.SetPen( *wxTRANSPARENT_PEN ); if ( right > rightCol ) @@ -7474,7 +7654,7 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) // edit control is erased by this code after being rendered. // On wxMac (QD build only), the cell editor is a wxTextCntl and is rendered // implicitly, causing this out-of order render. -#if !defined(__WXMAC__) || wxMAC_USE_CORE_GRAPHICS +#if !defined(__WXMAC__) wxGridCellEditor *editor = attr->GetEditor(this, row, col); editor->PaintBackground(rect, attr); editor->DecRef(); @@ -7493,6 +7673,10 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords ) void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) { + // don't show highlight when the grid doesn't have focus + if ( !HasFocus() ) + return; + int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); @@ -7522,7 +7706,7 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) // Now draw the rectangle // use the cellHighlightColour if the cell is inside a selection, this // will ensure the cell is always visible. - dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground : m_cellHighlightColour, penWidth, wxSOLID)); + dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground : m_cellHighlightColour, penWidth, wxPENSTYLE_SOLID)); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(rect); } @@ -7551,7 +7735,7 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr ) wxPen wxGrid::GetDefaultGridLinePen() { - return wxPen(GetGridLineColour(), 1, wxSOLID); + return wxPen(GetGridLineColour(), 1, wxPENSTYLE_SOLID); } wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row)) @@ -7669,17 +7853,15 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) int rightCol = GetColPos( internalXToCol(right) ); int bottomRow = internalYToRow(bottom); -#ifndef __WXMAC__ - // CS: I don't know why suddenly unscrolled coordinates are used for clipping wxRegion clippedcells(0, 0, cw, ch); int i, j, cell_rows, cell_cols; wxRect rect; - for (j=topRow; j 1) || (cell_cols > 1)) - { - rect = CellToRect(j, i); - clippedcells.Subtract(rect); - } - else if ((cell_rows < 0) || (cell_cols < 0)) - { - rect = CellToRect(j + cell_rows, i + cell_cols); - clippedcells.Subtract(rect); - } - } - } -#endif - - dc.SetClippingRegion( clippedcells ); + dc.SetDeviceClippingRegion( clippedcells ); // horizontal grid lines @@ -7794,22 +7952,10 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) wxRect rect; -#ifdef __WXGTK20__ - rect.SetX( 1 ); - rect.SetY( GetRowTop(row) + 1 ); - rect.SetWidth( m_rowLabelWidth - 2 ); - rect.SetHeight( GetRowHeight(row) - 2 ); - - CalcScrolledPosition( 0, rect.y, NULL, &rect.y ); - - wxWindowDC *win_dc = (wxWindowDC*) &dc; - - wxRendererNative::Get().DrawHeaderButton( win_dc->m_owner, dc, rect, 0 ); -#else int rowTop = GetRowTop(row), rowBottom = GetRowBottom(row) - 1; - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); + dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); dc.DrawLine( m_rowLabelWidth - 1, rowTop, m_rowLabelWidth - 1, rowBottom ); dc.DrawLine( 0, rowTop, 0, rowBottom ); dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom ); @@ -7817,9 +7963,8 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) dc.SetPen( *wxWHITE_PEN ); dc.DrawLine( 1, rowTop, 1, rowBottom ); dc.DrawLine( 1, rowTop, m_rowLabelWidth - 1, rowTop ); -#endif - dc.SetBackgroundMode( wxTRANSPARENT ); + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); dc.SetTextForeground( GetLabelTextColour() ); dc.SetFont( GetLabelFont() ); @@ -7833,6 +7978,18 @@ void wxGrid::DrawRowLabel( wxDC& dc, int row ) DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign ); } +void wxGrid::SetUseNativeColLabels( bool native ) +{ + m_nativeColumnLabels = native; + if (native) + { + int height = wxRendererNative::Get().GetHeaderButtonHeight( this ); + SetColLabelSize( height ); + } + + m_colLabelWin->Refresh(); +} + void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols ) { if ( !m_numCols ) @@ -7856,30 +8013,32 @@ void wxGrid::DrawColLabel( wxDC& dc, int col ) wxRect rect; -#ifdef __WXGTK20__ - rect.SetX( colLeft + 1 ); - rect.SetY( 1 ); - rect.SetWidth( GetColWidth(col) - 2 ); - rect.SetHeight( m_colLabelHeight - 2 ); - - wxWindowDC *win_dc = (wxWindowDC*) &dc; + if (m_nativeColumnLabels) + { + rect.SetX( colLeft); + rect.SetY( 0 ); + rect.SetWidth( GetColWidth(col)); + rect.SetHeight( m_colLabelHeight ); - wxRendererNative::Get().DrawHeaderButton( win_dc->m_owner, dc, rect, 0 ); -#else - int colRight = GetColRight(col) - 1; + wxWindowDC *win_dc = (wxWindowDC*) &dc; + wxRendererNative::Get().DrawHeaderButton( win_dc->GetWindow(), dc, rect, 0 ); + } + else + { + int colRight = GetColRight(col) - 1; - dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID) ); - dc.DrawLine( colRight, 0, colRight, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 0, colRight, 0 ); - dc.DrawLine( colLeft, m_colLabelHeight - 1, + dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxPENSTYLE_SOLID) ); + dc.DrawLine( colRight, 0, colRight, m_colLabelHeight - 1 ); + dc.DrawLine( colLeft, 0, colRight, 0 ); + dc.DrawLine( colLeft, m_colLabelHeight - 1, colRight + 1, m_colLabelHeight - 1 ); - dc.SetPen( *wxWHITE_PEN ); - dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 ); - dc.DrawLine( colLeft, 1, colRight, 1 ); -#endif + dc.SetPen( *wxWHITE_PEN ); + dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight - 1 ); + dc.DrawLine( colLeft, 1, colRight, 1 ); + } - dc.SetBackgroundMode( wxTRANSPARENT ); + dc.SetBackgroundMode( wxBRUSHSTYLE_TRANSPARENT ); dc.SetTextForeground( GetLabelTextColour() ); dc.SetFont( GetLabelFont() ); @@ -7975,7 +8134,7 @@ void wxGrid::DrawTextRectangle(wxDC& dc, continue; } - long lineWidth = 0, + wxCoord lineWidth = 0, lineHeight = 0; dc.GetTextExtent(line, &lineWidth, &lineHeight); @@ -8020,7 +8179,8 @@ void wxGrid::DrawTextRectangle(wxDC& dc, // Split multi-line text up into an array of strings. // Any existing contents of the string array are preserved. // -void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) +// TODO: refactor wxTextFile::Read() and reuse the same code from here +void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const { int startPos = 0; int pos; @@ -8040,25 +8200,25 @@ void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) } else { - lines.Add( value.Mid(startPos, pos) ); + lines.Add( tVal.Mid(startPos, pos) ); } startPos += pos + 1; } - if ( startPos < (int)value.length() ) + if ( startPos < (int)tVal.length() ) { - lines.Add( value.Mid( startPos ) ); + lines.Add( tVal.Mid( startPos ) ); } } void wxGrid::GetTextBoxSize( const wxDC& dc, const wxArrayString& lines, - long *width, long *height ) + long *width, long *height ) const { - long w = 0; - long h = 0; - long lineW = 0, lineH = 0; + wxCoord w = 0; + wxCoord h = 0; + wxCoord lineW = 0, lineH = 0; size_t i; for ( i = 0; i < lines.GetCount(); i++ ) @@ -8241,7 +8401,8 @@ void wxGrid::ShowCellEditControl() // might not cover the entire cell wxClientDC dc( m_gridWin ); PrepareDC( dc ); - dc.SetBrush(wxBrush(GetCellAttr(row, col)->GetBackgroundColour(), wxSOLID)); + wxGridCellAttr* attr = GetCellAttr(row, col); + dc.SetBrush(wxBrush(attr->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); dc.SetPen(*wxTRANSPARENT_PEN); dc.DrawRectangle(rect); @@ -8261,7 +8422,6 @@ void wxGrid::ShowCellEditControl() if (rect.y > 0) rect.y--; - wxGridCellAttr* attr = GetCellAttr(row, col); wxGridCellEditor* editor = attr->GetEditor(this, row, col); if ( !editor->IsCreated() ) { @@ -8401,7 +8561,7 @@ void wxGrid::SaveEditControlValue() // coordinates for mouse events etc. // -void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) +void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) const { int row = YToRow(y); int col = XToCol(x); @@ -8483,19 +8643,18 @@ static int CoordToRowOrCol(int coord, int defaultDist, int minDist, return i_max; } -int wxGrid::YToRow( int y ) +int wxGrid::YToRow( int y ) const { return CoordToRowOrCol(y, m_defaultRowHeight, m_minAcceptableRowHeight, m_rowBottoms, m_numRows, false); } -int wxGrid::XToCol( int x, bool clipToMinMax ) +int wxGrid::XToCol( int x, bool clipToMinMax ) const { if (x < 0) return clipToMinMax && (m_numCols > 0) ? GetColAt( 0 ) : -1; - if (!m_defaultColWidth) - m_defaultColWidth = 1; + wxASSERT_MSG(m_defaultColWidth > 0, wxT("Default column width can not be zero")); int maxPos = x / m_defaultColWidth; int minPos = 0; @@ -8561,7 +8720,7 @@ int wxGrid::XToCol( int x, bool clipToMinMax ) // (b) resizing rows/columns (the thing for which edge detection is // relevant at all) is enabled. // -int wxGrid::YToEdgeOfRow( int y ) +int wxGrid::YToEdgeOfRow( int y ) const { int i; i = internalYToRow(y); @@ -8583,7 +8742,7 @@ int wxGrid::YToEdgeOfRow( int y ) // -1 if not near an edge // See comment at YToEdgeOfRow for conditions on edge detection. // -int wxGrid::XToEdgeOfCol( int x ) +int wxGrid::XToEdgeOfCol( int x ) const { int i; i = internalXToCol(x); @@ -8601,7 +8760,7 @@ int wxGrid::XToEdgeOfCol( int x ) return -1; } -wxRect wxGrid::CellToRect( int row, int col ) +wxRect wxGrid::CellToRect( int row, int col ) const { wxRect rect( -1, -1, -1, -1 ); @@ -8636,7 +8795,7 @@ wxRect wxGrid::CellToRect( int row, int col ) return rect; } -bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) +bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const { // get the cell rectangle in logical coords // @@ -9214,7 +9373,7 @@ bool wxGrid::MoveCursorRightBlock( bool expandSelection ) // ------ Label values and formatting // -void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) +void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) const { if ( horiz ) *horiz = m_rowLabelHorizAlign; @@ -9222,7 +9381,7 @@ void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) *vert = m_rowLabelVertAlign; } -void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) +void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) const { if ( horiz ) *horiz = m_colLabelHorizAlign; @@ -9230,12 +9389,12 @@ void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) *vert = m_colLabelVertAlign; } -int wxGrid::GetColLabelTextOrientation() +int wxGrid::GetColLabelTextOrientation() const { return m_colLabelTextOrientation; } -wxString wxGrid::GetRowLabelValue( int row ) +wxString wxGrid::GetRowLabelValue( int row ) const { if ( m_table ) { @@ -9249,7 +9408,7 @@ wxString wxGrid::GetRowLabelValue( int row ) } } -wxString wxGrid::GetColLabelValue( int col ) +wxString wxGrid::GetColLabelValue( int col ) const { if ( m_table ) { @@ -9265,7 +9424,13 @@ wxString wxGrid::GetColLabelValue( int col ) void wxGrid::SetRowLabelSize( int width ) { - width = wxMax( width, 0 ); + wxASSERT( width >= 0 || width == wxGRID_AUTOSIZE ); + + if ( width == wxGRID_AUTOSIZE ) + { + width = CalcColOrRowLabelAreaMinSize(wxGRID_ROW); + } + if ( width != m_rowLabelWidth ) { if ( width == 0 ) @@ -9288,7 +9453,13 @@ void wxGrid::SetRowLabelSize( int width ) void wxGrid::SetColLabelSize( int height ) { - height = wxMax( height, 0 ); + wxASSERT( height >=0 || height == wxGRID_AUTOSIZE ); + + if ( height == wxGRID_AUTOSIZE ) + { + height = CalcColOrRowLabelAreaMinSize(wxGRID_COLUMN); + } + if ( height != m_colLabelHeight ) { if ( height == 0 ) @@ -9506,7 +9677,7 @@ void wxGrid::SetCellHighlightPenWidth(int width) // make any visible change if the the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); - if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) + if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) return; wxRect rect = CellToRect(row, col); @@ -9524,7 +9695,8 @@ void wxGrid::SetCellHighlightROPenWidth(int width) // make any visible change if the the thickness is getting smaller. int row = m_currentCellCoords.GetRow(); int col = m_currentCellCoords.GetCol(); - if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) + if ( row == -1 || col == -1 || + GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 ) return; wxRect rect = CellToRect(row, col); @@ -9554,24 +9726,24 @@ void wxGrid::EnableGridLines( bool enable ) } } -int wxGrid::GetDefaultRowSize() +int wxGrid::GetDefaultRowSize() const { return m_defaultRowHeight; } -int wxGrid::GetRowSize( int row ) +int wxGrid::GetRowSize( int row ) const { wxCHECK_MSG( row >= 0 && row < m_numRows, 0, _T("invalid row index") ); return GetRowHeight(row); } -int wxGrid::GetDefaultColSize() +int wxGrid::GetDefaultColSize() const { return m_defaultColWidth; } -int wxGrid::GetColSize( int col ) +int wxGrid::GetColSize( int col ) const { wxCHECK_MSG( col >= 0 && col < m_numCols, 0, _T("invalid column index") ); @@ -9635,30 +9807,30 @@ void wxGrid::SetDefaultEditor(wxGridCellEditor *editor) } // ---------------------------------------------------------------------------- -// access to the default attrbiutes +// access to the default attributes // ---------------------------------------------------------------------------- -wxColour wxGrid::GetDefaultCellBackgroundColour() +wxColour wxGrid::GetDefaultCellBackgroundColour() const { return m_defaultCellAttr->GetBackgroundColour(); } -wxColour wxGrid::GetDefaultCellTextColour() +wxColour wxGrid::GetDefaultCellTextColour() const { return m_defaultCellAttr->GetTextColour(); } -wxFont wxGrid::GetDefaultCellFont() +wxFont wxGrid::GetDefaultCellFont() const { return m_defaultCellAttr->GetFont(); } -void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) +void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) const { m_defaultCellAttr->GetAlignment(horiz, vert); } -bool wxGrid::GetDefaultCellOverflow() +bool wxGrid::GetDefaultCellOverflow() const { return m_defaultCellAttr->GetOverflow(); } @@ -9677,7 +9849,7 @@ wxGridCellEditor *wxGrid::GetDefaultEditor() const // access to cell attributes // ---------------------------------------------------------------------------- -wxColour wxGrid::GetCellBackgroundColour(int row, int col) +wxColour wxGrid::GetCellBackgroundColour(int row, int col) const { wxGridCellAttr *attr = GetCellAttr(row, col); wxColour colour = attr->GetBackgroundColour(); @@ -9686,7 +9858,7 @@ wxColour wxGrid::GetCellBackgroundColour(int row, int col) return colour; } -wxColour wxGrid::GetCellTextColour( int row, int col ) +wxColour wxGrid::GetCellTextColour( int row, int col ) const { wxGridCellAttr *attr = GetCellAttr(row, col); wxColour colour = attr->GetTextColour(); @@ -9695,7 +9867,7 @@ wxColour wxGrid::GetCellTextColour( int row, int col ) return colour; } -wxFont wxGrid::GetCellFont( int row, int col ) +wxFont wxGrid::GetCellFont( int row, int col ) const { wxGridCellAttr *attr = GetCellAttr(row, col); wxFont font = attr->GetFont(); @@ -9704,14 +9876,14 @@ wxFont wxGrid::GetCellFont( int row, int col ) return font; } -void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) +void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) const { wxGridCellAttr *attr = GetCellAttr(row, col); attr->GetAlignment(horiz, vert); attr->DecRef(); } -bool wxGrid::GetCellOverflow( int row, int col ) +bool wxGrid::GetCellOverflow( int row, int col ) const { wxGridCellAttr *attr = GetCellAttr(row, col); bool allow = attr->GetOverflow(); @@ -9720,14 +9892,14 @@ bool wxGrid::GetCellOverflow( int row, int col ) return allow; } -void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) +void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const { wxGridCellAttr *attr = GetCellAttr(row, col); attr->GetSize( num_rows, num_cols ); attr->DecRef(); } -wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) +wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const { wxGridCellAttr* attr = GetCellAttr(row, col); wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col); @@ -9736,7 +9908,7 @@ wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) return renderer; } -wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) +wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) const { wxGridCellAttr* attr = GetCellAttr(row, col); wxGridCellEditor* editor = attr->GetEditor(this, row, col); @@ -9758,7 +9930,7 @@ bool wxGrid::IsReadOnly(int row, int col) const // attribute support: cache, automatic provider creation, ... // ---------------------------------------------------------------------------- -bool wxGrid::CanHaveAttributes() +bool wxGrid::CanHaveAttributes() const { if ( !m_table ) { @@ -9772,9 +9944,14 @@ void wxGrid::ClearAttrCache() { if ( m_attrCache.row != -1 ) { - wxSafeDecRef(m_attrCache.attr); + wxGridCellAttr *oldAttr = m_attrCache.attr; m_attrCache.attr = NULL; m_attrCache.row = -1; + // wxSafeDecRec(...) might cause event processing that accesses + // the cached attribute, if one exists (e.g. by deleting the + // editor stored within the attribute). Therefore it is important + // to invalidate the cache before calling wxSafeDecRef! + wxSafeDecRef(oldAttr); } } @@ -9896,6 +10073,8 @@ void wxGrid::SetColFormatCustom(int col, const wxString& typeName) attr = new wxGridCellAttr; wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName); attr->SetRenderer(renderer); + wxGridCellEditor *editor = GetDefaultEditorForType(typeName); + attr->SetEditor(editor); SetColAttr(col, attr); @@ -10015,7 +10194,7 @@ void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols ) wxT("wxGrid::SetCellSize setting cell size to < 1")); // if this was already a multicell then "turn off" the other cells first - if ((cell_rows > 1) || (cell_rows > 1)) + if ((cell_rows > 1) || (cell_cols > 1)) { int i, j; for (j=row; j < row + cell_rows; j++) @@ -10112,10 +10291,7 @@ wxGridCellEditor * wxGrid::GetDefaultEditorForType(const wxString& typeName) con int index = m_typeRegistry->FindOrCloneDataType(typeName); if ( index == wxNOT_FOUND ) { - wxString errStr; - - errStr.Printf(wxT("Unknown data type name [%s]"), typeName.c_str()); - wxFAIL_MSG(errStr.c_str()); + wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str())); return NULL; } @@ -10128,10 +10304,7 @@ wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) int index = m_typeRegistry->FindOrCloneDataType(typeName); if ( index == wxNOT_FOUND ) { - wxString errStr; - - errStr.Printf(wxT("Unknown data type name [%s]"), typeName.c_str()); - wxFAIL_MSG(errStr.c_str()); + wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str())); return NULL; } @@ -10184,6 +10357,19 @@ void wxGrid::SetRowSize( int row, int height ) { wxCHECK_RET( row >= 0 && row < m_numRows, _T("invalid row index") ); + // if < 0 then calculate new height from label + if ( height < 0 ) + { + long w, h; + wxArrayString lines; + wxClientDC dc(m_rowLabelWin); + dc.SetFont(GetLabelFont()); + StringToLines(GetRowLabelValue( row ), lines); + GetTextBoxSize( dc, lines, &w, &h ); + //check that it is not less than the minimal height + height = wxMax(h, GetRowMinimalAcceptableHeight()); + } + // See comment in SetColSize if ( height < GetRowMinimalAcceptableHeight()) return; @@ -10198,8 +10384,7 @@ void wxGrid::SetRowSize( int row, int height ) int diff = h - m_rowHeights[row]; m_rowHeights[row] = h; - int i; - for ( i = row; i < m_numRows; i++ ) + for ( int i = row; i < m_numRows; i++ ) { m_rowBottoms[i] += diff; } @@ -10210,7 +10395,8 @@ void wxGrid::SetRowSize( int row, int height ) void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols ) { - m_defaultColWidth = wxMax( width, m_minAcceptableColWidth ); + // we dont allow zero default column width + m_defaultColWidth = wxMax( wxMax( width, m_minAcceptableColWidth ), 1 ); if ( resizeExistingCols ) { @@ -10229,6 +10415,23 @@ void wxGrid::SetColSize( int col, int width ) { wxCHECK_RET( col >= 0 && col < m_numCols, _T("invalid column index") ); + // if < 0 then calculate new width from label + if ( width < 0 ) + { + long w, h; + wxArrayString lines; + wxClientDC dc(m_colLabelWin); + dc.SetFont(GetLabelFont()); + StringToLines(GetColLabelValue(col), lines); + if ( GetColLabelTextOrientation() == wxHORIZONTAL ) + GetTextBoxSize( dc, lines, &w, &h ); + else + GetTextBoxSize( dc, lines, &h, &w ); + width = w + 6; + //check that it is not less than the minimal width + width = wxMax(width, GetColMinimalAcceptableWidth()); + } + // should we check that it's bigger than GetColMinimalWidth(col) here? // (VZ) // No, because it is reasonable to assume the library user know's @@ -10246,28 +10449,13 @@ void wxGrid::SetColSize( int col, int width ) InitColWidths(); } - // if < 0 then calculate new width from label - if ( width < 0 ) - { - long w, h; - wxArrayString lines; - wxClientDC dc(m_colLabelWin); - dc.SetFont(GetLabelFont()); - StringToLines(GetColLabelValue(col), lines); - GetTextBoxSize(dc, lines, &w, &h); - width = w + 6; - } - int w = wxMax( 0, width ); int diff = w - m_colWidths[col]; m_colWidths[col] = w; - int i; - int colPos; - for ( colPos = GetColPos( col ); colPos < m_numCols; colPos++ ) + for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ ) { - i = GetColAt( colPos ); - m_colRights[i] += diff; + m_colRights[GetColAt(colPos)] += diff; } if ( !GetBatchCount() ) @@ -10338,8 +10526,11 @@ int wxGrid::GetRowMinimalAcceptableHeight() const // auto sizing // ---------------------------------------------------------------------------- -void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column ) +void +wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction) { + const bool column = direction == wxGRID_COLUMN; + wxClientDC dc(m_gridWin); // cancel editing of cell @@ -10384,12 +10575,12 @@ void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column ) if ( column ) { - dc.GetTextExtent( GetColLabelValue(col), &w, &h ); + dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h ); if ( GetColLabelTextOrientation() == wxVERTICAL ) w = h; } else - dc.GetTextExtent( GetRowLabelValue(row), &w, &h ); + dc.GetMultiLineTextExtent( GetRowLabelValue(row), &w, &h ); extent = column ? w : h; if ( extent > extentMax ) @@ -10412,6 +10603,12 @@ void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column ) if ( column ) { + // Ensure automatic width is not less than minimal width. See the + // comment in SetColSize() for explanation of why this isn't done + // in SetColSize(). + if ( !setAsMin ) + extentMax = wxMax(extentMax, GetColMinimalWidth(col)); + SetColSize( col, extentMax ); if ( !GetBatchCount() ) { @@ -10427,6 +10624,12 @@ void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column ) } else { + // Ensure automatic width is not less than minimal height. See the + // comment in SetColSize() for explanation of why this isn't done + // in SetRowSize(). + if ( !setAsMin ) + extentMax = wxMax(extentMax, GetRowMinimalHeight(row)); + SetRowSize(row, extentMax); if ( !GetBatchCount() ) { @@ -10450,12 +10653,66 @@ void wxGrid::AutoSizeColOrRow( int colOrRow, bool setAsMin, bool column ) } } +wxCoord wxGrid::CalcColOrRowLabelAreaMinSize(wxGridDirection direction) +{ + // calculate size for the rows or columns? + const bool calcRows = direction == wxGRID_ROW; + + wxClientDC dc(calcRows ? GetGridRowLabelWindow() + : GetGridColLabelWindow()); + dc.SetFont(GetLabelFont()); + + // which dimension should we take into account for calculations? + // + // for columns, the text can be only horizontal so it's easy but for rows + // we also have to take into account the text orientation + const bool + useWidth = calcRows || (GetColLabelTextOrientation() == wxVERTICAL); + + wxArrayString lines; + wxCoord extentMax = 0; + + const int numRowsOrCols = calcRows ? m_numRows : m_numCols; + for ( int rowOrCol = 0; rowOrCol < numRowsOrCols; rowOrCol++ ) + { + lines.Clear(); + + wxString label = calcRows ? GetRowLabelValue(rowOrCol) + : GetColLabelValue(rowOrCol); + StringToLines(label, lines); + + long w, h; + GetTextBoxSize(dc, lines, &w, &h); + + const wxCoord extent = useWidth ? w : h; + if ( extent > extentMax ) + extentMax = extent; + } + + if ( !extentMax ) + { + // empty column - give default extent (notice that if extentMax is less + // than default extent but != 0, it's OK) + extentMax = calcRows ? GetDefaultRowLabelSize() + : GetDefaultColLabelSize(); + } + + // leave some space around text (taken from AutoSizeColOrRow) + if ( calcRows ) + extentMax += 10; + else + extentMax += 6; + + return extentMax; +} + int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin) { int width = m_rowLabelWidth; - if ( !calcOnly ) - BeginBatch(); + wxGridUpdateLocker locker; + if(!calcOnly) + locker.Create(this); for ( int col = 0; col < m_numCols; col++ ) { @@ -10465,9 +10722,6 @@ int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin) width += GetColWidth(col); } - if ( !calcOnly ) - EndBatch(); - return width; } @@ -10475,8 +10729,9 @@ int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin) { int height = m_colLabelHeight; - if ( !calcOnly ) - BeginBatch(); + wxGridUpdateLocker locker; + if(!calcOnly) + locker.Create(this); for ( int row = 0; row < m_numRows; row++ ) { @@ -10486,31 +10741,24 @@ int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin) height += GetRowHeight(row); } - if ( !calcOnly ) - EndBatch(); - return height; } void wxGrid::AutoSize() { - BeginBatch(); - - wxSize size(SetOrCalcColumnSizes(false), SetOrCalcRowSizes(false)); + wxGridUpdateLocker locker(this); - // round up the size to a multiple of scroll step - this ensures that we - // won't get the scrollbars if we're sized exactly to this width - // CalcDimension adds m_extraWidth + 1 etc. to calculate the necessary - // scrollbar steps - wxSize sizeFit( - GetScrollX(size.x + m_extraWidth + 1) * m_scrollLineX, - GetScrollY(size.y + m_extraHeight + 1) * m_scrollLineY ); + // we need to round up the size of the scrollable area to a multiple of + // scroll step to ensure that we don't get the scrollbars when we're sized + // exactly to fit our contents + wxSize size(SetOrCalcColumnSizes(false) - m_rowLabelWidth + m_extraWidth, + SetOrCalcRowSizes(false) - m_colLabelHeight + m_extraHeight); + wxSize sizeFit(GetScrollX(size.x) * GetScrollLineX(), + GetScrollY(size.y) * GetScrollLineY()); // distribute the extra space between the columns/rows to avoid having // extra white space - - // Remove the extra m_extraWidth + 1 added above - wxCoord diff = sizeFit.x - size.x + (m_extraWidth + 1); + wxCoord diff = sizeFit.x - size.x; if ( diff && m_numCols ) { // try to resize the columns uniformly @@ -10535,7 +10783,7 @@ void wxGrid::AutoSize() } // same for rows - diff = sizeFit.y - size.y - (m_extraHeight + 1); + diff = sizeFit.y - size.y; if ( diff && m_numRows ) { // try to resize the columns uniformly @@ -10559,16 +10807,15 @@ void wxGrid::AutoSize() } } - EndBatch(); - - SetClientSize(sizeFit); + // we know that we're not going to have scrollbars so disable them now to + // avoid trouble in SetClientSize() which can otherwise set the correct + // client size but also leave space for (not needed any more) scrollbars + SetScrollbars(0, 0, 0, 0, 0, 0, true); + SetClientSize(sizeFit.x + m_rowLabelWidth, sizeFit.y + m_colLabelHeight); } void wxGrid::AutoSizeRowLabelSize( int row ) { - wxArrayString lines; - long w, h; - // Hide the edit control, so it // won't interfere with drag-shrinking. if ( IsCellEditControlShown() ) @@ -10578,20 +10825,12 @@ void wxGrid::AutoSizeRowLabelSize( int row ) } // autosize row height depending on label text - StringToLines( GetRowLabelValue( row ), lines ); - wxClientDC dc( m_rowLabelWin ); - GetTextBoxSize( dc, lines, &w, &h ); - if ( h < m_defaultRowHeight ) - h = m_defaultRowHeight; - SetRowSize(row, h); + SetRowSize(row, -1); ForceRefresh(); } void wxGrid::AutoSizeColLabelSize( int col ) { - wxArrayString lines; - long w, h; - // Hide the edit control, so it // won't interfere with drag-shrinking. if ( IsCellEditControlShown() ) @@ -10601,60 +10840,28 @@ void wxGrid::AutoSizeColLabelSize( int col ) } // autosize column width depending on label text - StringToLines( GetColLabelValue( col ), lines ); - wxClientDC dc( m_colLabelWin ); - if ( GetColLabelTextOrientation() == wxHORIZONTAL ) - GetTextBoxSize( dc, lines, &w, &h ); - else - GetTextBoxSize( dc, lines, &h, &w ); - if ( w < m_defaultColWidth ) - w = m_defaultColWidth; - SetColSize(col, w); + SetColSize(col, -1); ForceRefresh(); } wxSize wxGrid::DoGetBestSize() const { - // don't set sizes, only calculate them wxGrid *self = (wxGrid *)this; // const_cast - int width, height; - width = self->SetOrCalcColumnSizes(true); - height = self->SetOrCalcRowSizes(true); - - if (!width) - width = 100; - if (!height) - height = 80; - - // Round up to a multiple the scroll rate - // NOTE: this still doesn't get rid of the scrollbars; - // is there any magic incantation for that? - int xpu, ypu; - GetScrollPixelsPerUnit(&xpu, &ypu); - if (xpu) - width += 1 + xpu - (width % xpu); - if (ypu) - height += 1 + ypu - (height % ypu); - - // limit to 1/4 of the screen size - int maxwidth, maxheight; - wxDisplaySize( &maxwidth, &maxheight ); - maxwidth /= 2; - maxheight /= 2; - if ( width > maxwidth ) - width = maxwidth; - if ( height > maxheight ) - height = maxheight; - - wxSize best(width, height); + // we do the same as in AutoSize() here with the exception that we don't + // change the column/row sizes, only calculate them + wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth, + self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight); + wxSize sizeFit(GetScrollX(size.x) * GetScrollLineX(), + GetScrollY(size.y) * GetScrollLineY()); // NOTE: This size should be cached, but first we need to add calls to // InvalidateBestSize everywhere that could change the results of this // calculation. // CacheBestSize(size); - return best; + return wxSize(sizeFit.x + m_rowLabelWidth, sizeFit.y + m_colLabelHeight) + + GetWindowBorderSize(); } void wxGrid::Fit() @@ -10793,7 +11000,7 @@ void wxGrid::DeselectCell( int row, int col ) m_selection->ToggleCellSelection(row, col); } -bool wxGrid::IsSelection() +bool wxGrid::IsSelection() const { return ( m_selection && (m_selection->IsSelection() || ( m_selectingTopLeft != wxGridNoCellCoords && @@ -10866,9 +11073,13 @@ wxArrayInt wxGrid::GetSelectedCols() const void wxGrid::ClearSelection() { + wxRect r1 = BlockToDeviceRect( m_selectingTopLeft, m_selectingBottomRight); + wxRect r2 = BlockToDeviceRect( m_currentCellCoords, m_selectingKeyboard ); m_selectingTopLeft = m_selectingBottomRight = m_selectingKeyboard = wxGridNoCellCoords; + Refresh( false, &r1 ); + Refresh( false, &r2 ); if ( m_selection ) m_selection->ClearSelection(); } @@ -10876,37 +11087,36 @@ void wxGrid::ClearSelection() // This function returns the rectangle that encloses the given block // in device coords clipped to the client size of the grid window. // -wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft, - const wxGridCellCoords &bottomRight ) +wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft, + const wxGridCellCoords& bottomRight ) const { - wxRect rect( wxGridNoCellRect ); - wxRect cellRect; - - cellRect = CellToRect( topLeft ); - if ( cellRect != wxGridNoCellRect ) + wxRect resultRect; + wxRect tempCellRect = CellToRect(topLeft); + if ( tempCellRect != wxGridNoCellRect ) { - rect = cellRect; + resultRect = tempCellRect; } else { - rect = wxRect(0, 0, 0, 0); + resultRect = wxRect(0, 0, 0, 0); } - cellRect = CellToRect( bottomRight ); - if ( cellRect != wxGridNoCellRect ) + tempCellRect = CellToRect(bottomRight); + if ( tempCellRect != wxGridNoCellRect ) { - rect += cellRect; + resultRect += tempCellRect; } else { + // If both inputs were "wxGridNoCellRect," then there's nothing to do. return wxGridNoCellRect; } - int i, j; - int left = rect.GetLeft(); - int top = rect.GetTop(); - int right = rect.GetRight(); - int bottom = rect.GetBottom(); + // Ensure that left/right and top/bottom pairs are in order. + int left = resultRect.GetLeft(); + int top = resultRect.GetTop(); + int right = resultRect.GetRight(); + int bottom = resultRect.GetBottom(); int leftCol = topLeft.GetCol(); int topRow = topLeft.GetRow(); @@ -10915,67 +11125,105 @@ wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords &topLeft, if (left > right) { - i = left; + int tmp = left; left = right; - right = i; - i = leftCol; + right = tmp; + + tmp = leftCol; leftCol = rightCol; - rightCol = i; + rightCol = tmp; } if (top > bottom) { - i = top; + int tmp = top; top = bottom; - bottom = i; - i = topRow; + bottom = tmp; + + tmp = topRow; topRow = bottomRow; - bottomRow = i; + bottomRow = tmp; } - for ( j = topRow; j <= bottomRow; j++ ) + // The following loop is ONLY necessary to detect and handle merged cells. + int cw, ch; + m_gridWin->GetClientSize( &cw, &ch ); + + // Get the origin coordinates: notice that they will be negative if the + // grid is scrolled downwards/to the right. + int gridOriginX = 0; + int gridOriginY = 0; + CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY); + + int onScreenLeftmostCol = internalXToCol(-gridOriginX); + int onScreenUppermostRow = internalYToRow(-gridOriginY); + + int onScreenRightmostCol = internalXToCol(-gridOriginX + cw); + int onScreenBottommostRow = internalYToRow(-gridOriginY + ch); + + // Bound our loop so that we only examine the portion of the selected block + // that is shown on screen. Therefore, we compare the Top-Left block values + // to the Top-Left screen values, and the Bottom-Right block values to the + // Bottom-Right screen values, choosing appropriately. + const int visibleTopRow = wxMax(topRow, onScreenUppermostRow); + const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow); + const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol); + const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol); + + for ( int j = visibleTopRow; j <= visibleBottomRow; j++ ) { - for ( i = leftCol; i <= rightCol; i++ ) + for ( int i = visibleLeftCol; i <= visibleRightCol; i++ ) { - if ((j == topRow) || (j == bottomRow) || (i == leftCol) || (i == rightCol)) + if ( (j == visibleTopRow) || (j == visibleBottomRow) || + (i == visibleLeftCol) || (i == visibleRightCol) ) { - cellRect = CellToRect( j, i ); + tempCellRect = CellToRect( j, i ); - if (cellRect.x < left) - left = cellRect.x; - if (cellRect.y < top) - top = cellRect.y; - if (cellRect.x + cellRect.width > right) - right = cellRect.x + cellRect.width; - if (cellRect.y + cellRect.height > bottom) - bottom = cellRect.y + cellRect.height; + if (tempCellRect.x < left) + left = tempCellRect.x; + if (tempCellRect.y < top) + top = tempCellRect.y; + if (tempCellRect.x + tempCellRect.width > right) + right = tempCellRect.x + tempCellRect.width; + if (tempCellRect.y + tempCellRect.height > bottom) + bottom = tempCellRect.y + tempCellRect.height; } else { - i = rightCol; // jump over inner cells. + i = visibleRightCol; // jump over inner cells. } } } - // convert to scrolled coords - // + // Convert to scrolled coords CalcScrolledPosition( left, top, &left, &top ); CalcScrolledPosition( right, bottom, &right, &bottom ); - int cw, ch; - m_gridWin->GetClientSize( &cw, &ch ); - if (right < 0 || bottom < 0 || left > cw || top > ch) return wxRect(0,0,0,0); - rect.SetLeft( wxMax(0, left) ); - rect.SetTop( wxMax(0, top) ); - rect.SetRight( wxMin(cw, right) ); - rect.SetBottom( wxMin(ch, bottom) ); + resultRect.SetLeft( wxMax(0, left) ); + resultRect.SetTop( wxMax(0, top) ); + resultRect.SetRight( wxMin(cw, right) ); + resultRect.SetBottom( wxMin(ch, bottom) ); - return rect; + return resultRect; +} + +// ---------------------------------------------------------------------------- +// drop target +// ---------------------------------------------------------------------------- + +#if wxUSE_DRAG_AND_DROP + +// this allow setting drop target directly on wxGrid +void wxGrid::SetDropTarget(wxDropTarget *dropTarget) +{ + GetGridWindow()->SetDropTarget(dropTarget); } +#endif // wxUSE_DRAG_AND_DROP + // ---------------------------------------------------------------------------- // grid event classes // ----------------------------------------------------------------------------