]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/grid.cpp
new file added
[wxWidgets.git] / src / generic / grid.cpp
index edb709d70617ffdab91e7e84b4e437a7317148c0..337c2264e0339c3d93a18b28066b5289b88819be 100644 (file)
@@ -72,8 +72,8 @@ const char wxGridNameStr[] = "grid";
 // Required for wxIs... functions
 #include <ctype.h>
 
-WX_DECLARE_HASH_SET_WITH_DECL(int, wxIntegerHash, wxIntegerEqual,
-                              wxGridFixedIndicesSet, class WXDLLIMPEXP_ADV);
+WX_DECLARE_HASH_SET_WITH_DECL_PTR(int, ::wxIntegerHash, ::wxIntegerEqual,
+                                  wxGridFixedIndicesSet, class WXDLLIMPEXP_ADV);
 
 
 // ----------------------------------------------------------------------------
@@ -156,6 +156,23 @@ wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent );
 wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent );
 wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent );
 
+// ----------------------------------------------------------------------------
+// private helpers
+// ----------------------------------------------------------------------------
+
+namespace
+{
+    
+    // ensure that first is less or equal to second, swapping the values if
+    // necessary
+    void EnsureFirstLessThanSecond(int& first, int& second)
+    {
+        if ( first > second )
+            wxSwap(first, second);
+    }
+    
+} // anonymous namespace
+
 // ============================================================================
 // implementation
 // ============================================================================
@@ -170,6 +187,8 @@ END_EVENT_TABLE()
 
 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)
@@ -400,7 +419,7 @@ void wxGridCellAttr::SetSize(int num_rows, int num_cols)
     wxASSERT_MSG( (!((num_rows > 0) && (num_cols <= 0)) ||
                   !((num_rows <= 0) && (num_cols > 0)) ||
                   !((num_rows == 0) && (num_cols == 0))),
-                  wxT("wxGridCellAttr::SetSize only takes two postive values or negative/zero values"));
+                  wxT("wxGridCellAttr::SetSize only takes two positive values or negative/zero values"));
 
     m_sizeRows = num_rows;
     m_sizeCols = num_cols;
@@ -1515,9 +1534,9 @@ bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
         // m_colLabels stores just as many elements as it needs, e.g. if only
         // the label of the first column had been set it would have only one
         // element and not numCols, so account for it
-        int nToRm = m_colLabels.size() - colID;
-        if ( nToRm > 0 )
-            m_colLabels.RemoveAt( colID, nToRm );
+        int numRemaining = m_colLabels.size() - colID;
+        if (numRemaining > 0)
+            m_colLabels.RemoveAt( colID, wxMin(numCols, numRemaining) );
     }
 
     if ( numCols >= curNumCols )
@@ -1757,6 +1776,300 @@ void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
     m_owner->DrawHighlight( dc, dirtyCells );
 }
 
+void wxGrid::Render( wxDC& dc,
+                     const wxPoint& position,
+                     const wxSize& size,
+                     const wxGridCellCoords& topLeft,
+                     const wxGridCellCoords& bottomRight,
+                     int style )
+{
+    wxCHECK_RET( bottomRight.GetCol() < GetNumberCols(),
+                 "Invalid right column" );
+    wxCHECK_RET( bottomRight.GetRow() < GetNumberRows(),
+                 "Invalid bottom row" );
+
+    // store user settings and reset later
+
+    // remove grid selection, don't paint selection colour
+    // unless we have wxGRID_DRAW_SELECTION
+    // block selections are the only ones catered for here
+    wxGridCellCoordsArray selectedCells;
+    bool hasSelection = IsSelection();
+    if ( hasSelection && !( style & wxGRID_DRAW_SELECTION ) )
+    {
+        selectedCells = GetSelectionBlockTopLeft();
+        // non block selections may not have a bottom right
+        if ( GetSelectionBlockBottomRight().size() )
+            selectedCells.Add( GetSelectionBlockBottomRight()[ 0 ] );
+
+        ClearSelection();
+    }
+
+    // store user device origin
+    wxCoord userOriginX, userOriginY;
+    dc.GetDeviceOrigin( &userOriginX, &userOriginY );
+
+    // store user scale
+    double scaleUserX, scaleUserY;
+    dc.GetUserScale( &scaleUserX, &scaleUserY );
+
+    // set defaults if necessary
+    wxGridCellCoords leftTop( topLeft ), rightBottom( bottomRight );
+    if ( leftTop.GetCol() < 0 )
+        leftTop.SetCol(0);
+    if ( leftTop.GetRow() < 0 )
+        leftTop.SetRow(0);
+    if ( rightBottom.GetCol() < 0 )
+        rightBottom.SetCol(GetNumberCols() - 1);
+    if ( rightBottom.GetRow() < 0 )
+        rightBottom.SetRow(GetNumberRows() - 1);
+
+    // get grid offset, size and cell parameters
+    wxPoint pointOffSet;
+    wxSize sizeGrid;
+    wxGridCellCoordsArray renderCells;
+    wxArrayInt arrayCols;
+    wxArrayInt arrayRows;
+
+    GetRenderSizes( leftTop, rightBottom,
+                    pointOffSet, sizeGrid,
+                    renderCells,
+                    arrayCols, arrayRows );
+
+    // add headers/labels to dimensions
+    if ( style & wxGRID_DRAW_ROWS_HEADER )
+        sizeGrid.x += GetRowLabelSize();
+    if ( style & wxGRID_DRAW_COLS_HEADER )
+        sizeGrid.y += GetColLabelSize();
+
+    // get render start position in logical units
+    wxPoint positionRender = GetRenderPosition( dc, position );
+
+    wxCoord originX = dc.LogicalToDeviceX( positionRender.x );
+    wxCoord originY = dc.LogicalToDeviceY( positionRender.y );
+
+    dc.SetDeviceOrigin( originX, originY );
+
+    SetRenderScale( dc, positionRender, size, sizeGrid );
+
+    // draw row headers at specified origin
+    if ( GetRowLabelSize() > 0 && ( style & wxGRID_DRAW_ROWS_HEADER ) )
+    {
+        if ( style & wxGRID_DRAW_COLS_HEADER )
+        {
+            DrawCornerLabel( dc ); // do only if both col and row labels drawn
+            originY += dc.LogicalToDeviceYRel( GetColLabelSize() );
+        }
+
+        originY -= dc.LogicalToDeviceYRel( pointOffSet.y );
+        dc.SetDeviceOrigin( originX, originY );
+
+        DrawRowLabels( dc, arrayRows );
+
+        // reset for columns
+        if ( style & wxGRID_DRAW_COLS_HEADER )
+            originY -= dc.LogicalToDeviceYRel( GetColLabelSize() );
+
+        originY += dc.LogicalToDeviceYRel( pointOffSet.y );
+        // X offset so we don't overwrite row labels
+        originX += dc.LogicalToDeviceXRel( GetRowLabelSize() );
+    }
+
+    // subtract col offset where startcol > 0
+    originX -= dc.LogicalToDeviceXRel( pointOffSet.x );
+    // no y offset for col labels, they are at the Y origin
+
+    // draw column labels
+    if ( style & wxGRID_DRAW_COLS_HEADER )
+    {
+        dc.SetDeviceOrigin( originX, originY );
+        DrawColLabels( dc, arrayCols );
+        // don't overwrite the labels, increment originY
+        originY += dc.LogicalToDeviceYRel( GetColLabelSize() );
+    }
+
+    // set device origin to draw grid cells and lines
+    originY -= dc.LogicalToDeviceYRel( pointOffSet.y );
+    dc.SetDeviceOrigin( originX, originY );
+
+    // draw cell area background
+    dc.SetBrush( GetDefaultCellBackgroundColour() );
+    dc.SetPen( *wxTRANSPARENT_PEN );
+    // subtract headers from grid area dimensions
+    wxSize sizeCells( sizeGrid );
+    if ( style & wxGRID_DRAW_ROWS_HEADER )
+        sizeCells.x -= GetRowLabelSize();
+    if ( style & wxGRID_DRAW_COLS_HEADER )
+        sizeCells.y -= GetColLabelSize();
+
+    dc.DrawRectangle( pointOffSet, sizeCells );
+
+    // draw cells
+    DrawGridCellArea( dc, renderCells );
+
+    // draw grid lines
+    if ( style & wxGRID_DRAW_CELL_LINES )
+    {
+        wxRegion regionClip( pointOffSet.x, pointOffSet.y,
+                             sizeCells.x, sizeCells.y );
+
+        DrawRangeGridLines(dc, regionClip, renderCells[0], renderCells.Last());
+    }
+
+    // draw render rectangle bounding lines
+    DoRenderBox( dc, style,
+                 pointOffSet, sizeCells,
+                 leftTop, rightBottom );
+
+    // restore user setings
+    dc.SetDeviceOrigin( userOriginX, userOriginY );
+    dc.SetUserScale( scaleUserX, scaleUserY );
+
+    if ( selectedCells.size() && !( style & wxGRID_DRAW_SELECTION ) )
+    {
+        SelectBlock( selectedCells[ 0 ].GetRow(),
+                     selectedCells[ 0 ].GetCol(),
+                     selectedCells[ selectedCells.size() -1 ].GetRow(),
+                     selectedCells[ selectedCells.size() -1 ].GetCol() );
+    }
+}
+
+void
+wxGrid::SetRenderScale(wxDC& dc,
+                       const wxPoint& pos, const wxSize& size,
+                       const wxSize& sizeGrid )
+{
+    double scaleX, scaleY;
+    wxSize sizeTemp;
+
+    if ( size.GetWidth() != wxDefaultSize.GetWidth() ) // size.x was specified
+        sizeTemp.SetWidth( size.GetWidth() );
+    else
+        sizeTemp.SetWidth( dc.DeviceToLogicalXRel( dc.GetSize().GetWidth() )
+                           - pos.x );
+
+    if ( size.GetHeight() != wxDefaultSize.GetHeight() ) // size.y was specified
+        sizeTemp.SetHeight( size.GetHeight() );
+    else
+        sizeTemp.SetHeight( dc.DeviceToLogicalYRel( dc.GetSize().GetHeight() )
+                            - pos.y );
+
+    scaleX = (double)( (double) sizeTemp.GetWidth() / (double) sizeGrid.GetWidth() );
+    scaleY = (double)( (double) sizeTemp.GetHeight() / (double) sizeGrid.GetHeight() );
+
+    dc.SetUserScale( wxMin( scaleX, scaleY), wxMin( scaleX, scaleY ) );
+}
+
+// get grid rendered size, origin offset and fill cell arrays
+void wxGrid::GetRenderSizes( const wxGridCellCoords& topLeft,
+                             const wxGridCellCoords& bottomRight,
+                             wxPoint& pointOffSet, wxSize& sizeGrid,
+                             wxGridCellCoordsArray& renderCells,
+                             wxArrayInt& arrayCols, wxArrayInt& arrayRows )
+{
+    pointOffSet.x = 0;
+    pointOffSet.y = 0;
+    sizeGrid.SetWidth( 0 );
+    sizeGrid.SetHeight( 0 );
+
+    int col, row;
+
+    wxGridSizesInfo sizeinfo = GetColSizes();
+    for ( col = 0; col <= bottomRight.GetCol(); col++ )
+    {
+        if ( col < topLeft.GetCol() )
+        {
+            pointOffSet.x += sizeinfo.GetSize( col );
+        }
+        else
+        {
+            for ( row = topLeft.GetRow(); row <= bottomRight.GetRow(); row++ )
+            {
+                renderCells.Add( wxGridCellCoords( row, col ));
+                arrayRows.Add( row ); // column labels rendered in DrawColLabels
+            }
+            arrayCols.Add( col ); // row labels rendered in DrawRowLabels
+            sizeGrid.x += sizeinfo.GetSize( col );
+        }
+    }
+
+    sizeinfo = GetRowSizes();
+    for ( row = 0; row <= bottomRight.GetRow(); row++ )
+    {
+        if ( row < topLeft.GetRow() )
+            pointOffSet.y += sizeinfo.GetSize( row );
+        else
+            sizeGrid.y += sizeinfo.GetSize( row );
+    }
+}
+
+// get render start position
+// if position not specified use dc draw extents MaxX and MaxY
+wxPoint wxGrid::GetRenderPosition( wxDC& dc, const wxPoint& position )
+{
+    wxPoint positionRender( position );
+
+    if ( !positionRender.IsFullySpecified() )
+    {
+        if ( positionRender.x == wxDefaultPosition.x )
+            positionRender.x = dc.MaxX();
+
+        if ( positionRender.y == wxDefaultPosition.y )
+            positionRender.y = dc.MaxY();
+    }
+
+    return positionRender;
+}
+
+// draw render rectangle bounding lines
+// useful where there is multi cell row or col clipping and no cell border
+void wxGrid::DoRenderBox( wxDC& dc, const int& style,
+                          const wxPoint& pointOffSet,
+                          const wxSize& sizeCells,
+                          const wxGridCellCoords& topLeft,
+                          const wxGridCellCoords& bottomRight )
+{
+    if ( !( style & wxGRID_DRAW_BOX_RECT ) )
+        return;
+
+    int bottom = pointOffSet.y + sizeCells.GetY(),
+        right = pointOffSet.x + sizeCells.GetX() - 1;
+
+    // horiz top line if we are not drawing column header/labels
+    if ( !( style & wxGRID_DRAW_COLS_HEADER ) )
+    {
+        int left = pointOffSet.x;
+        left += ( style & wxGRID_DRAW_COLS_HEADER )
+                     ? - GetRowLabelSize() : 0;
+        dc.SetPen( GetRowGridLinePen( topLeft.GetRow() ) );
+        dc.DrawLine( left,
+                     pointOffSet.y,
+                     right,
+                     pointOffSet.y );
+    }
+
+    // horiz bottom line
+    dc.SetPen( GetRowGridLinePen( bottomRight.GetRow() ) );
+    dc.DrawLine( pointOffSet.x, bottom - 1, right, bottom - 1 );
+
+    // left vertical line if we are not drawing row header/labels
+    if ( !( style & wxGRID_DRAW_ROWS_HEADER ) )
+    {
+        int top = pointOffSet.y;
+        top += ( style & wxGRID_DRAW_COLS_HEADER )
+                     ? - GetColLabelSize() : 0;
+        dc.SetPen( GetColGridLinePen( topLeft.GetCol() ) );
+        dc.DrawLine( pointOffSet.x -1,
+                     top,
+                     pointOffSet.x - 1,
+                     bottom - 1 );
+    }
+
+    // right vertical line
+    dc.SetPen( GetColGridLinePen( bottomRight.GetCol() ) );
+    dc.DrawLine( right, pointOffSet.y, right, bottom - 1 );
+}
+
 void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
 {
     wxWindow::ScrollWindow( dx, dy, rect );
@@ -1837,58 +2150,6 @@ void wxGridWindow::OnFocus(wxFocusEvent& event)
 
 /////////////////////////////////////////////////////////////////////
 
-#if wxUSE_EXTENDED_RTTI
-WX_DEFINE_FLAGS( wxGridStyle )
-
-wxBEGIN_FLAGS( wxGridStyle )
-    // new style border flags, we put them first to
-    // use them for streaming out
-    wxFLAGS_MEMBER(wxBORDER_SIMPLE)
-    wxFLAGS_MEMBER(wxBORDER_SUNKEN)
-    wxFLAGS_MEMBER(wxBORDER_DOUBLE)
-    wxFLAGS_MEMBER(wxBORDER_RAISED)
-    wxFLAGS_MEMBER(wxBORDER_STATIC)
-    wxFLAGS_MEMBER(wxBORDER_NONE)
-
-    // old style border flags
-    wxFLAGS_MEMBER(wxSIMPLE_BORDER)
-    wxFLAGS_MEMBER(wxSUNKEN_BORDER)
-    wxFLAGS_MEMBER(wxDOUBLE_BORDER)
-    wxFLAGS_MEMBER(wxRAISED_BORDER)
-    wxFLAGS_MEMBER(wxSTATIC_BORDER)
-    wxFLAGS_MEMBER(wxBORDER)
-
-    // standard window styles
-    wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
-    wxFLAGS_MEMBER(wxCLIP_CHILDREN)
-    wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
-    wxFLAGS_MEMBER(wxWANTS_CHARS)
-    wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
-    wxFLAGS_MEMBER(wxALWAYS_SHOW_SB)
-    wxFLAGS_MEMBER(wxVSCROLL)
-    wxFLAGS_MEMBER(wxHSCROLL)
-
-wxEND_FLAGS( wxGridStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxGrid, wxScrolledWindow,"wx/grid.h")
-
-wxBEGIN_PROPERTIES_TABLE(wxGrid)
-    wxHIDE_PROPERTY( Children )
-    wxPROPERTY_FLAGS( WindowStyle , wxGridStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxGrid)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_5( wxGrid , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
-
-/*
- TODO : Expose more information of a list's layout, etc. via appropriate objects (e.g., NotebookPageInfo)
-*/
-#else
-IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
-#endif
-
 BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
     EVT_PAINT( wxGrid::OnPaint )
     EVT_SIZE( wxGrid::OnSize )
@@ -3102,7 +3363,7 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
             ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
             m_dragLastPos = -1;
         }
-        else // not on row separator or it's not resizeable
+        else // not on row separator or it's not resizable
         {
             row = YToRow(y);
             if ( row >=0 &&
@@ -3681,13 +3942,15 @@ void wxGrid::ChangeCursorMode(CursorMode mode,
 // grid mouse event processing
 // ----------------------------------------------------------------------------
 
-void
+bool
 wxGrid::DoGridCellDrag(wxMouseEvent& event,
                        const wxGridCellCoords& coords,
                        bool isFirstDrag)
 {
+    bool performDefault = true ;
+    
     if ( coords == wxGridNoCellCoords )
-        return; // we're outside any valid cell
+        return performDefault; // we're outside any valid cell
 
     // Hide the edit control, so it won't interfere with drag-shrinking.
     if ( IsCellEditControlShown() )
@@ -3698,7 +3961,7 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event,
 
     switch ( event.GetModifiers() )
     {
-        case wxMOD_CMD:
+        case wxMOD_CONTROL:
             if ( m_selectedBlockCorner == wxGridNoCellCoords)
                 m_selectedBlockCorner = coords;
             UpdateBlockBeingSelected(m_selectedBlockCorner, coords);
@@ -3712,8 +3975,11 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event,
                     if ( m_selectedBlockCorner == wxGridNoCellCoords)
                         m_selectedBlockCorner = coords;
 
-                    SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event);
-                    return;
+                    // if event is handled by user code, no further processing
+                    if ( SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event) != 0 )
+                        performDefault = false;
+                    
+                    return performDefault;
                 }
             }
 
@@ -3724,6 +3990,8 @@ wxGrid::DoGridCellDrag(wxMouseEvent& event,
             // we don't handle the other key modifiers
             event.Skip();
     }
+    
+    return performDefault;
 }
 
 void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper)
@@ -3776,7 +4044,9 @@ void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords
     switch ( m_cursorMode )
     {
         case WXGRID_CURSOR_SELECT_CELL:
-            DoGridCellDrag(event, coords, isFirstDrag);
+            // no further handling if handled by user
+            if ( DoGridCellDrag(event, coords, isFirstDrag) == false )
+                return;
             break;
 
         case WXGRID_CURSOR_RESIZE_ROW:
@@ -4130,13 +4400,13 @@ bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper)
             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(
@@ -4430,6 +4700,14 @@ wxGrid::SendEvent(const wxEventType type,
                mouseEv.GetY() + GetColLabelSize(),
                false,
                mouseEv);
+
+       if ( type == wxEVT_GRID_CELL_BEGIN_DRAG )
+       {
+           // by default the dragging is not supported, the user code must
+           // explicitly allow the event for it to take place
+           gridEvt.Veto();
+       }
+              
        claimed = GetEventHandler()->ProcessEvent(gridEvt);
        vetoed = !gridEvt.IsAllowed();
    }
@@ -4857,6 +5135,9 @@ void
 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() )
@@ -4893,9 +5174,6 @@ wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol,
         }
     }
 
-    m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
-    MakeCellVisible(m_selectedBlockCorner);
-
     EnsureFirstLessThanSecond(topRow, bottomRow);
     EnsureFirstLessThanSecond(leftCol, rightCol);
 
@@ -5362,6 +5640,67 @@ void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells)
     }
 }
 
+// Used by wxGrid::Render() to draw the grid lines only for the cells in the
+// specified range.
+void
+wxGrid::DrawRangeGridLines(wxDC& dc,
+                           const wxRegion& reg,
+                           const wxGridCellCoords& topLeft,
+                           const wxGridCellCoords& bottomRight)
+{
+    if ( !m_gridLinesEnabled )
+         return;
+
+    int top, left, width, height;
+    reg.GetBox( left, top, width, height );
+
+    // create a clipping region
+    wxRegion clippedcells( dc.LogicalToDeviceX( left ),
+                           dc.LogicalToDeviceY( top ),
+                           dc.LogicalToDeviceXRel( width ),
+                           dc.LogicalToDeviceYRel( height ) );
+
+    // subtract multi cell span area from clipping region for lines
+    wxRect rect;
+    for ( int row = topLeft.GetRow(); row <= bottomRight.GetRow(); row++ )
+    {
+        for ( int col = topLeft.GetCol(); col <= bottomRight.GetCol(); col++ )
+        {
+            int cell_rows, cell_cols;
+            GetCellSize( row, col, &cell_rows, &cell_cols );
+            if ( cell_rows > 1 || cell_cols > 1 ) // multi cell
+            {
+                rect = CellToRect( row, col );
+                // cater for scaling
+                // device origin already set in ::Render() for x, y
+                rect.x = dc.LogicalToDeviceX( rect.x );
+                rect.y = dc.LogicalToDeviceY( rect.y );
+                rect.width = dc.LogicalToDeviceXRel( rect.width );
+                rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
+                clippedcells.Subtract( rect );
+            }
+            else if ( cell_rows < 0 || cell_cols < 0 ) // part of multicell
+            {
+                rect = CellToRect( row + cell_rows, col + cell_cols );
+                rect.x = dc.LogicalToDeviceX( rect.x );
+                rect.y = dc.LogicalToDeviceY( rect.y );
+                rect.width = dc.LogicalToDeviceXRel( rect.width );
+                rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
+                clippedcells.Subtract( rect );
+            }
+        }
+    }
+
+    dc.SetDeviceClippingRegion( clippedcells );
+
+    DoDrawGridLines(dc,
+                    top, left, top + height, left + width,
+                    topLeft.GetRow(), topLeft.GetCol(),
+                    bottomRight.GetRow(), bottomRight.GetCol());
+
+    dc.DestroyClippingRegion();
+}
+
 // This is used to redraw all grid lines e.g. when the grid line colour
 // has been changed
 //
@@ -5433,9 +5772,22 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
 
     dc.SetDeviceClippingRegion( clippedcells );
 
+    DoDrawGridLines(dc,
+                    top, left, bottom, right,
+                    topRow, leftCol, m_numRows, m_numCols);
+
+    dc.DestroyClippingRegion();
+}
 
+void
+wxGrid::DoDrawGridLines(wxDC& dc,
+                        int top, int left,
+                        int bottom, int right,
+                        int topRow, int leftCol,
+                        int bottomRow, int rightCol)
+{
     // horizontal grid lines
-    for ( int i = internalYToRow(top); i < m_numRows; i++ )
+    for ( int i = topRow; i < bottomRow; i++ )
     {
         int bot = GetRowBottom(i) - 1;
 
@@ -5450,7 +5802,7 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
     }
 
     // vertical grid lines
-    for ( int colPos = leftCol; colPos < m_numCols; colPos++ )
+    for ( int colPos = leftCol; colPos < rightCol; colPos++ )
     {
         int i = GetColAt( colPos );
 
@@ -5469,8 +5821,6 @@ void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
             dc.DrawLine( colRight, top, colRight, bottom );
         }
     }
-
-    dc.DestroyClippingRegion();
 }
 
 void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows)
@@ -5890,8 +6240,8 @@ void wxGrid::EnableCellEditControl( bool enable )
 
 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();
 
@@ -6279,7 +6629,9 @@ int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
         else if ( line > 0 &&
                     pos - oper.GetLineStartPos(this,
                                                line) < WXGRID_LABEL_EDGE_ZONE )
-            return line - 1;
+        {
+            return oper.GetLineBefore(this, line);
+        }
     }
 
     return -1;
@@ -6949,7 +7301,7 @@ void wxGrid::SetCellHighlightPenWidth(int width)
         m_cellHighlightPenWidth = width;
 
         // Just redrawing the cell highlight is not enough since that won't
-        // make any visible change if the the thickness is getting smaller.
+        // make any visible change if the thickness is getting smaller.
         int row = m_currentCellCoords.GetRow();
         int col = m_currentCellCoords.GetCol();
         if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
@@ -6967,7 +7319,7 @@ void wxGrid::SetCellHighlightROPenWidth(int width)
         m_cellHighlightROPenWidth = width;
 
         // Just redrawing the cell highlight is not enough since that won't
-        // make any visible change if the the thickness is getting smaller.
+        // make any visible change if the thickness is getting smaller.
         int row = m_currentCellCoords.GetRow();
         int col = m_currentCellCoords.GetCol();
         if ( row == -1 || col == -1 ||
@@ -7257,11 +7609,18 @@ void wxGrid::ClearAttrCache()
     }
 }
 
+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;
@@ -7689,7 +8048,7 @@ void wxGrid::SetRowSize( int row, int height )
     }
 
     // See comment in SetColSize
-    if ( height < GetRowMinimalAcceptableHeight())
+    if ( height > 0 && height < GetRowMinimalAcceptableHeight())
         return;
 
     if ( m_rowHeights.IsEmpty() )
@@ -7708,7 +8067,10 @@ void wxGrid::SetRowSize( int row, int height )
     }
 
     if ( !GetBatchCount() )
+    {
         CalcDimensions();
+        Refresh();
+    }
 }
 
 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
@@ -8155,7 +8517,7 @@ void wxGrid::AutoSizeColLabelSize( int col )
 
 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
@@ -8176,10 +8538,12 @@ void wxGrid::Fit()
     AutoSize();
 }
 
+#if WXWIN_COMPATIBILITY_2_8
 wxPen& wxGrid::GetDividerPen() const
 {
     return wxNullPen;
 }
+#endif // WXWIN_COMPATIBILITY_2_8
 
 // ----------------------------------------------------------------------------
 // cell value accessor functions