X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/04ee05f92add0ab92ce08ecb827e3be41336aa06..386a2898e3a55362d30d22adf024b95ce0d5773f:/src/generic/grid.cpp diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp index 2ba9c636e8..6590754456 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp @@ -70,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() @@ -79,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, @@ -645,10 +672,7 @@ void wxGridCellTextEditor::DoCreate(wxWindow* parent, wxEvtHandler* evtHandler, long style) { - style |= wxTE_PROCESS_ENTER | - wxTE_PROCESS_TAB | - wxTE_AUTO_SCROLL | - wxNO_BORDER; + style |= wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxNO_BORDER; m_control = new wxTextCtrl(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, @@ -1089,7 +1113,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); @@ -1097,35 +1121,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() @@ -1556,19 +1598,7 @@ void wxGridCellChoiceEditor::BeginEdit(int row, int col, wxGrid* grid) m_startValue = grid->GetTable()->GetValue(row, col); - 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); - } + Reset(); // this updates combo box to correspond to m_startValue Combo()->SetFocus(); @@ -1596,8 +1626,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) @@ -2287,8 +2328,8 @@ void wxGridCellBoolRenderer::Draw(wxGrid& grid, int flags = 0; if (value) - flags |= wxCONTROL_CHECKED; - + flags |= wxCONTROL_CHECKED; + wxRendererNative::Get().DrawCheckBox( &grid, dc, rectBorder, flags ); } @@ -2587,21 +2628,25 @@ wxGridCellEditor* wxGridCellAttr::GetEditor(const wxGrid* grid, int row, int col 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 { @@ -2650,8 +2695,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--; @@ -2686,8 +2729,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--; @@ -2746,7 +2787,8 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) { if ( attr ) { - // add the attribute + // 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); } @@ -2755,15 +2797,19 @@ void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol) 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); @@ -3164,7 +3210,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 @@ -5987,7 +6034,8 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event ) } - 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 ); @@ -6005,7 +6053,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 ); @@ -7214,7 +7263,7 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords ) m_currentCellCoords = coords; wxGridCellAttr *attr = GetCellAttr( coords ); -#if !defined(__WXMAC__) +#if !defined(__WXMAC__) DrawCellHighlight( dc, attr ); #endif attr->DecRef(); @@ -7819,7 +7868,7 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) ) } } - dc.SetClippingRegion( clippedcells ); + dc.SetDeviceClippingRegion( clippedcells ); // horizontal grid lines @@ -7924,7 +7973,7 @@ void wxGrid::SetUseNativeColLabels( bool native ) int height = wxRendererNative::Get().GetHeaderButtonHeight( this ); SetColLabelSize( height ); } - + m_colLabelWin->Refresh(); } @@ -8412,7 +8461,7 @@ void wxGrid::ShowCellEditControl() if (rect.GetRight() > client_right) rect.SetRight( client_right - 1 ); } - + editor->SetCellAttr( attr ); editor->SetSize( rect ); if (nXMove != 0) @@ -9633,7 +9682,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); @@ -10005,6 +10055,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); @@ -11004,9 +11056,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(); } @@ -11014,37 +11070,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 ) const +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(); @@ -11053,65 +11108,89 @@ 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; } // ----------------------------------------------------------------------------