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()
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,
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,
bool wxGridCellNumberEditor::EndEdit(int row, int col,
wxGrid* grid)
{
- bool changed;
long value = 0;
wxString text;
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()
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);
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()
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();
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)
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
{
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--;
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--;
{
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);
}
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);
{
if ( m_attrProvider )
{
- attr->SetKind(wxGridCellAttr::Cell);
+ if ( attr )
+ attr->SetKind(wxGridCellAttr::Cell);
m_attrProvider->SetAttr(attr, row, col);
}
else
}
- 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 );
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 );
}
}
- dc.SetClippingRegion( clippedcells );
+ dc.SetDeviceClippingRegion( clippedcells );
// horizontal grid lines
{
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);
}
}
attr = new wxGridCellAttr;
wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName);
attr->SetRenderer(renderer);
+ wxGridCellEditor *editor = GetDefaultEditorForType(typeName);
+ attr->SetEditor(editor);
SetColAttr(col, attr);
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++)
{
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;
{
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
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;
void wxGrid::AutoSizeRowLabelSize( int row )
{
- wxArrayString lines;
- long w, h;
-
// Hide the edit control, so it
// won't interfere with drag-shrinking.
if ( IsCellEditControlShown() )
}
// 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() )
}
// 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();
}
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();
}
// 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();
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;
}
// ----------------------------------------------------------------------------