+ case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
+ {
+ int numCols = msg.GetCommandInt();
+ int oldNumCols = m_numCols;
+ m_numCols += numCols;
+ if ( !m_colWidths.IsEmpty() )
+ {
+ m_colWidths.Add( m_defaultColWidth, numCols );
+ m_colRights.Add( 0, numCols );
+
+ int right = 0;
+ if ( oldNumCols > 0 ) right = m_colRights[oldNumCols-1];
+
+ for ( i = oldNumCols; i < m_numCols; i++ )
+ {
+ right += m_colWidths[i];
+ m_colRights[i] = right;
+ }
+ }
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ {
+ // if we have just inserted cols into an empty grid the current
+ // cell will be undefined...
+ //
+ SetCurrentCell( 0, 0 );
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_colLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+
+ case wxGRIDTABLE_NOTIFY_COLS_DELETED:
+ {
+ size_t pos = msg.GetCommandInt();
+ int numCols = msg.GetCommandInt2();
+ m_numCols -= numCols;
+
+ if ( !m_colWidths.IsEmpty() )
+ {
+ m_colWidths.RemoveAt( pos, numCols );
+ m_colRights.RemoveAt( pos, numCols );
+
+ int w = 0;
+ for ( i = 0; i < m_numCols; i++ )
+ {
+ w += m_colWidths[i];
+ m_colRights[i] = w;
+ }
+ }
+ if ( !m_numCols )
+ {
+ m_currentCellCoords = wxGridNoCellCoords;
+ }
+ else
+ {
+ if ( m_currentCellCoords.GetCol() >= m_numCols )
+ m_currentCellCoords.Set( 0, 0 );
+ }
+
+ if ( m_selection )
+ m_selection->UpdateCols( pos, -((int)numCols) );
+ wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
+ if (attrProvider) {
+ attrProvider->UpdateAttrCols( pos, -((int)numCols) );
+// ifdef'd out following patch from Paul Gammans
+#if 0
+ // No need to touch row attributes, unless we
+ // removed _all_ columns, in this case, we remove
+ // all row attributes.
+ // I hate to do this here, but the
+ // needed data is not available inside UpdateAttrCols.
+ if ( !GetNumberCols() )
+ attrProvider->UpdateAttrRows( 0, -GetNumberRows() );
+#endif
+ }
+ if ( !GetBatchCount() )
+ {
+ CalcDimensions();
+ m_colLabelWin->Refresh();
+ }
+ }
+ result = true;
+ break;
+ }
+
+ if (result && !GetBatchCount() )
+ m_gridWin->Refresh();
+ return result;
+}
+
+
+wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxArrayInt rowlabels;
+
+ int top, bottom;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetTop() > ch ) r.SetTop( 0 );
+ r.SetBottom( wxMin( r.GetBottom(), ch ) );
+#endif
+
+ // logical bounds of update region
+ //
+ int dummy;
+ CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
+ CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
+
+ // find the row labels within these bounds
+ //
+ int row;
+ for ( row = internalYToRow(top); row < m_numRows; row++ )
+ {
+ if ( GetRowBottom(row) < top )
+ continue;
+
+ if ( GetRowTop(row) > bottom )
+ break;
+
+ rowlabels.Add( row );
+ }
+
+ ++iter;
+ }
+
+ return rowlabels;
+}
+
+
+wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxArrayInt colLabels;
+
+ int left, right;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetLeft() > cw ) r.SetLeft( 0 );
+ r.SetRight( wxMin( r.GetRight(), cw ) );
+#endif
+
+ // logical bounds of update region
+ //
+ int dummy;
+ CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
+ CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
+
+ // find the cells within these bounds
+ //
+ int col;
+ for ( col = internalXToCol(left); col < m_numCols; col++ )
+ {
+ if ( GetColRight(col) < left )
+ continue;
+
+ if ( GetColLeft(col) > right )
+ break;
+
+ colLabels.Add( col );
+ }
+
+ ++iter;
+ }
+ return colLabels;
+}
+
+
+wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg )
+{
+ wxRegionIterator iter( reg );
+ wxRect r;
+
+ wxGridCellCoordsArray cellsExposed;
+
+ int left, top, right, bottom;
+ while ( iter )
+ {
+ r = iter.GetRect();
+
+ // TODO: remove this when we can...
+ // There is a bug in wxMotif that gives garbage update
+ // rectangles if you jump-scroll a long way by clicking the
+ // scrollbar with middle button. This is a work-around
+ //
+#if defined(__WXMOTIF__)
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+ if ( r.GetTop() > ch ) r.SetTop( 0 );
+ if ( r.GetLeft() > cw ) r.SetLeft( 0 );
+ r.SetRight( wxMin( r.GetRight(), cw ) );
+ r.SetBottom( wxMin( r.GetBottom(), ch ) );
+#endif
+
+ // logical bounds of update region
+ //
+ CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
+ CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
+
+ // find the cells within these bounds
+ //
+ int row, col;
+ for ( row = internalYToRow(top); row < m_numRows; row++ )
+ {
+ if ( GetRowBottom(row) <= top )
+ continue;
+
+ if ( GetRowTop(row) > bottom )
+ break;
+
+ for ( col = internalXToCol(left); col < m_numCols; col++ )
+ {
+ if ( GetColRight(col) <= left )
+ continue;
+
+ if ( GetColLeft(col) > right )
+ break;
+
+ cellsExposed.Add( wxGridCellCoords( row, col ) );
+ }
+ }
+
+ ++iter;
+ }
+
+ return cellsExposed;
+}
+
+
+void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
+{
+ int x, y, row;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ if ( event.Dragging() )
+ {
+ if (!m_isDragging)
+ {
+ m_isDragging = true;
+ m_rowLabelWin->CaptureMouse();
+ }
+
+ if ( event.LeftIsDown() )
+ {
+ switch( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_ROW:
+ {
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ y = wxMax( y,
+ GetRowTop(m_dragRowOrCol) +
+ GetRowMinimalHeight(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ }
+ dc.DrawLine( left, y, left+cw, y );
+ m_dragLastPos = y;
+ }
+ break;
+
+ case WXGRID_CURSOR_SELECT_ROW:
+ {
+ if ( (row = YToRow( y )) >= 0 )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectRow( row,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ }
+ break;
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ if ( m_isDragging && (event.Entering() || event.Leaving()) )
+ return;
+
+ if (m_isDragging)
+ {
+ if (m_rowLabelWin->HasCapture()) m_rowLabelWin->ReleaseMouse();
+ m_isDragging = false;
+ }
+
+ // ------------ Entering or leaving the window
+ //
+ if ( event.Entering() || event.Leaving() )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
+ }
+
+
+ // ------------ Left button pressed
+ //
+ else if ( event.LeftDown() )
+ {
+ // don't send a label click event for a hit on the
+ // edge of the row label - this is probably the user
+ // wanting to resize the row
+ //
+ if ( YToEdgeOfRow(y) < 0 )
+ {
+ row = YToRow(y);
+ if ( row >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
+ {
+ if ( !event.ShiftDown() && !event.ControlDown() )
+ ClearSelection();
+ if ( m_selection )
+ {
+ if ( event.ShiftDown() )
+ {
+ m_selection->SelectBlock( m_currentCellCoords.GetRow(),
+ 0,
+ row,
+ GetNumberCols() - 1,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ else
+ {
+ m_selection->SelectRow( row,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
+ }
+ }
+ else
+ {
+ // starting to drag-resize a row
+ //
+ if ( CanDragRowSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ else if (event.LeftDClick() )
+ {
+ int row = YToEdgeOfRow(y);
+ if ( row < 0 )
+ {
+ row = YToRow(y);
+ if ( row >=0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+ else
+ {
+ // adjust row height depending on label text
+ AutoSizeRowLabelSize( row );
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ DoEndDragResizeRow();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() )
+ {
+ row = YToRow(y);
+ if ( row >=0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() )
+ {
+ row = YToRow(y);
+ if ( row >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ No buttons down and mouse moving
+ //
+ else if ( event.Moving() )
+ {
+ m_dragRowOrCol = YToEdgeOfRow( y );
+ if ( m_dragRowOrCol >= 0 )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // don't capture the mouse yet
+ if ( CanDragRowSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false);
+ }
+ }
+ else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false);
+ }
+ }
+}
+
+
+void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
+{
+ int x, y, col;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ if ( event.Dragging() )
+ {
+ if (!m_isDragging)
+ {
+ m_isDragging = true;
+ m_colLabelWin->CaptureMouse();
+ }
+
+ if ( event.LeftIsDown() )
+ {
+ switch( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_COL:
+ {
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+
+ x = wxMax( x, GetColLeft(m_dragRowOrCol) +
+ GetColMinimalWidth(m_dragRowOrCol));
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ }
+ dc.DrawLine( x, top, x, top+ch );
+ m_dragLastPos = x;
+ }
+ break;
+
+ case WXGRID_CURSOR_SELECT_COL:
+ {
+ if ( (col = XToCol( x )) >= 0 )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectCol( col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ }
+ break;
+
+ // default label to suppress warnings about "enumeration value
+ // 'xxx' not handled in switch
+ default:
+ break;
+ }
+ }
+ return;
+ }
+
+ if ( m_isDragging && (event.Entering() || event.Leaving()) )
+ return;
+
+ if (m_isDragging)
+ {
+ if (m_colLabelWin->HasCapture()) m_colLabelWin->ReleaseMouse();
+ m_isDragging = false;
+ }
+
+ // ------------ Entering or leaving the window
+ //
+ if ( event.Entering() || event.Leaving() )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ }
+
+
+ // ------------ Left button pressed
+ //
+ else if ( event.LeftDown() )
+ {
+ // don't send a label click event for a hit on the
+ // edge of the col label - this is probably the user
+ // wanting to resize the col
+ //
+ if ( XToEdgeOfCol(x) < 0 )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
+ {
+ if ( !event.ShiftDown() && !event.ControlDown() )
+ ClearSelection();
+ if ( m_selection )
+ {
+ if ( event.ShiftDown() )
+ {
+ m_selection->SelectBlock( 0,
+ m_currentCellCoords.GetCol(),
+ GetNumberRows() - 1, col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ else
+ {
+ m_selection->SelectCol( col,
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, m_colLabelWin);
+ }
+ }
+ else
+ {
+ // starting to drag-resize a col
+ //
+ if ( CanDragColSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin);
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ if ( event.LeftDClick() )
+ {
+ int col = XToEdgeOfCol(x);
+ if ( col < 0 )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+ else
+ {
+ // adjust column width depending on label text
+ AutoSizeColLabelSize( col );
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ DoEndDragResizeCol();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
+ }
+
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin);
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() )
+ {
+ col = XToCol(x);
+ if ( col >= 0 &&
+ !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ No buttons down and mouse moving
+ //
+ else if ( event.Moving() )
+ {
+ m_dragRowOrCol = XToEdgeOfCol( x );
+ if ( m_dragRowOrCol >= 0 )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // don't capture the cursor yet
+ if ( CanDragColSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, m_colLabelWin, false);
+ }
+ }
+ else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_colLabelWin, false);
+ }
+ }
+}
+
+
+void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
+{
+ if ( event.LeftDown() )
+ {
+ // indicate corner label by having both row and
+ // col args == -1
+ //
+ if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
+ {
+ SelectAll();
+ }
+ }
+
+ else if ( event.LeftDClick() )
+ {
+ SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
+ }
+
+ else if ( event.RightDown() )
+ {
+ if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+ else if ( event.RightDClick() )
+ {
+ if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
+ {
+ // no default action at the moment
+ }
+ }
+}
+
+void wxGrid::ChangeCursorMode(CursorMode mode,
+ wxWindow *win,
+ bool captureMouse)
+{
+#ifdef __WXDEBUG__
+ static const wxChar *cursorModes[] =
+ {
+ _T("SELECT_CELL"),
+ _T("RESIZE_ROW"),
+ _T("RESIZE_COL"),
+ _T("SELECT_ROW"),
+ _T("SELECT_COL")
+ };
+
+ wxLogTrace(_T("grid"),
+ _T("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
+ win == m_colLabelWin ? _T("colLabelWin")
+ : win ? _T("rowLabelWin")
+ : _T("gridWin"),
+ cursorModes[m_cursorMode], cursorModes[mode]);
+#endif // __WXDEBUG__
+
+ if ( mode == m_cursorMode &&
+ win == m_winCapture &&
+ captureMouse == (m_winCapture != NULL))
+ return;
+
+ if ( !win )
+ {
+ // by default use the grid itself
+ win = m_gridWin;
+ }
+
+ if ( m_winCapture )
+ {
+ if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
+ m_winCapture = (wxWindow *)NULL;
+ }
+
+ m_cursorMode = mode;
+
+ switch ( m_cursorMode )
+ {
+ case WXGRID_CURSOR_RESIZE_ROW:
+ win->SetCursor( m_rowResizeCursor );
+ break;
+
+ case WXGRID_CURSOR_RESIZE_COL:
+ win->SetCursor( m_colResizeCursor );
+ break;
+
+ default:
+ win->SetCursor( *wxSTANDARD_CURSOR );
+ }
+
+ // we need to capture mouse when resizing
+ bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
+ m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
+
+ if ( captureMouse && resize )
+ {
+ win->CaptureMouse();
+ m_winCapture = win;
+ }
+}
+
+void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
+{
+ int x, y;
+ wxPoint pos( event.GetPosition() );
+ CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
+
+ wxGridCellCoords coords;
+ XYToCell( x, y, coords );
+
+ int cell_rows, cell_cols;
+ bool isFirstDrag = !m_isDragging;
+ GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols );
+ if ((cell_rows < 0) || (cell_cols < 0))
+ {
+ coords.SetRow(coords.GetRow() + cell_rows);
+ coords.SetCol(coords.GetCol() + cell_cols);
+ }
+
+ if ( event.Dragging() )
+ {
+ //wxLogDebug("pos(%d, %d) coords(%d, %d)", pos.x, pos.y, coords.GetRow(), coords.GetCol());
+
+ // Don't start doing anything until the mouse has been drug at
+ // least 3 pixels in any direction...
+ if (! m_isDragging)
+ {
+ if (m_startDragPos == wxDefaultPosition)
+ {
+ m_startDragPos = pos;
+ return;
+ }
+ if (abs(m_startDragPos.x - pos.x) < 4 && abs(m_startDragPos.y - pos.y) < 4)
+ return;
+ }
+
+ m_isDragging = true;
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ // Hide the edit control, so it
+ // won't interfer with drag-shrinking.
+ if ( IsCellEditControlShown() )
+ {
+ HideCellEditControl();
+ SaveEditControlValue();
+ }
+
+ // Have we captured the mouse yet?
+ if (! m_winCapture)
+ {
+ m_winCapture = m_gridWin;
+ m_winCapture->CaptureMouse();
+ }
+
+ if ( coords != wxGridNoCellCoords )
+ {
+ if ( event.ControlDown() )
+ {
+ if ( m_selectingKeyboard == wxGridNoCellCoords)
+ m_selectingKeyboard = coords;
+ HighlightBlock ( m_selectingKeyboard, coords );
+ }
+ else if ( CanDragCell() )
+ {
+ if ( isFirstDrag )
+ {
+ if ( m_selectingKeyboard == wxGridNoCellCoords)
+ m_selectingKeyboard = coords;
+
+ SendEvent( wxEVT_GRID_CELL_BEGIN_DRAG,
+ coords.GetRow(),
+ coords.GetCol(),
+ event );
+ }
+ }
+ else
+ {
+ if ( !IsSelection() )
+ {
+ HighlightBlock( coords, coords );
+ }
+ else
+ {
+ HighlightBlock( m_currentCellCoords, coords );
+ }
+ }
+
+ if (! IsVisible(coords))
+ {
+ MakeCellVisible(coords);
+ // TODO: need to introduce a delay or something here. The
+ // scrolling is way to fast, at least on MSW - also on GTK.
+ }
+ }
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ y = wxMax( y, GetRowTop(m_dragRowOrCol) +
+ GetRowMinimalHeight(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ }
+ dc.DrawLine( left, y, left+cw, y );
+ m_dragLastPos = y;
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ x = wxMax( x, GetColLeft(m_dragRowOrCol) +
+ GetColMinimalWidth(m_dragRowOrCol) );
+ dc.SetLogicalFunction(wxINVERT);
+ if ( m_dragLastPos >= 0 )
+ {
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ }
+ dc.DrawLine( x, top, x, top+ch );
+ m_dragLastPos = x;
+ }
+
+ return;
+ }
+
+ 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 );
+ }
+ else
+#endif // 0
+
+ // ------------ Left button pressed
+ //
+ if ( event.LeftDown() && coords != wxGridNoCellCoords )
+ {
+ if ( !SendEvent( wxEVT_GRID_CELL_LEFT_CLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ if ( !event.ControlDown() )
+ ClearSelection();
+ if ( event.ShiftDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_currentCellCoords.GetRow(),
+ m_currentCellCoords.GetCol(),
+ coords.GetRow(),
+ coords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+ else if ( XToEdgeOfCol(x) < 0 &&
+ YToEdgeOfRow(y) < 0 )
+ {
+ DisableCellEditControl();
+ MakeCellVisible( coords );
+
+ if ( event.ControlDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->ToggleCellSelection( coords.GetRow(),
+ coords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+ m_selectingKeyboard = coords;
+ }
+ else
+ {
+ m_waitForSlowClick = m_currentCellCoords == coords && coords != wxGridNoCellCoords;
+ SetCurrentCell( coords );
+ if ( m_selection )
+ {
+ if ( m_selection->GetSelectionMode() !=
+ wxGrid::wxGridSelectCells )
+ {
+ HighlightBlock( coords, coords );
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // ------------ Left double click
+ //
+ else if ( event.LeftDClick() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+
+ if ( XToEdgeOfCol(x) < 0 && YToEdgeOfRow(y) < 0 )
+ {
+ if ( !SendEvent( wxEVT_GRID_CELL_LEFT_DCLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // we want double click to select a cell and start editing
+ // (i.e. to behave in same way as sequence of two slow clicks):
+ m_waitForSlowClick = true;
+ }
+ }
+
+ }
+
+
+ // ------------ Left button released
+ //
+ else if ( event.LeftUp() )
+ {
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if (m_winCapture)
+ {
+ if (m_winCapture->HasCapture()) m_winCapture->ReleaseMouse();
+ m_winCapture = NULL;
+ }
+
+ if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl())
+ {
+ ClearSelection();
+ EnableCellEditControl();
+
+ wxGridCellAttr* attr = GetCellAttr(coords);
+ wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol());
+ editor->StartingClick();
+ editor->DecRef();
+ attr->DecRef();
+
+ m_waitForSlowClick = false;
+ }
+ else if ( m_selectingTopLeft != wxGridNoCellCoords &&
+ m_selectingBottomRight != wxGridNoCellCoords )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
+ m_selectingTopLeft.GetCol(),
+ m_selectingBottomRight.GetRow(),
+ m_selectingBottomRight.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+
+ // Show the edit control, if it has been hidden for
+ // drag-shrinking.
+ ShowCellEditControl();
+ }
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ DoEndDragResizeRow();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event );
+ }
+ else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ DoEndDragResizeCol();
+
+ // Note: we are ending the event *after* doing
+ // default processing in this case
+ //
+ SendEvent( wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event );
+ }
+
+ m_dragLastPos = -1;
+ }
+
+
+ // ------------ Right button down
+ //
+ else if ( event.RightDown() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+ if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_CLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+
+ // ------------ Right double click
+ //
+ else if ( event.RightDClick() && coords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+ if ( !SendEvent( wxEVT_GRID_CELL_RIGHT_DCLICK,
+ coords.GetRow(),
+ coords.GetCol(),
+ event ) )
+ {
+ // no default action at the moment
+ }
+ }
+
+ // ------------ Moving and no button action
+ //
+ else if ( event.Moving() && !event.IsButton() )
+ {
+ if( coords.GetRow() < 0 || coords.GetCol() < 0 )
+ {
+ // out of grid cell area
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ return;
+ }
+
+ int dragRow = YToEdgeOfRow( y );
+ int dragCol = XToEdgeOfCol( x );
+
+ // Dragging on the corner of a cell to resize in both
+ // directions is not implemented yet...
+ //
+ if ( dragRow >= 0 && dragCol >= 0 )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ return;
+ }
+
+ if ( dragRow >= 0 )
+ {
+ m_dragRowOrCol = dragRow;
+
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if ( CanDragRowSize() && CanDragGridSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW);
+ }
+
+ if ( dragCol >= 0 )
+ {
+ m_dragRowOrCol = dragCol;
+ }
+
+ return;
+ }
+
+ if ( dragCol >= 0 )
+ {
+ m_dragRowOrCol = dragCol;
+
+ if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
+ {
+ if ( CanDragColSize() && CanDragGridSize() )
+ ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL);
+ }
+
+ return;
+ }
+
+ // Neither on a row or col edge
+ //
+ if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
+ {
+ ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
+ }
+ }
+}
+
+
+void wxGrid::DoEndDragResizeRow()
+{
+ if ( m_dragLastPos >= 0 )
+ {
+ // erase the last line and resize the row
+ //
+ int cw, ch, left, dummy;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &left, &dummy );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ dc.SetLogicalFunction( wxINVERT );
+ dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ int rowTop = GetRowTop(m_dragRowOrCol);
+ SetRowSize( m_dragRowOrCol,
+ wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) );
+
+ if ( !GetBatchCount() )
+ {
+ // Only needed to get the correct rect.y:
+ wxRect rect ( CellToRect( m_dragRowOrCol, 0 ) );
+ rect.x = 0;
+ CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
+ rect.width = m_rowLabelWidth;
+ rect.height = ch - rect.y;
+ m_rowLabelWin->Refresh( true, &rect );
+ rect.width = cw;
+ // if there is a multicell block, paint all of it
+ if (m_table)
+ {
+ int i, cell_rows, cell_cols, subtract_rows = 0;
+ int leftCol = XToCol(left);
+ int rightCol = internalXToCol(left+cw);
+ if (leftCol >= 0)
+ {
+ for (i=leftCol; i<rightCol; i++)
+ {
+ GetCellSize(m_dragRowOrCol, i, &cell_rows, &cell_cols);
+ if (cell_rows < subtract_rows)
+ subtract_rows = cell_rows;
+ }
+ rect.y = GetRowTop(m_dragRowOrCol + subtract_rows);
+ CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
+ rect.height = ch - rect.y;
+ }
+ }
+ m_gridWin->Refresh( false, &rect );
+ }
+
+ ShowCellEditControl();
+ }
+}
+
+
+void wxGrid::DoEndDragResizeCol()
+{
+ if ( m_dragLastPos >= 0 )
+ {
+ // erase the last line and resize the col
+ //
+ int cw, ch, dummy, top;
+ m_gridWin->GetClientSize( &cw, &ch );
+ CalcUnscrolledPosition( 0, 0, &dummy, &top );
+
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ dc.SetLogicalFunction( wxINVERT );
+ dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top+ch );
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ int colLeft = GetColLeft(m_dragRowOrCol);
+ SetColSize( m_dragRowOrCol,
+ wxMax( m_dragLastPos - colLeft,
+ GetColMinimalWidth(m_dragRowOrCol) ) );
+
+ if ( !GetBatchCount() )
+ {
+ // Only needed to get the correct rect.x:
+ wxRect rect ( CellToRect( 0, m_dragRowOrCol ) );
+ rect.y = 0;
+ CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
+ rect.width = cw - rect.x;
+ rect.height = m_colLabelHeight;
+ m_colLabelWin->Refresh( true, &rect );
+ rect.height = ch;
+ // if there is a multicell block, paint all of it
+ if (m_table)
+ {
+ int i, cell_rows, cell_cols, subtract_cols = 0;
+ int topRow = YToRow(top);
+ int bottomRow = internalYToRow(top+cw);
+ if (topRow >= 0)
+ {
+ for (i=topRow; i<bottomRow; i++)
+ {
+ GetCellSize(i, m_dragRowOrCol, &cell_rows, &cell_cols);
+ if (cell_cols < subtract_cols)
+ subtract_cols = cell_cols;
+ }
+ rect.x = GetColLeft(m_dragRowOrCol + subtract_cols);
+ CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
+ rect.width = cw - rect.x;
+ }
+ }
+ m_gridWin->Refresh( false, &rect );
+ }
+
+ ShowCellEditControl();
+ }
+}
+
+
+
+//
+// ------ interaction with data model
+//
+bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
+{
+ switch ( msg.GetId() )
+ {
+ case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
+ return GetModelValues();
+
+ case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
+ return SetModelValues();
+
+ case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
+ case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
+ case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
+ case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
+ case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
+ case wxGRIDTABLE_NOTIFY_COLS_DELETED:
+ return Redimension( msg );
+
+ default:
+ return false;
+ }
+}
+
+
+
+// The behaviour of this function depends on the grid table class
+// Clear() function. For the default wxGridStringTable class the
+// behavious is to replace all cell contents with wxEmptyString but
+// not to change the number of rows or cols.
+//
+void wxGrid::ClearGrid()
+{
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ m_table->Clear();
+ if ( !GetBatchCount() ) m_gridWin->Refresh();
+ }
+}
+
+
+bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::InsertRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->InsertRows( pos, numRows );
+ return done;
+
+ // the table will have sent the results of the insert row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ bool done = m_table && m_table->AppendRows( numRows );
+ return done;
+ // the table will have sent the results of the append row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->DeleteRows( pos, numRows );
+ return done;
+ // the table will have sent the results of the delete row
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->InsertCols( pos, numCols );
+ return done;
+ // the table will have sent the results of the insert col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::AppendCols( int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::AppendCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ bool done = m_table->AppendCols( numCols );
+ return done;
+ // the table will have sent the results of the append col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+bool wxGrid::DeleteCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
+{
+ // TODO: something with updateLabels flag
+
+ if ( !m_created )
+ {
+ wxFAIL_MSG( wxT("Called wxGrid::DeleteCols() before calling CreateGrid()") );
+ return false;
+ }
+
+ if ( m_table )
+ {
+ if (IsCellEditControlEnabled())
+ DisableCellEditControl();
+
+ bool done = m_table->DeleteCols( pos, numCols );
+ return done;
+ // the table will have sent the results of the delete col
+ // operation to this view object as a grid table message
+ }
+ return false;
+}
+
+
+
+//
+// ----- event handlers
+//
+
+// Generate a grid event based on a mouse event and
+// return the result of ProcessEvent()
+//
+int wxGrid::SendEvent( const wxEventType type,
+ int row, int col,
+ wxMouseEvent& mouseEv )
+{
+ bool claimed;
+ bool vetoed;
+
+ if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
+ {
+ int rowOrCol = (row == -1 ? col : row);
+
+ wxGridSizeEvent gridEvt( GetId(),
+ type,
+ this,
+ rowOrCol,
+ mouseEv.GetX() + GetRowLabelSize(),
+ mouseEv.GetY() + GetColLabelSize(),
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else if ( type == wxEVT_GRID_RANGE_SELECT )
+ {
+ // Right now, it should _never_ end up here!
+ wxGridRangeSelectEvent gridEvt( GetId(),
+ type,
+ this,
+ m_selectingTopLeft,
+ m_selectingBottomRight,
+ true,
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else
+ {
+ wxGridEvent gridEvt( GetId(),
+ type,
+ this,
+ row, col,
+ mouseEv.GetX() + GetRowLabelSize(),
+ mouseEv.GetY() + GetColLabelSize(),
+ false,
+ mouseEv.ControlDown(),
+ mouseEv.ShiftDown(),
+ mouseEv.AltDown(),
+ mouseEv.MetaDown() );
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+
+ // A Veto'd event may not be `claimed' so test this first
+ if (vetoed) return -1;
+ return claimed ? 1 : 0;
+}
+
+
+// Generate a grid event of specified type and return the result
+// of ProcessEvent().
+//
+int wxGrid::SendEvent( const wxEventType type,
+ int row, int col )
+{
+ bool claimed;
+ bool vetoed;
+
+ if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
+ {
+ int rowOrCol = (row == -1 ? col : row);
+
+ wxGridSizeEvent gridEvt( GetId(),
+ type,
+ this,
+ rowOrCol );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+ else
+ {
+ wxGridEvent gridEvt( GetId(),
+ type,
+ this,
+ row, col );
+
+ claimed = GetEventHandler()->ProcessEvent(gridEvt);
+ vetoed = !gridEvt.IsAllowed();
+ }
+
+ // A Veto'd event may not be `claimed' so test this first
+ if (vetoed) return -1;
+ return claimed ? 1 : 0;
+}
+
+
+void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
+{
+ wxPaintDC dc(this); // needed to prevent zillions of paint events on MSW
+}
+
+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())
+ {
+ // Refresh to get correct scrolled position:
+ wxScrolledWindow::Refresh(eraseb,rect);
+
+ if (rect)
+ {
+ int rect_x, rect_y, rectWidth, rectHeight;
+ int width_label, width_cell, height_label, height_cell;
+ int x, y;
+
+ //Copy rectangle can get scroll offsets..
+ rect_x = rect->GetX();
+ rect_y = rect->GetY();
+ rectWidth = rect->GetWidth();
+ rectHeight = rect->GetHeight();
+
+ width_label = m_rowLabelWidth - rect_x;
+ if (width_label > rectWidth) width_label = rectWidth;
+
+ height_label = m_colLabelHeight - rect_y;
+ if (height_label > rectHeight) height_label = rectHeight;
+
+ if (rect_x > m_rowLabelWidth)
+ {
+ x = rect_x - m_rowLabelWidth;
+ width_cell = rectWidth;
+ }
+ else
+ {
+ x = 0;
+ width_cell = rectWidth - (m_rowLabelWidth - rect_x);
+ }
+
+ if (rect_y > m_colLabelHeight)
+ {
+ y = rect_y - m_colLabelHeight;
+ height_cell = rectHeight;
+ }
+ else
+ {
+ y = 0;
+ height_cell = rectHeight - (m_colLabelHeight - rect_y);
+ }
+
+ // Paint corner label part intersecting rect.
+ if ( width_label > 0 && height_label > 0 )
+ {
+ wxRect anotherrect(rect_x, rect_y, width_label, height_label);
+ m_cornerLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint col labels part intersecting rect.
+ if ( width_cell > 0 && height_label > 0 )
+ {
+ wxRect anotherrect(x, rect_y, width_cell, height_label);
+ m_colLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint row labels part intersecting rect.
+ if ( width_label > 0 && height_cell > 0 )
+ {
+ wxRect anotherrect(rect_x, y, width_label, height_cell);
+ m_rowLabelWin->Refresh(eraseb, &anotherrect);
+ }
+
+ // Paint cell area part intersecting rect.
+ if ( width_cell > 0 && height_cell > 0 )
+ {
+ wxRect anotherrect(x, y, width_cell, height_cell);
+ m_gridWin->Refresh(eraseb, &anotherrect);
+ }
+ }
+ else
+ {
+ m_cornerLabelWin->Refresh(eraseb, NULL);
+ m_colLabelWin->Refresh(eraseb, NULL);
+ m_rowLabelWin->Refresh(eraseb, NULL);
+ m_gridWin->Refresh(eraseb, NULL);
+ }
+ }
+}
+
+void wxGrid::OnSize( wxSizeEvent& event )
+{
+ // position the child windows
+ CalcWindowSizes();
+
+ // don't call CalcDimensions() from here, the base class handles the size
+ // changes itself
+ event.Skip();
+}
+
+
+void wxGrid::OnKeyDown( wxKeyEvent& event )
+{
+ if ( m_inOnKeyDown )
+ {
+ // shouldn't be here - we are going round in circles...
+ //
+ wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
+ }
+
+ m_inOnKeyDown = true;
+
+ // propagate the event up and see if it gets processed
+ //
+ wxWindow *parent = GetParent();
+ wxKeyEvent keyEvt( event );
+ keyEvt.SetEventObject( parent );
+
+ if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
+ {
+
+ // try local handlers
+ //
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_UP:
+ if ( event.ControlDown() )
+ {
+ MoveCursorUpBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorUp( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_DOWN:
+ if ( event.ControlDown() )
+ {
+ MoveCursorDownBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorDown( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_LEFT:
+ if ( event.ControlDown() )
+ {
+ MoveCursorLeftBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorLeft( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_RIGHT:
+ if ( event.ControlDown() )
+ {
+ MoveCursorRightBlock( event.ShiftDown() );
+ }
+ else
+ {
+ MoveCursorRight( event.ShiftDown() );
+ }
+ break;
+
+ case WXK_RETURN:
+ case WXK_NUMPAD_ENTER:
+ if ( event.ControlDown() )
+ {
+ event.Skip(); // to let the edit control have the return
+ }
+ else
+ {
+ if ( GetGridCursorRow() < GetNumberRows()-1 )
+ {
+ MoveCursorDown( event.ShiftDown() );
+ }
+ else
+ {
+ // at the bottom of a column
+ DisableCellEditControl();
+ }
+ }
+ break;
+
+ case WXK_ESCAPE:
+ ClearSelection();
+ break;
+
+ case WXK_TAB:
+ if (event.ShiftDown())
+ {
+ if ( GetGridCursorCol() > 0 )
+ {
+ MoveCursorLeft( false );
+ }
+ else
+ {
+ // at left of grid
+ DisableCellEditControl();
+ }
+ }
+ else
+ {
+ if ( GetGridCursorCol() < GetNumberCols()-1 )
+ {
+ MoveCursorRight( false );
+ }
+ else
+ {
+ // at right of grid
+ DisableCellEditControl();
+ }
+ }
+ break;
+
+ case WXK_HOME:
+ if ( event.ControlDown() )
+ {
+ MakeCellVisible( 0, 0 );
+ SetCurrentCell( 0, 0 );
+ }
+ else
+ {
+ event.Skip();
+ }
+ break;
+
+ case WXK_END:
+ if ( event.ControlDown() )
+ {
+ MakeCellVisible( m_numRows-1, m_numCols-1 );
+ SetCurrentCell( m_numRows-1, m_numCols-1 );
+ }
+ else
+ {
+ event.Skip();
+ }
+ break;
+
+ case WXK_PRIOR:
+ MovePageUp();
+ break;
+
+ case WXK_NEXT:
+ MovePageDown();
+ break;
+
+ case WXK_SPACE:
+ if ( event.ControlDown() )
+ {
+ if ( m_selection )
+ {
+ m_selection->ToggleCellSelection( m_currentCellCoords.GetRow(),
+ m_currentCellCoords.GetCol(),
+ event.ControlDown(),
+ event.ShiftDown(),
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ break;
+ }
+ if ( !IsEditable() )
+ {
+ MoveCursorRight( false );
+ break;
+ }
+ // Otherwise fall through to default
+
+ default:
+ event.Skip();
+ break;
+ }
+ }
+
+ m_inOnKeyDown = false;
+}
+
+void wxGrid::OnKeyUp( wxKeyEvent& event )
+{
+ // try local handlers
+ //
+ if ( event.GetKeyCode() == WXK_SHIFT )
+ {
+ if ( m_selectingTopLeft != wxGridNoCellCoords &&
+ m_selectingBottomRight != wxGridNoCellCoords )
+ {
+ if ( m_selection )
+ {
+ m_selection->SelectBlock( m_selectingTopLeft.GetRow(),
+ m_selectingTopLeft.GetCol(),
+ m_selectingBottomRight.GetRow(),
+ m_selectingBottomRight.GetCol(),
+ event.ControlDown(),
+ true,
+ event.AltDown(),
+ event.MetaDown() );
+ }
+ }
+
+ m_selectingTopLeft = wxGridNoCellCoords;
+ m_selectingBottomRight = wxGridNoCellCoords;
+ m_selectingKeyboard = wxGridNoCellCoords;
+ }
+}
+
+void wxGrid::OnChar( wxKeyEvent& event )
+{
+ // is it possible to edit the current cell at all?
+ if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
+ {
+ // yes, now check whether the cells editor accepts the key
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor *editor = attr->GetEditor(this, row, col);
+
+ // <F2> is special and will always start editing, for
+ // other keys - ask the editor itself
+ if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
+ || editor->IsAcceptedKey(event) )
+ {
+ // ensure cell is visble
+ MakeCellVisible(row, col);
+ EnableCellEditControl();
+
+ // a problem can arise if the cell is not completely
+ // visible (even after calling MakeCellVisible the
+ // control is not created and calling StartingKey will
+ // crash the app
+ if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
+ editor->StartingKey(event);
+ }
+ else
+ {
+ event.Skip();
+ }
+
+ editor->DecRef();
+ attr->DecRef();
+ }
+ else
+ {
+ event.Skip();
+ }
+}
+
+
+void wxGrid::OnEraseBackground(wxEraseEvent&)
+{
+}
+
+void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
+{
+ if ( SendEvent( wxEVT_GRID_SELECT_CELL, coords.GetRow(), coords.GetCol() ) )
+ {
+ // the event has been intercepted - do nothing
+ return;
+ }
+
+ wxClientDC dc(m_gridWin);
+ PrepareDC(dc);
+
+ if ( m_currentCellCoords != wxGridNoCellCoords )
+ {
+ DisableCellEditControl();
+
+ if ( IsVisible( m_currentCellCoords, false ) )
+ {
+ wxRect r;
+ r = BlockToDeviceRect(m_currentCellCoords, m_currentCellCoords);
+ if ( !m_gridLinesEnabled )
+ {
+ r.x--;
+ r.y--;
+ r.width++;
+ r.height++;
+ }
+
+ wxGridCellCoordsArray cells = CalcCellsExposed( r );
+
+ // Otherwise refresh redraws the highlight!
+ m_currentCellCoords = coords;
+
+ DrawGridCellArea(dc,cells);
+ DrawAllGridLines( dc, r );
+ }
+ }
+
+ m_currentCellCoords = coords;
+
+ wxGridCellAttr* attr = GetCellAttr(coords);
+ DrawCellHighlight(dc, attr);
+ attr->DecRef();
+}
+
+
+void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol )
+{
+ int temp;
+ wxGridCellCoords updateTopLeft, updateBottomRight;
+
+ if ( m_selection )
+ {
+ if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows )
+ {
+ leftCol = 0;
+ rightCol = GetNumberCols() - 1;
+ }
+ else if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns )
+ {
+ topRow = 0;
+ bottomRow = GetNumberRows() - 1;
+ }
+ }
+
+ if ( topRow > bottomRow )
+ {
+ temp = topRow;
+ topRow = bottomRow;
+ bottomRow = temp;
+ }
+
+ if ( leftCol > rightCol )
+ {
+ temp = leftCol;
+ leftCol = rightCol;
+ rightCol = temp;
+ }
+
+ updateTopLeft = wxGridCellCoords( topRow, leftCol );
+ updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
+
+ // First the case that we selected a completely new area
+ if ( m_selectingTopLeft == wxGridNoCellCoords ||
+ m_selectingBottomRight == wxGridNoCellCoords )
+ {
+ wxRect rect;
+ rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
+ wxGridCellCoords ( bottomRow, rightCol ) );
+ m_gridWin->Refresh( false, &rect );
+ }
+ // Now handle changing an existing selection area.
+ else if ( m_selectingTopLeft != updateTopLeft ||
+ m_selectingBottomRight != updateBottomRight )
+ {
+ // Compute two optimal update rectangles:
+ // Either one rectangle is a real subset of the
+ // other, or they are (almost) disjoint!
+ wxRect rect[4];
+ bool need_refresh[4];
+ need_refresh[0] =
+ need_refresh[1] =
+ need_refresh[2] =
+ need_refresh[3] = false;
+ int i;
+
+ // Store intermediate values
+ wxCoord oldLeft = m_selectingTopLeft.GetCol();
+ wxCoord oldTop = m_selectingTopLeft.GetRow();
+ wxCoord oldRight = m_selectingBottomRight.GetCol();
+ wxCoord oldBottom = m_selectingBottomRight.GetRow();
+
+ // Determine the outer/inner coordinates.
+ if (oldLeft > leftCol)
+ {
+ temp = oldLeft;
+ oldLeft = leftCol;
+ leftCol = temp;
+ }
+ if (oldTop > topRow )
+ {
+ temp = oldTop;
+ oldTop = topRow;
+ topRow = temp;
+ }
+ if (oldRight < rightCol )
+ {
+ temp = oldRight;
+ oldRight = rightCol;
+ rightCol = temp;
+ }
+ if (oldBottom < bottomRow)
+ {
+ temp = oldBottom;
+ oldBottom = bottomRow;
+ bottomRow = temp;
+ }
+
+ // Now, either the stuff marked old is the outer
+ // rectangle or we don't have a situation where one
+ // is contained in the other.
+
+ if ( oldLeft < leftCol )
+ {
+ // Refresh the newly selected or deselected
+ // area to the left of the old or new selection.
+ need_refresh[0] = true;
+ rect[0] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ oldLeft ),
+ wxGridCellCoords ( oldBottom,
+ leftCol - 1 ) );
+ }
+
+ if ( oldTop < topRow )
+ {
+ // Refresh the newly selected or deselected
+ // area above the old or new selection.
+ need_refresh[1] = true;
+ rect[1] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ leftCol ),
+ wxGridCellCoords ( topRow - 1,
+ rightCol ) );
+ }
+
+ if ( oldRight > rightCol )
+ {
+ // Refresh the newly selected or deselected
+ // area to the right of the old or new selection.
+ need_refresh[2] = true;
+ rect[2] = BlockToDeviceRect( wxGridCellCoords ( oldTop,
+ rightCol + 1 ),
+ wxGridCellCoords ( oldBottom,
+ oldRight ) );
+ }
+
+ if ( oldBottom > bottomRow )
+ {
+ // Refresh the newly selected or deselected
+ // area below the old or new selection.
+ need_refresh[3] = true;
+ rect[3] = BlockToDeviceRect( wxGridCellCoords ( bottomRow + 1,
+ leftCol ),
+ wxGridCellCoords ( oldBottom,
+ rightCol ) );
+ }
+
+ // various Refresh() calls
+ for (i = 0; i < 4; i++ )
+ if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
+ m_gridWin->Refresh( false, &(rect[i]) );
+ }
+ // Change Selection
+ m_selectingTopLeft = updateTopLeft;
+ m_selectingBottomRight = updateBottomRight;
+}
+
+//
+// ------ functions to get/send data (see also public functions)
+//
+
+bool wxGrid::GetModelValues()
+{
+ // Hide the editor, so it won't hide a changed value.
+ HideCellEditControl();
+
+ if ( m_table )
+ {
+ // all we need to do is repaint the grid
+ //
+ m_gridWin->Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+
+bool wxGrid::SetModelValues()
+{
+ int row, col;
+
+ // Disable the editor, so it won't hide a changed value.
+ // Do we also want to save the current value of the editor first?
+ // I think so ...
+ DisableCellEditControl();
+
+ if ( m_table )
+ {
+ for ( row = 0; row < m_numRows; row++ )
+ {
+ for ( col = 0; col < m_numCols; col++ )
+ {
+ m_table->SetValue( row, col, GetCellValue(row, col) );
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+
+// Note - this function only draws cells that are in the list of
+// exposed cells (usually set from the update region by
+// CalcExposedCells)
+//
+void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
+{
+ if ( !m_numRows || !m_numCols ) return;
+
+ int i, numCells = cells.GetCount();
+ int row, col, cell_rows, cell_cols;
+ wxGridCellCoordsArray redrawCells;
+
+ for ( i = numCells-1; i >= 0; i-- )
+ {
+ row = cells[i].GetRow();
+ col = cells[i].GetCol();
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+
+ // If this cell is part of a multicell block, find owner for repaint
+ if ( cell_rows <= 0 || cell_cols <= 0 )
+ {
+ wxGridCellCoords cell(row+cell_rows, col+cell_cols);
+ bool marked = false;
+ for ( int j = 0; j < numCells; j++ )
+ {
+ if ( cell == cells[j] )
+ {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked)
+ {
+ int count = redrawCells.GetCount();
+ for (int j = 0; j < count; j++)
+ {
+ if ( cell == redrawCells[j] )
+ {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked) redrawCells.Add( cell );
+ }
+ continue; // don't bother drawing this cell
+ }
+
+ // If this cell is empty, find cell to left that might want to overflow
+ if (m_table && m_table->IsEmptyCell(row, col))
+ {
+ for ( int l = 0; l < cell_rows; l++ )
+ {
+ // find a cell in this row to left alreay marked for repaint
+ int left = col;
+ for (int k = 0; k < int(redrawCells.GetCount()); k++)
+ if ((redrawCells[k].GetCol() < left) &&
+ (redrawCells[k].GetRow() == row))
+ left=redrawCells[k].GetCol();
+
+ if (left == col) left = 0; // oh well
+
+ for (int j = col-1; j >= left; j--)
+ {
+ if (!m_table->IsEmptyCell(row+l, j))
+ {
+ if (GetCellOverflow(row+l, j))
+ {
+ wxGridCellCoords cell(row+l, j);
+ bool marked = false;
+
+ for (int k = 0; k < numCells; k++)
+ {
+ if ( cell == cells[k] )
+ {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked)
+ {
+ int count = redrawCells.GetCount();
+ for (int k = 0; k < count; k++)
+ {
+ if ( cell == redrawCells[k] )
+ {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked) redrawCells.Add( cell );
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ DrawCell( dc, cells[i] );
+ }
+
+ numCells = redrawCells.GetCount();
+
+ for ( i = numCells - 1; i >= 0; i-- )
+ {
+ DrawCell( dc, redrawCells[i] );
+ }
+}
+
+
+void wxGrid::DrawGridSpace( wxDC& dc )
+{
+ int cw, ch;
+ m_gridWin->GetClientSize( &cw, &ch );
+
+ int right, bottom;
+ CalcUnscrolledPosition( cw, ch, &right, &bottom );
+
+ int rightCol = m_numCols > 0 ? GetColRight(m_numCols - 1) : 0;
+ int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0 ;
+
+ if ( right > rightCol || bottom > bottomRow )
+ {
+ int left, top;
+ CalcUnscrolledPosition( 0, 0, &left, &top );
+
+ dc.SetBrush( wxBrush(GetDefaultCellBackgroundColour(), wxSOLID) );
+ dc.SetPen( *wxTRANSPARENT_PEN );
+
+ if ( right > rightCol )
+ {
+ dc.DrawRectangle( rightCol, top, right - rightCol, ch);
+ }
+
+ if ( bottom > bottomRow )
+ {
+ dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow);
+ }
+ }
+}
+
+
+void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
+{
+ int row = coords.GetRow();
+ int col = coords.GetCol();
+
+ if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
+ return;
+
+ // we draw the cell border ourselves
+#if !WXGRID_DRAW_LINES
+ if ( m_gridLinesEnabled )
+ DrawCellBorder( dc, coords );
+#endif
+
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+
+ bool isCurrent = coords == m_currentCellCoords;
+
+ wxRect rect = CellToRect( row, col );
+
+ // if the editor is shown, we should use it and not the renderer
+ // Note: However, only if it is really _shown_, i.e. not hidden!
+ if ( isCurrent && IsCellEditControlShown() )
+ {
+ wxGridCellEditor *editor = attr->GetEditor(this, row, col);
+ editor->PaintBackground(rect, attr);
+ editor->DecRef();
+ }
+ else
+ {
+ // but all the rest is drawn by the cell renderer and hence may be
+ // customized
+ wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
+ renderer->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
+ renderer->DecRef();
+ }
+
+ attr->DecRef();
+}
+
+void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
+{
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
+ return;
+
+ wxRect rect = CellToRect(row, col);
+
+ // hmmm... what could we do here to show that the cell is disabled?
+ // for now, I just draw a thinner border than for the other ones, but
+ // it doesn't look really good
+
+ int penWidth = attr->IsReadOnly() ? m_cellHighlightROPenWidth : m_cellHighlightPenWidth;
+
+ if (penWidth > 0)
+ {
+ // The center of th drawn line is where the position/width/height of
+ // the rectangle is actually at, (on wxMSW atr least,) so we will
+ // reduce the size of the rectangle to compensate for the thickness of
+ // the line. If this is too strange on non wxMSW platforms then
+ // please #ifdef this appropriately.
+ rect.x += penWidth/2;
+ rect.y += penWidth/2;
+ rect.width -= penWidth-1;
+ rect.height -= penWidth-1;
+
+
+ // 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.SetBrush(*wxTRANSPARENT_BRUSH);
+ dc.DrawRectangle(rect);
+ }
+
+#if 0
+ // VZ: my experiments with 3d borders...
+
+ // how to properly set colours for arbitrary bg?
+ wxCoord x1 = rect.x,
+ y1 = rect.y,
+ x2 = rect.x + rect.width -1,
+ y2 = rect.y + rect.height -1;
+
+ dc.SetPen(*wxWHITE_PEN);
+ dc.DrawLine(x1, y1, x2, y1);
+ dc.DrawLine(x1, y1, x1, y2);
+
+ dc.DrawLine(x1 + 1, y2 - 1, x2 - 1, y2 - 1);
+ dc.DrawLine(x2 - 1, y1 + 1, x2 - 1, y2 );
+
+ dc.SetPen(*wxBLACK_PEN);
+ dc.DrawLine(x1, y2, x2, y2);
+ dc.DrawLine(x2, y1, x2, y2+1);
+#endif // 0
+}
+
+
+void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
+{
+ int row = coords.GetRow();
+ int col = coords.GetCol();
+ if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
+ return;
+
+ dc.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
+
+ wxRect rect = CellToRect( row, col );
+
+ // right hand border
+ //
+ dc.DrawLine( rect.x + rect.width, rect.y,
+ rect.x + rect.width, rect.y + rect.height + 1 );
+
+ // bottom border
+ //
+ dc.DrawLine( rect.x, rect.y + rect.height,
+ rect.x + rect.width, rect.y + rect.height);
+}
+
+void wxGrid::DrawHighlight(wxDC& dc,const wxGridCellCoordsArray& cells)
+{
+ // This if block was previously in wxGrid::OnPaint but that doesn't
+ // seem to get called under wxGTK - MB
+ //
+ if ( m_currentCellCoords == wxGridNoCellCoords &&
+ m_numRows && m_numCols )
+ {
+ m_currentCellCoords.Set(0, 0);
+ }
+
+ if ( IsCellEditControlShown() )
+ {
+ // don't show highlight when the edit control is shown
+ return;
+ }
+
+ // if the active cell was repainted, repaint its highlight too because it
+ // might have been damaged by the grid lines
+ size_t count = cells.GetCount();
+ for ( size_t n = 0; n < count; n++ )
+ {
+ if ( cells[n] == m_currentCellCoords )
+ {
+ wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
+ DrawCellHighlight(dc, attr);
+ attr->DecRef();
+
+ break;
+ }
+ }
+}
+
+// TODO: remove this ???
+// This is used to redraw all grid lines e.g. when the grid line colour
+// has been changed
+//
+void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
+{
+#if !WXGRID_DRAW_LINES
+ return;
+#endif
+
+ if ( !m_gridLinesEnabled ||
+ !m_numRows ||
+ !m_numCols ) return;
+
+ int top, bottom, left, right;
+
+#if 0 //#ifndef __WXGTK__
+ if (reg.IsEmpty())
+ {
+ int cw, ch;
+ m_gridWin->GetClientSize(&cw, &ch);
+
+ // virtual coords of visible area
+ //
+ CalcUnscrolledPosition( 0, 0, &left, &top );
+ CalcUnscrolledPosition( cw, ch, &right, &bottom );
+ }
+ else
+ {
+ wxCoord x, y, w, h;
+ reg.GetBox(x, y, w, h);
+ CalcUnscrolledPosition( x, y, &left, &top );
+ CalcUnscrolledPosition( x + w, y + h, &right, &bottom );
+ }
+#else
+ int cw, ch;
+ m_gridWin->GetClientSize(&cw, &ch);
+ CalcUnscrolledPosition( 0, 0, &left, &top );
+ CalcUnscrolledPosition( cw, ch, &right, &bottom );
+#endif
+
+ // avoid drawing grid lines past the last row and col
+ //
+ right = wxMin( right, GetColRight(m_numCols - 1) );
+ bottom = wxMin( bottom, GetRowBottom(m_numRows - 1) );
+
+ // no gridlines inside multicells, clip them out
+ int leftCol = internalXToCol(left);
+ int topRow = internalYToRow(top);
+ int rightCol = 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<bottomRow; j++)
+ {
+ for (i=leftCol; i<rightCol; i++)
+ {
+ GetCellSize( j, i, &cell_rows, &cell_cols );
+ if ((cell_rows > 1) || (cell_cols > 1))
+ {
+ rect = CellToRect(j,i);
+ CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+ clippedcells.Subtract(rect);
+ }
+ else if ((cell_rows < 0) || (cell_cols < 0))
+ {
+ rect = CellToRect(j+cell_rows, i+cell_cols);
+ CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+ clippedcells.Subtract(rect);
+ }
+ }
+ }
+#else
+ wxRegion clippedcells( left , top, right - left, bottom - top);
+
+ int i, j, cell_rows, cell_cols;
+ wxRect rect;
+
+ for (j=topRow; j<bottomRow; j++)
+ {
+ for (i=leftCol; i<rightCol; i++)
+ {
+ GetCellSize( j, i, &cell_rows, &cell_cols );
+ if ((cell_rows > 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.SetPen( wxPen(GetGridLineColour(), 1, wxSOLID) );
+
+ // horizontal grid lines
+ //
+ // already declared above - int i;
+ for ( i = internalYToRow(top); i < m_numRows; i++ )
+ {
+ int bot = GetRowBottom(i) - 1;
+
+ if ( bot > bottom )
+ {
+ break;
+ }
+
+ if ( bot >= top )
+ {
+ dc.DrawLine( left, bot, right, bot );
+ }
+ }
+
+
+ // vertical grid lines
+ //
+ for ( i = internalXToCol(left); i < m_numCols; i++ )
+ {
+ int colRight = GetColRight(i) - 1;
+ if ( colRight > right )
+ {
+ break;
+ }
+
+ if ( colRight >= left )
+ {
+ dc.DrawLine( colRight, top, colRight, bottom );
+ }
+ }
+ dc.DestroyClippingRegion();
+}
+
+
+void wxGrid::DrawRowLabels( wxDC& dc ,const wxArrayInt& rows)
+{
+ if ( !m_numRows ) return;
+
+ size_t i;
+ size_t numLabels = rows.GetCount();
+
+ for ( i = 0; i < numLabels; i++ )
+ {
+ DrawRowLabel( dc, rows[i] );
+ }
+}
+
+
+void wxGrid::DrawRowLabel( wxDC& dc, int row )
+{
+ if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
+ return;
+
+ 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_3DDKSHADOW),1, wxSOLID) );
+ dc.DrawLine( m_rowLabelWidth-1, rowTop,
+ m_rowLabelWidth-1, rowBottom );
+
+ dc.DrawLine( 0, rowTop, 0, rowBottom );
+
+ dc.DrawLine( 0, rowBottom, m_rowLabelWidth, rowBottom );
+
+ dc.SetPen( *wxWHITE_PEN );
+ dc.DrawLine( 1, rowTop, 1, rowBottom );
+ dc.DrawLine( 1, rowTop, m_rowLabelWidth-1, rowTop );
+#endif
+ dc.SetBackgroundMode( wxTRANSPARENT );
+ dc.SetTextForeground( GetLabelTextColour() );
+ dc.SetFont( GetLabelFont() );
+
+ int hAlign, vAlign;
+ GetRowLabelAlignment( &hAlign, &vAlign );
+
+ rect.SetX( 2 );
+ rect.SetY( GetRowTop(row) + 2 );
+ rect.SetWidth( m_rowLabelWidth - 4 );
+ rect.SetHeight( GetRowHeight(row) - 4 );
+ DrawTextRectangle( dc, GetRowLabelValue( row ), rect, hAlign, vAlign );
+}
+
+
+void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols )
+{
+ if ( !m_numCols ) return;
+
+ size_t i;
+ size_t numLabels = cols.GetCount();
+
+ for ( i = 0; i < numLabels; i++ )
+ {
+ DrawColLabel( dc, cols[i] );
+ }
+}
+
+
+void wxGrid::DrawColLabel( wxDC& dc, int col )
+{
+ if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 )
+ return;
+
+ int colLeft = GetColLeft(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;
+
+ wxRendererNative::Get().DrawHeaderButton( win_dc->m_owner, dc, rect, 0 );
+#else
+ int colRight = GetColRight(col) - 1;
+
+ dc.SetPen( wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW),1, wxSOLID) );
+ 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.SetBackgroundMode( wxTRANSPARENT );
+ dc.SetTextForeground( GetLabelTextColour() );
+ dc.SetFont( GetLabelFont() );
+
+ int hAlign, vAlign, orient;
+ GetColLabelAlignment( &hAlign, &vAlign );
+ orient = GetColLabelTextOrientation();
+
+ rect.SetX( colLeft + 2 );
+ rect.SetY( 2 );
+ rect.SetWidth( GetColWidth(col) - 4 );
+ rect.SetHeight( m_colLabelHeight - 4 );
+ DrawTextRectangle( dc, GetColLabelValue( col ), rect, hAlign, vAlign, orient );
+}
+
+void wxGrid::DrawTextRectangle( wxDC& dc,
+ const wxString& value,
+ const wxRect& rect,
+ int horizAlign,
+ int vertAlign,
+ int textOrientation )
+{
+ wxArrayString lines;
+
+ StringToLines( value, lines );
+
+
+ //Forward to new API.
+ DrawTextRectangle( dc,
+ lines,
+ rect,
+ horizAlign,
+ vertAlign,
+ textOrientation );
+
+}
+
+void wxGrid::DrawTextRectangle( wxDC& dc,
+ const wxArrayString& lines,
+ const wxRect& rect,
+ int horizAlign,
+ int vertAlign,
+ int textOrientation )
+{
+ long textWidth, textHeight;
+ long lineWidth, lineHeight;
+ int nLines;
+
+ dc.SetClippingRegion( rect );
+
+ nLines = lines.GetCount();
+ if( nLines > 0 )
+ {
+ int l;
+ float x = 0.0, y = 0.0;
+
+ if( textOrientation == wxHORIZONTAL )
+ GetTextBoxSize(dc, lines, &textWidth, &textHeight);
+ else
+ GetTextBoxSize( dc, lines, &textHeight, &textWidth );
+
+ switch( vertAlign )
+ {
+ case wxALIGN_BOTTOM:
+ if( textOrientation == wxHORIZONTAL )
+ y = rect.y + (rect.height - textHeight - 1);
+ else
+ x = rect.x + rect.width - textWidth;
+ break;
+
+ case wxALIGN_CENTRE:
+ if( textOrientation == wxHORIZONTAL )
+ y = rect.y + ((rect.height - textHeight)/2);
+ else
+ x = rect.x + ((rect.width - textWidth)/2);
+ break;
+
+ case wxALIGN_TOP:
+ default:
+ if( textOrientation == wxHORIZONTAL )
+ y = rect.y + 1;
+ else
+ x = rect.x + 1;
+ break;
+ }
+
+ // Align each line of a multi-line label
+ for( l = 0; l < nLines; l++ )
+ {
+ dc.GetTextExtent(lines[l], &lineWidth, &lineHeight);
+
+ switch( horizAlign )
+ {
+ case wxALIGN_RIGHT:
+ if( textOrientation == wxHORIZONTAL )
+ x = rect.x + (rect.width - lineWidth - 1);
+ else
+ y = rect.y + lineWidth + 1;
+ break;
+
+ case wxALIGN_CENTRE:
+ if( textOrientation == wxHORIZONTAL )
+ x = rect.x + ((rect.width - lineWidth)/2);
+ else
+ y = rect.y + rect.height - ((rect.height - lineWidth)/2);
+ break;
+
+ case wxALIGN_LEFT:
+ default:
+ if( textOrientation == wxHORIZONTAL )
+ x = rect.x + 1;
+ else
+ y = rect.y + rect.height - 1;
+ break;
+ }
+
+ if( textOrientation == wxHORIZONTAL )
+ {
+ dc.DrawText( lines[l], (int)x, (int)y );
+ y += lineHeight;
+ }
+ else
+ {
+ dc.DrawRotatedText( lines[l], (int)x, (int)y, 90.0 );
+ x += lineHeight;
+ }
+ }
+ }
+ dc.DestroyClippingRegion();
+}
+
+
+// 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 )
+{
+ int startPos = 0;
+ int pos;
+ wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
+ wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
+
+ while ( startPos < (int)tVal.Length() )
+ {
+ pos = tVal.Mid(startPos).Find( eol );
+ if ( pos < 0 )
+ {
+ break;
+ }
+ else if ( pos == 0 )
+ {
+ lines.Add( wxEmptyString );
+ }
+ else
+ {
+ lines.Add( value.Mid(startPos, pos) );
+ }
+ startPos += pos+1;
+ }
+ if ( startPos < (int)value.Length() )
+ {
+ lines.Add( value.Mid( startPos ) );
+ }
+}
+
+
+void wxGrid::GetTextBoxSize( wxDC& dc,
+ const wxArrayString& lines,
+ long *width, long *height )
+{
+ long w = 0;
+ long h = 0;
+ long lineW, lineH;
+
+ size_t i;
+ for ( i = 0; i < lines.GetCount(); i++ )
+ {
+ dc.GetTextExtent( lines[i], &lineW, &lineH );
+ w = wxMax( w, lineW );
+ h += lineH;
+ }
+
+ *width = w;
+ *height = h;
+}
+
+//
+// ------ Batch processing.
+//
+void wxGrid::EndBatch()
+{
+ if ( m_batchCount > 0 )
+ {
+ m_batchCount--;
+ if ( !m_batchCount )
+ {
+ CalcDimensions();
+ m_rowLabelWin->Refresh();
+ m_colLabelWin->Refresh();
+ m_cornerLabelWin->Refresh();
+ m_gridWin->Refresh();
+ }
+ }
+}
+
+// Use this, rather than wxWindow::Refresh(), to force an immediate
+// repainting of the grid. Has no effect if you are already inside a
+// BeginBatch / EndBatch block.
+//
+void wxGrid::ForceRefresh()
+{
+ BeginBatch();
+ EndBatch();
+}
+
+bool wxGrid::Enable(bool enable)
+{
+ if ( !wxScrolledWindow::Enable(enable) )
+ return false;
+
+ // redraw in the new state
+ m_gridWin->Refresh();
+
+ return true;
+}
+
+//
+// ------ Edit control functions
+//
+
+
+void wxGrid::EnableEditing( bool edit )
+{
+ // TODO: improve this ?
+ //
+ if ( edit != m_editable )
+ {
+ if(!edit) EnableCellEditControl(edit);
+ m_editable = edit;
+ }
+}
+
+
+void wxGrid::EnableCellEditControl( bool enable )
+{
+ if (! m_editable)
+ return;
+
+ if ( m_currentCellCoords == wxGridNoCellCoords )
+ SetCurrentCell( 0, 0 );
+
+ if ( enable != m_cellEditCtrlEnabled )
+ {
+ if ( enable )
+ {
+ if (SendEvent( wxEVT_GRID_EDITOR_SHOWN) <0)
+ return;
+
+ // this should be checked by the caller!
+ wxASSERT_MSG( CanEnableCellControl(),
+ _T("can't enable editing for this cell!") );
+
+ // do it before ShowCellEditControl()
+ m_cellEditCtrlEnabled = enable;
+
+ ShowCellEditControl();
+ }
+ else
+ {
+ //FIXME:add veto support
+ SendEvent( wxEVT_GRID_EDITOR_HIDDEN);
+
+ HideCellEditControl();
+ SaveEditControlValue();
+
+ // do it after HideCellEditControl()
+ m_cellEditCtrlEnabled = enable;
+ }
+ }
+}
+
+bool wxGrid::IsCurrentCellReadOnly() const
+{
+ // const_cast
+ wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
+ bool readonly = attr->IsReadOnly();
+ attr->DecRef();
+
+ return readonly;
+}
+
+bool wxGrid::CanEnableCellControl() const
+{
+ return m_editable && (m_currentCellCoords != wxGridNoCellCoords) &&
+ !IsCurrentCellReadOnly();
+
+}
+
+bool wxGrid::IsCellEditControlEnabled() const
+{
+ // the cell edit control might be disable for all cells or just for the
+ // current one if it's read only
+ return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : false;
+}
+
+bool wxGrid::IsCellEditControlShown() const
+{
+ bool isShown = false;
+
+ if ( m_cellEditCtrlEnabled )
+ {
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
+ attr->DecRef();
+
+ if ( editor )
+ {
+ if ( editor->IsCreated() )
+ {
+ isShown = editor->GetControl()->IsShown();
+ }
+
+ editor->DecRef();
+ }
+ }
+
+ return isShown;
+}
+
+void wxGrid::ShowCellEditControl()
+{
+ if ( IsCellEditControlEnabled() )
+ {
+ if ( !IsVisible( m_currentCellCoords ) )
+ {
+ m_cellEditCtrlEnabled = false;
+ return;
+ }
+ else
+ {
+ wxRect rect = CellToRect( m_currentCellCoords );
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ // if this is part of a multicell, find owner (topleft)
+ int cell_rows, cell_cols;
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+ if ( cell_rows <= 0 || cell_cols <= 0 )
+ {
+ row += cell_rows;
+ col += cell_cols;
+ m_currentCellCoords.SetRow( row );
+ m_currentCellCoords.SetCol( col );
+ }
+
+ // convert to scrolled coords
+ //
+ CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+ // done in PaintBackground()
+#if 0
+ // erase the highlight and the cell contents because the editor
+ // might not cover the entire cell
+ wxClientDC dc( m_gridWin );
+ PrepareDC( dc );
+ dc.SetBrush(*wxLIGHT_GREY_BRUSH); //wxBrush(attr->GetBackgroundColour(), wxSOLID));
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ dc.DrawRectangle(rect);
+#endif // 0
+
+ // cell is shifted by one pixel
+ // However, don't allow x or y to become negative
+ // since the SetSize() method interprets that as
+ // "don't change."
+ if (rect.x > 0)
+ rect.x--;
+ if (rect.y > 0)
+ rect.y--;
+
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor* editor = attr->GetEditor(this, row, col);
+ if ( !editor->IsCreated() )
+ {
+ editor->Create(m_gridWin, wxID_ANY,
+ new wxGridCellEditorEvtHandler(this, editor));
+
+ wxGridEditorCreatedEvent evt(GetId(),
+ wxEVT_GRID_EDITOR_CREATED,
+ this,
+ row,
+ col,
+ editor->GetControl());
+ GetEventHandler()->ProcessEvent(evt);
+ }
+
+
+ // resize editor to overflow into righthand cells if allowed
+ int maxWidth = rect.width;
+ wxString value = GetCellValue(row, col);
+ if ( (value != wxEmptyString) && (attr->GetOverflow()) )
+ {
+ int y;
+ GetTextExtent(value, &maxWidth, &y,
+ NULL, NULL, &attr->GetFont());
+ if (maxWidth < rect.width) maxWidth = rect.width;
+ }
+ int client_right = m_gridWin->GetClientSize().GetWidth();
+ if (rect.x+maxWidth > client_right)
+ maxWidth = client_right - rect.x;
+
+ if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
+ {
+ GetCellSize( row, col, &cell_rows, &cell_cols );
+ // may have changed earlier
+ for (int i = col+cell_cols; i < m_numCols; i++)
+ {
+ int c_rows, c_cols;
+ GetCellSize( row, i, &c_rows, &c_cols );
+ // looks weird going over a multicell
+ if (m_table->IsEmptyCell(row,i) &&
+ (rect.width < maxWidth) && (c_rows == 1))
+ rect.width += GetColWidth(i);
+ else
+ break;
+ }
+ if (rect.GetRight() > client_right)
+ rect.SetRight(client_right-1);
+ }
+
+ editor->SetCellAttr(attr);
+ editor->SetSize( rect );
+ editor->Show( true, attr );
+
+ // recalc dimensions in case we need to
+ // expand the scrolled window to account for editor
+ CalcDimensions();
+
+ editor->BeginEdit(row, col, this);
+ editor->SetCellAttr(NULL);
+
+ editor->DecRef();
+ attr->DecRef();
+ }
+ }
+}
+
+
+void wxGrid::HideCellEditControl()
+{
+ if ( IsCellEditControlEnabled() )
+ {
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor *editor = attr->GetEditor(this, row, col);
+ editor->Show( false );
+ editor->DecRef();
+ attr->DecRef();
+ m_gridWin->SetFocus();
+ // refresh whole row to the right
+ wxRect rect( CellToRect(row, col) );
+ CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
+ rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
+#ifdef __WXMAC__
+ // ensure that the pixels under the focus ring get refreshed as well
+ rect.Inflate(10,10);
+#endif
+ m_gridWin->Refresh( false, &rect );
+ }
+}
+
+
+void wxGrid::SaveEditControlValue()
+{
+ if ( IsCellEditControlEnabled() )
+ {
+ int row = m_currentCellCoords.GetRow();
+ int col = m_currentCellCoords.GetCol();
+
+ wxString oldval = GetCellValue(row,col);
+
+ wxGridCellAttr* attr = GetCellAttr(row, col);
+ wxGridCellEditor* editor = attr->GetEditor(this, row, col);
+ bool changed = editor->EndEdit(row, col, this);
+
+ editor->DecRef();
+ attr->DecRef();
+
+ if (changed)
+ {
+ if ( SendEvent( wxEVT_GRID_CELL_CHANGE,
+ m_currentCellCoords.GetRow(),
+ m_currentCellCoords.GetCol() ) < 0 ) {
+
+ // Event has been vetoed, set the data back.
+ SetCellValue(row,col,oldval);
+ }
+ }
+ }
+}
+
+
+//
+// ------ Grid location functions
+// Note that all of these functions work with the logical coordinates of
+// grid cells and labels so you will need to convert from device
+// coordinates for mouse events etc.
+//
+
+void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords )