// globals
// ----------------------------------------------------------------------------
+namespace
+{
+
//#define DEBUG_ATTR_CACHE
#ifdef DEBUG_ATTR_CACHE
static size_t gs_nAttrCacheHits = 0;
static size_t gs_nAttrCacheMisses = 0;
#endif
+// this struct simply combines together the default header renderers
+//
+// as the renderers ctors are trivial, there is no problem with making them
+// globals
+struct DefaultHeaderRenderers
+{
+ wxGridColumnHeaderRendererDefault colRenderer;
+ wxGridRowHeaderRendererDefault rowRenderer;
+ wxGridCornerHeaderRendererDefault cornerRenderer;
+} gs_defaultHeaderRenderers;
+
+} // anonymous namespace
+
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl)
EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick)
+ EVT_HEADER_DCLICK(wxID_ANY, wxGridHeaderCtrl::OnDoubleClick)
+ EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxGridHeaderCtrl::OnRightClick)
EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize)
EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing)
}
}
+void wxGridCellAttr::GetNonDefaultAlignment(int *hAlign, int *vAlign) const
+{
+ if ( hAlign && m_hAlign != wxALIGN_INVALID )
+ *hAlign = m_hAlign;
+
+ if ( vAlign && m_vAlign != wxALIGN_INVALID )
+ *vAlign = m_vAlign;
+}
+
void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const
{
if ( num_rows )
}
}
+const wxGridColumnHeaderRenderer&
+wxGridCellAttrProvider::GetColumnHeaderRenderer(int WXUNUSED(col))
+{
+ return gs_defaultHeaderRenderers.colRenderer;
+}
+
+const wxGridRowHeaderRenderer&
+wxGridCellAttrProvider::GetRowHeaderRenderer(int WXUNUSED(row))
+{
+ return gs_defaultHeaderRenderers.rowRenderer;
+}
+
+const wxGridCornerHeaderRenderer& wxGridCellAttrProvider::GetCornerRenderer()
+{
+ return gs_defaultHeaderRenderers.cornerRenderer;
+}
+
// ----------------------------------------------------------------------------
// wxGridTableBase
// ----------------------------------------------------------------------------
Create();
SetInitialSize(size);
- SetScrollRate(m_scrollLineX, m_scrollLineY);
CalcDimensions();
return true;
m_table = NULL;
}
- delete m_selection;
- m_selection = NULL;
+ wxDELETE(m_selection);
m_ownTable = false;
m_numRows = 0;
m_extraWidth =
m_extraHeight = 0;
- m_scrollLineX = GRID_SCROLL_LINE_X;
- m_scrollLineY = GRID_SCROLL_LINE_Y;
+ // we can't call SetScrollRate() as the window isn't created yet but OTOH
+ // we don't need to call it neither as the scroll position is (0, 0) right
+ // now anyhow, so just set the parameters directly
+ m_xScrollPixelsPerLine = GRID_SCROLL_LINE_X;
+ m_yScrollPixelsPerLine = GRID_SCROLL_LINE_Y;
}
// ----------------------------------------------------------------------------
}
else
{
- DoEndMoveCol(XToPos(x));
+ // get the position of the column we're over
+ int pos = XToPos(x);
+
+ // we may need to adjust the drop position but don't bother
+ // checking for it if we can't anyhow
+ if ( pos > 1 )
+ {
+ // also find the index of the column we're over: notice
+ // that the existing "col" variable may be invalid but
+ // we need a valid one here
+ const int colValid = GetColAt(pos);
+
+ // if we're on the "near" (usually left but right in
+ // RTL case) part of the column, the actual position we
+ // should be placed in is actually the one before it
+ bool onNearPart;
+ const int middle = GetColLeft(colValid) +
+ GetColWidth(colValid)/2;
+ if ( GetLayoutDirection() == wxLayout_LeftToRight )
+ onNearPart = (x <= middle);
+ else // wxLayout_RightToLeft
+ onNearPart = (x > middle);
+
+ if ( onNearPart )
+ pos--;
+ }
+
+ DoEndMoveCol(pos);
}
break;
bool captureMouse)
{
#if wxUSE_LOG_TRACE
- static const wxChar *cursorModes[] =
+ static const wxChar *const cursorModes[] =
{
wxT("SELECT_CELL"),
wxT("RESIZE_ROW"),
if ( isFirstDrag )
{
+ wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" );
+
m_winCapture = m_gridWin;
m_winCapture->CaptureMouse();
}
}
else
{
+ if ( m_selection )
+ {
+ // In row or column selection mode just clicking on the cell
+ // should select the row or column containing it: this is more
+ // convenient for the kinds of controls that use such selection
+ // mode and is compatible with 2.8 behaviour (see #12062).
+ switch ( m_selection->GetSelectionMode() )
+ {
+ case wxGridSelectCells:
+ case wxGridSelectRowsOrColumns:
+ // nothing to do in these cases
+ break;
+
+ case wxGridSelectRows:
+ m_selection->SelectRow(coords.GetRow());
+ break;
+
+ case wxGridSelectColumns:
+ m_selection->SelectCol(coords.GetCol());
+ break;
+ }
+ }
+
m_waitForSlowClick = m_currentCellCoords == coords &&
coords != wxGridNoCellCoords;
SetCurrentCell( coords );
void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
{
+ if ( event.Entering() || event.Leaving() )
+ {
+ // we don't care about these events but we must not reset m_isDragging
+ // if they happen so return before anything else is done
+ event.Skip();
+ return;
+ }
+
const wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
// coordinates of the cell under mouse
m_isDragging = false;
m_startDragPos = wxDefaultPosition;
- // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL
- // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under
- // wxGTK
-#if 0
- if ( event.Entering() || event.Leaving() )
- {
- ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
- m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
- }
-#endif // 0
-
// deal with various button presses
if ( event.IsButton() )
{
oper.SelectSize(rect) = oper.Select(size);
int subtractLines = 0;
- const int lineStart = oper.PosToLine(this, posLineStart);
+ const int lineStart = doper.PosToLine(this, posLineStart);
if ( lineStart >= 0 )
{
// ensure that if we have a multi-cell block we redraw all of
// it by increasing the refresh area to cover it entirely if a
// part of it is affected
- const int lineEnd = oper.PosToLine(this, posLineEnd, true);
+ const int lineEnd = doper.PosToLine(this, posLineEnd, true);
for ( int line = lineStart; line < lineEnd; line++ )
{
int cellLines = oper.Select(
break;
case WXK_HOME:
- if ( event.ControlDown() )
- {
- GoToCell(0, 0);
- }
- else
- {
- event.Skip();
- }
+ GoToCell(event.ControlDown() ? 0
+ : m_currentCellCoords.GetRow(),
+ 0);
break;
case WXK_END:
- if ( event.ControlDown() )
- {
- GoToCell(m_numRows - 1, m_numCols - 1);
- }
- else
- {
- event.Skip();
- }
+ GoToCell(event.ControlDown() ? m_numRows - 1
+ : m_currentCellCoords.GetRow(),
+ m_numCols - 1);
break;
case WXK_PAGEUP:
wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol,
int bottomRow, int rightCol)
{
+ MakeCellVisible(m_selectedBlockCorner);
+ m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
+
if ( m_selection )
{
switch ( m_selection->GetSelectionMode() )
}
}
- m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
- MakeCellVisible(m_selectedBlockCorner);
-
EnsureFirstLessThanSecond(topRow, bottomRow);
EnsureFirstLessThanSecond(leftCol, rightCol);
if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
return;
+ wxGridCellAttrProvider * const
+ attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
+
+ // notice that an explicit static_cast is needed to avoid a compilation
+ // error with VC7.1 which, for some reason, tries to instantiate (abstract)
+ // wxGridRowHeaderRenderer class without it
const wxGridRowHeaderRenderer&
- rend = m_table->GetAttrProvider()->GetRowHeaderRenderer(row);
+ rend = attrProvider ? attrProvider->GetRowHeaderRenderer(row)
+ : static_cast<const wxGridRowHeaderRenderer&>
+ (gs_defaultHeaderRenderers.rowRenderer);
+
wxRect rect(0, GetRowTop(row), m_rowLabelWidth, GetRowHeight(row));
rend.DrawBorder(*this, dc, rect);
rect.width++;
rect.height++;
+ wxGridCellAttrProvider * const
+ attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
const wxGridCornerHeaderRenderer&
- rend = m_table->GetAttrProvider()->GetCornerRenderer();
+ rend = attrProvider ? attrProvider->GetCornerRenderer()
+ : static_cast<wxGridCornerHeaderRenderer&>
+ (gs_defaultHeaderRenderers.cornerRenderer);
rend.DrawBorder(*this, dc, rect);
}
int colLeft = GetColLeft(col);
wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight);
+ wxGridCellAttrProvider * const
+ attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
const wxGridColumnHeaderRenderer&
- rend = m_table->GetAttrProvider()->GetColumnHeaderRenderer(col);
+ rend = attrProvider ? attrProvider->GetColumnHeaderRenderer(col)
+ : static_cast<wxGridColumnHeaderRenderer&>
+ (gs_defaultHeaderRenderers.colRenderer);
if ( m_nativeColumnLabels )
{
}
else
{
+ // It is reported that we need to erase the background to avoid display
+ // artefacts, see #12055.
+ wxDCBrushChanger setBrush(dc, m_colWindow->GetBackgroundColour());
+ dc.DrawRectangle(rect);
+
rend.DrawBorder(*this, dc, rect);
}
bool wxGrid::IsCurrentCellReadOnly() const
{
- // const_cast
- wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
+ wxGridCellAttr*
+ attr = const_cast<wxGrid *>(this)->GetCellAttr(m_currentCellCoords);
bool readonly = attr->IsReadOnly();
attr->DecRef();
else if ( line > 0 &&
pos - oper.GetLineStartPos(this,
line) < WXGRID_LABEL_EDGE_ZONE )
- return line - 1;
+ {
+ return oper.GetLineBefore(this, line);
+ }
}
return -1;
//
// Sometimes GRID_SCROLL_LINE / 2 is not enough,
// so just add a full scroll unit...
- ypos += m_scrollLineY;
+ ypos += m_yScrollPixelsPerLine;
}
// special handling for wide cells - show always left part of the cell!
xpos = x0 + (right - cw);
// see comment for ypos above
- xpos += m_scrollLineX;
+ xpos += m_xScrollPixelsPerLine;
}
if ( xpos != -1 || ypos != -1 )
{
if ( xpos != -1 )
- xpos /= m_scrollLineX;
+ xpos /= m_xScrollPixelsPerLine;
if ( ypos != -1 )
- ypos /= m_scrollLineY;
+ ypos /= m_yScrollPixelsPerLine;
Scroll( xpos, ypos );
AdjustScrollbars();
}
return allow;
}
-void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const
+wxGrid::CellSpan
+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();
+
+ if ( *num_rows == 1 && *num_cols == 1 )
+ return CellSpan_None; // just a normal cell
+
+ if ( *num_rows < 0 || *num_cols < 0 )
+ return CellSpan_Inside; // covered by a multi-span cell
+
+ // this cell spans multiple cells to its right/bottom
+ return CellSpan_Main;
}
wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const
}
}
+void wxGrid::RefreshAttr(int row, int col)
+{
+ if ( m_attrCache.row == row && m_attrCache.col == col )
+ ClearAttrCache();
+}
+
+
void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
{
if ( attr != NULL )
{
- wxGrid *self = (wxGrid *)this; // const_cast
+ wxGrid * const self = const_cast<wxGrid *>(this);
self->ClearAttrCache();
self->m_attrCache.row = row;
HideCellEditControl();
SaveEditControlValue();
- // init both of them to avoid compiler warnings, even if we only need one
- int row = -1,
- col = -1;
+ // initialize both of them just to avoid compiler warnings even if only
+ // really needs to be initialized here
+ int row,
+ col;
if ( column )
+ {
+ row = -1;
col = colOrRow;
+ }
else
+ {
row = colOrRow;
+ col = -1;
+ }
wxCoord extent, extentMax = 0;
int max = column ? m_numRows : m_numCols;
else
col = rowOrCol;
+ // we need to account for the cells spanning multiple columns/rows:
+ // while they may need a lot of space, they don't need all of it in
+ // this column/row
+ int numRows, numCols;
+ const CellSpan span = GetCellSize(row, col, &numRows, &numCols);
+ if ( span == CellSpan_Inside )
+ {
+ // we need to get the size of the main cell, not of a cell hidden
+ // by it
+ row += numRows;
+ col += numCols;
+
+ // get the size of the main cell too
+ GetCellSize(row, col, &numRows, &numCols);
+ }
+
wxGridCellAttr *attr = GetCellAttr(row, col);
wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
if ( renderer )
{
wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
extent = column ? size.x : size.y;
+
+ if ( span != CellSpan_None )
+ {
+ // we spread the size of a spanning cell over all the cells it
+ // covers evenly -- this is probably not ideal but we can't
+ // really do much better here
+ //
+ // notice that numCols and numRows are never 0 as they
+ // correspond to the size of the main cell of the span and not
+ // of the cell inside it
+ extent /= column ? numCols : numRows;
+ }
+
if ( extent > extentMax )
extentMax = extent;
// 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);
-
- // restore the scroll rate parameters overwritten by SetScrollbars()
- SetScrollRate(m_scrollLineX, m_scrollLineY);
+ SetScrollbars(m_xScrollPixelsPerLine, m_yScrollPixelsPerLine,
+ 0, 0, 0, 0, true);
SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight);
}
wxSize wxGrid::DoGetBestSize() const
{
- wxGrid *self = (wxGrid *)this; // const_cast
+ wxGrid * const self = const_cast<wxGrid *>(this);
// we do the same as in AutoSize() here with the exception that we don't
// change the column/row sizes, only calculate them