]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/grid.cpp
Document struct wxPGPaintData in function docs of only function that uses it
[wxWidgets.git] / src / generic / grid.cpp
index e9c3813f83a7e5bf1ee5d81bea86c9f74e903204..41fe1d6fc9ee6eebe486269ffca27033d7d05aa3 100644 (file)
@@ -9,6 +9,15 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
+/*
+    TODO:
+
+    - Replace use of wxINVERT with wxOverlay
+    - Make Begin/EndBatch() the same as the generic Freeze/Thaw()
+    - Review the column reordering code, it's a mess.
+    - Implement row reordering after dealing with the columns.
+ */
+
 // For compilers that support precompilation, includes "wx/wx.h".
 #include "wx/wxprec.h"
 
@@ -388,7 +397,7 @@ WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray,
 class WXDLLIMPEXP_ADV wxGridTypeRegistry
 {
 public:
-  wxGridTypeRegistry() {}
+    wxGridTypeRegistry() {}
     ~wxGridTypeRegistry();
 
     void RegisterDataType(const wxString& typeName,
@@ -414,6 +423,351 @@ private:
     wxGridDataTypeInfoArray m_typeinfo;
 };
 
+// ----------------------------------------------------------------------------
+// operations classes abstracting the difference between operating on rows and
+// columns
+// ----------------------------------------------------------------------------
+
+// This class allows to write a function only once because by using its methods
+// it will apply to both columns and rows.
+//
+// This is an abstract interface definition, the two concrete implementations
+// below should be used when working with rows and columns respectively.
+class wxGridOperations
+{
+public:
+    // Returns the operations in the other direction, i.e. wxGridRowOperations
+    // if this object is a wxGridColumnOperations and vice versa.
+    virtual wxGridOperations& Dual() const = 0;
+
+    // Return the number of rows or columns.
+    virtual int GetNumberOfLines(const wxGrid *grid) const = 0;
+
+    // Return the selection mode which allows selecting rows or columns.
+    virtual wxGrid::wxGridSelectionModes GetSelectionMode() const = 0;
+
+    // Make a wxGridCellCoords from the given components: thisDir is row or
+    // column and otherDir is column or row
+    virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const = 0;
+
+    // Calculate the scrolled position of the given abscissa or ordinate.
+    virtual int CalcScrolledPosition(wxGrid *grid, int pos) const = 0;
+
+    // Selects the horizontal or vertical component from the given object.
+    virtual int Select(const wxGridCellCoords& coords) const = 0;
+    virtual int Select(const wxPoint& pt) const = 0;
+    virtual int Select(const wxSize& sz) const = 0;
+    virtual int Select(const wxRect& r) const = 0;
+    virtual int& Select(wxRect& r) const = 0;
+
+    // Returns width or height of the rectangle
+    virtual int& SelectSize(wxRect& r) const = 0;
+
+    // Make a wxSize such that Select() applied to it returns first component
+    virtual wxSize MakeSize(int first, int second) const = 0;
+
+    // Sets the row or column component of the given cell coordinates
+    virtual void Set(wxGridCellCoords& coords, int line) const = 0;
+
+
+    // Draws a line parallel to the row or column, i.e. horizontal or vertical:
+    // pos is the vertical or horizontal position of the line and start and end
+    // are the coordinates of the line extremities in the other direction
+    virtual void
+        DrawParallelLine(wxDC& dc, int start, int end, int pos) const = 0;
+
+
+    // Return the row or column at the given pixel coordinate.
+    virtual int
+        PosToLine(const wxGrid *grid, int pos, bool clip = false) const = 0;
+
+    // Get the top/left position, in pixels, of the given row or column
+    virtual int GetLineStartPos(const wxGrid *grid, int line) const = 0;
+
+    // Get the bottom/right position, in pixels, of the given row or column
+    virtual int GetLineEndPos(const wxGrid *grid, int line) const = 0;
+
+    // Get the height/width of the given row/column
+    virtual int GetLineSize(const wxGrid *grid, int line) const = 0;
+
+    // Get wxGrid::m_rowBottoms/m_colRights array
+    virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const = 0;
+
+    // Get default height row height or column width
+    virtual int GetDefaultLineSize(const wxGrid *grid) const = 0;
+
+    // Return the minimal acceptable row height or column width
+    virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const = 0;
+
+    // Return the minimal row height or column width
+    virtual int GetMinimalLineSize(const wxGrid *grid, int line) const = 0;
+
+    // Set the row height or column width
+    virtual void SetLineSize(wxGrid *grid, int line, int size) const = 0;
+
+    // True if rows/columns can be resized by user
+    virtual bool CanResizeLines(const wxGrid *grid) const = 0;
+
+
+    // Return the index of the line at the given position
+    //
+    // NB: currently this is always identity for the rows as reordering is only
+    //     implemented for the lines
+    virtual int GetLineAt(const wxGrid *grid, int line) const = 0;
+
+
+    // Get the row or column label window
+    virtual wxWindow *GetHeaderWindow(wxGrid *grid) const = 0;
+
+    // Get the width or height of the row or column label window
+    virtual int GetHeaderWindowSize(wxGrid *grid) const = 0;
+
+
+    // This class is never used polymorphically but give it a virtual dtor
+    // anyhow to suppress g++ complaints about it
+    virtual ~wxGridOperations() { }
+};
+
+class wxGridRowOperations : public wxGridOperations
+{
+public:
+    virtual wxGridOperations& Dual() const;
+
+    virtual int GetNumberOfLines(const wxGrid *grid) const
+        { return grid->GetNumberRows(); }
+
+    virtual wxGrid::wxGridSelectionModes GetSelectionMode() const
+        { return wxGrid::wxGridSelectRows; }
+
+    virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const
+        { return wxGridCellCoords(thisDir, otherDir); }
+
+    virtual int CalcScrolledPosition(wxGrid *grid, int pos) const
+        { return grid->CalcScrolledPosition(wxPoint(pos, 0)).x; }
+
+    virtual int Select(const wxGridCellCoords& c) const { return c.GetRow(); }
+    virtual int Select(const wxPoint& pt) const { return pt.x; }
+    virtual int Select(const wxSize& sz) const { return sz.x; }
+    virtual int Select(const wxRect& r) const { return r.x; }
+    virtual int& Select(wxRect& r) const { return r.x; }
+    virtual int& SelectSize(wxRect& r) const { return r.width; }
+    virtual wxSize MakeSize(int first, int second) const
+        { return wxSize(first, second); }
+    virtual void Set(wxGridCellCoords& coords, int line) const
+        { coords.SetRow(line); }
+
+    virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const
+        { dc.DrawLine(start, pos, end, pos); }
+
+    virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const
+        { return grid->YToRow(pos, clip); }
+    virtual int GetLineStartPos(const wxGrid *grid, int line) const
+        { return grid->GetRowTop(line); }
+    virtual int GetLineEndPos(const wxGrid *grid, int line) const
+        { return grid->GetRowBottom(line); }
+    virtual int GetLineSize(const wxGrid *grid, int line) const
+        { return grid->GetRowHeight(line); }
+    virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const
+        { return grid->m_rowBottoms; }
+    virtual int GetDefaultLineSize(const wxGrid *grid) const
+        { return grid->GetDefaultRowSize(); }
+    virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const
+        { return grid->GetRowMinimalAcceptableHeight(); }
+    virtual int GetMinimalLineSize(const wxGrid *grid, int line) const
+        { return grid->GetRowMinimalHeight(line); }
+    virtual void SetLineSize(wxGrid *grid, int line, int size) const
+        { grid->SetRowSize(line, size); }
+    virtual bool CanResizeLines(const wxGrid *grid) const
+        { return grid->CanDragRowSize(); }
+
+    virtual int GetLineAt(const wxGrid * WXUNUSED(grid), int line) const
+        { return line; } // TODO: implement row reordering
+
+    virtual wxWindow *GetHeaderWindow(wxGrid *grid) const
+        { return grid->GetGridRowLabelWindow(); }
+    virtual int GetHeaderWindowSize(wxGrid *grid) const
+        { return grid->GetRowLabelSize(); }
+};
+
+class wxGridColumnOperations : public wxGridOperations
+{
+public:
+    virtual wxGridOperations& Dual() const;
+
+    virtual int GetNumberOfLines(const wxGrid *grid) const
+        { return grid->GetNumberCols(); }
+
+    virtual wxGrid::wxGridSelectionModes GetSelectionMode() const
+        { return wxGrid::wxGridSelectColumns; }
+
+    virtual wxGridCellCoords MakeCoords(int thisDir, int otherDir) const
+        { return wxGridCellCoords(otherDir, thisDir); }
+
+    virtual int CalcScrolledPosition(wxGrid *grid, int pos) const
+        { return grid->CalcScrolledPosition(wxPoint(0, pos)).y; }
+
+    virtual int Select(const wxGridCellCoords& c) const { return c.GetCol(); }
+    virtual int Select(const wxPoint& pt) const { return pt.y; }
+    virtual int Select(const wxSize& sz) const { return sz.y; }
+    virtual int Select(const wxRect& r) const { return r.y; }
+    virtual int& Select(wxRect& r) const { return r.y; }
+    virtual int& SelectSize(wxRect& r) const { return r.height; }
+    virtual wxSize MakeSize(int first, int second) const
+        { return wxSize(second, first); }
+    virtual void Set(wxGridCellCoords& coords, int line) const
+        { coords.SetCol(line); }
+
+    virtual void DrawParallelLine(wxDC& dc, int start, int end, int pos) const
+        { dc.DrawLine(pos, start, pos, end); }
+
+    virtual int PosToLine(const wxGrid *grid, int pos, bool clip = false) const
+        { return grid->XToCol(pos, clip); }
+    virtual int GetLineStartPos(const wxGrid *grid, int line) const
+        { return grid->GetColLeft(line); }
+    virtual int GetLineEndPos(const wxGrid *grid, int line) const
+        { return grid->GetColRight(line); }
+    virtual int GetLineSize(const wxGrid *grid, int line) const
+        { return grid->GetColWidth(line); }
+    virtual const wxArrayInt& GetLineEnds(const wxGrid *grid) const
+        { return grid->m_colRights; }
+    virtual int GetDefaultLineSize(const wxGrid *grid) const
+        { return grid->GetDefaultColSize(); }
+    virtual int GetMinimalAcceptableLineSize(const wxGrid *grid) const
+        { return grid->GetColMinimalAcceptableWidth(); }
+    virtual int GetMinimalLineSize(const wxGrid *grid, int line) const
+        { return grid->GetColMinimalWidth(line); }
+    virtual void SetLineSize(wxGrid *grid, int line, int size) const
+        { grid->SetColSize(line, size); }
+    virtual bool CanResizeLines(const wxGrid *grid) const
+        { return grid->CanDragColSize(); }
+
+    virtual int GetLineAt(const wxGrid *grid, int line) const
+        { return grid->GetColAt(line); }
+
+    virtual wxWindow *GetHeaderWindow(wxGrid *grid) const
+        { return grid->GetGridColLabelWindow(); }
+    virtual int GetHeaderWindowSize(wxGrid *grid) const
+        { return grid->GetColLabelSize(); }
+};
+
+wxGridOperations& wxGridRowOperations::Dual() const
+{
+    static wxGridColumnOperations s_colOper;
+
+    return s_colOper;
+}
+
+wxGridOperations& wxGridColumnOperations::Dual() const
+{
+    static wxGridRowOperations s_rowOper;
+
+    return s_rowOper;
+}
+
+// This class abstracts the difference between operations going forward
+// (down/right) and backward (up/left) and allows to use the same code for
+// functions which differ only in the direction of grid traversal
+//
+// Like wxGridOperations it's an ABC with two concrete subclasses below. Unlike
+// it, this is a normal object and not just a function dispatch table and has a
+// non-default ctor.
+//
+// Note: the explanation of this discrepancy is the existence of (very useful)
+// Dual() method in wxGridOperations which forces us to make wxGridOperations a
+// function dispatcher only.
+class wxGridDirectionOperations
+{
+public:
+    // The oper parameter to ctor selects whether we work with rows or columns
+    wxGridDirectionOperations(wxGrid *grid, const wxGridOperations& oper)
+        : m_grid(grid),
+          m_oper(oper)
+    {
+    }
+
+    // Check if the component of this point in our direction is at the
+    // boundary, i.e. is the first/last row/column
+    virtual bool IsAtBoundary(const wxGridCellCoords& coords) const = 0;
+
+    // Increment the component of this point in our direction
+    virtual void Advance(wxGridCellCoords& coords) const = 0;
+
+    // Find the line at the given distance, in pixels, away from this one
+    // (this uses clipping, i.e. anything after the last line is counted as the
+    // last one and anything before the first one as 0)
+    virtual int MoveByPixelDistance(int line, int distance) const = 0;
+
+    // This class is never used polymorphically but give it a virtual dtor
+    // anyhow to suppress g++ complaints about it
+    virtual ~wxGridDirectionOperations() { }
+
+protected:
+    wxGrid * const m_grid;
+    const wxGridOperations& m_oper;
+};
+
+class wxGridBackwardOperations : public wxGridDirectionOperations
+{
+public:
+    wxGridBackwardOperations(wxGrid *grid, const wxGridOperations& oper)
+        : wxGridDirectionOperations(grid, oper)
+    {
+    }
+
+    virtual bool IsAtBoundary(const wxGridCellCoords& coords) const
+    {
+        wxASSERT_MSG( m_oper.Select(coords) >= 0, "invalid row/column" );
+
+        return m_oper.Select(coords) == 0;
+    }
+
+    virtual void Advance(wxGridCellCoords& coords) const
+    {
+        wxASSERT( !IsAtBoundary(coords) );
+
+        m_oper.Set(coords, m_oper.Select(coords) - 1);
+    }
+
+    virtual int MoveByPixelDistance(int line, int distance) const
+    {
+        int pos = m_oper.GetLineStartPos(m_grid, line);
+        return m_oper.PosToLine(m_grid, pos - distance + 1, true);
+    }
+};
+
+class wxGridForwardOperations : public wxGridDirectionOperations
+{
+public:
+    wxGridForwardOperations(wxGrid *grid, const wxGridOperations& oper)
+        : wxGridDirectionOperations(grid, oper),
+          m_numLines(oper.GetNumberOfLines(grid))
+    {
+    }
+
+    virtual bool IsAtBoundary(const wxGridCellCoords& coords) const
+    {
+        wxASSERT_MSG( m_oper.Select(coords) < m_numLines, "invalid row/column" );
+
+        return m_oper.Select(coords) == m_numLines - 1;
+    }
+
+    virtual void Advance(wxGridCellCoords& coords) const
+    {
+        wxASSERT( !IsAtBoundary(coords) );
+
+        m_oper.Set(coords, m_oper.Select(coords) + 1);
+    }
+
+    virtual int MoveByPixelDistance(int line, int distance) const
+    {
+        int pos = m_oper.GetLineStartPos(m_grid, line);
+        return m_oper.PosToLine(m_grid, pos + distance, true);
+    }
+
+private:
+    const int m_numLines;
+};
+
 // ----------------------------------------------------------------------------
 // globals
 // ----------------------------------------------------------------------------
@@ -439,6 +793,23 @@ static const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X;
 // in these hash tables is the number of rows/columns)
 static const int GRID_HASH_SIZE = 100;
 
+// ----------------------------------------------------------------------------
+// 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
 // ============================================================================
@@ -2634,7 +3005,7 @@ void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col)
 
 wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const
 {
-    wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
+    wxGridCellAttr *attr = NULL;
 
     int n = FindIndex(row, col);
     if ( n != wxNOT_FOUND )
@@ -2744,7 +3115,7 @@ wxGridRowOrColAttrData::~wxGridRowOrColAttrData()
 
 wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const
 {
-    wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
+    wxGridCellAttr *attr = NULL;
 
     int n = m_rowsOrCols.Index(rowOrCol);
     if ( n != wxNOT_FOUND )
@@ -2830,7 +3201,7 @@ void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols
 
 wxGridCellAttrProvider::wxGridCellAttrProvider()
 {
-    m_data = (wxGridCellAttrProviderData *)NULL;
+    m_data = NULL;
 }
 
 wxGridCellAttrProvider::~wxGridCellAttrProvider()
@@ -2846,7 +3217,7 @@ void wxGridCellAttrProvider::InitData()
 wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col,
                                                 wxGridCellAttr::wxAttrKind  kind ) const
 {
-    wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
+    wxGridCellAttr *attr = NULL;
     if ( m_data )
     {
         switch (kind)
@@ -3148,8 +3519,8 @@ IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject )
 
 wxGridTableBase::wxGridTableBase()
 {
-    m_view = (wxGrid *) NULL;
-    m_attrProvider = (wxGridCellAttrProvider *) NULL;
+    m_view = NULL;
+    m_attrProvider = NULL;
 }
 
 wxGridTableBase::~wxGridTableBase()
@@ -3179,7 +3550,7 @@ wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAtt
     if ( m_attrProvider )
         return m_attrProvider->GetAttr(row, col, kind);
     else
-        return (wxGridCellAttr *)NULL;
+        return NULL;
 }
 
 void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col)
@@ -3378,7 +3749,7 @@ void  wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
 
 wxGridTableMessage::wxGridTableMessage()
 {
-    m_table = (wxGridTableBase *) NULL;
+    m_table =  NULL;
     m_id = -1;
     m_comInt1 = -1;
     m_comInt2 = -1;
@@ -4040,23 +4411,8 @@ void wxGridWindow::OnFocus(wxFocusEvent& event)
         event.Skip();
 }
 
-//////////////////////////////////////////////////////////////////////
-
-// Internal Helper function for computing row or column from some
-// (unscrolled) coordinate value, using either
-// m_defaultRowHeight/m_defaultColWidth or binary search on array
-// of m_rowBottoms/m_ColRights to speed up the search!
-
-// Internal helper macros for simpler use of that function
-
-static int CoordToRowOrCol(int coord, int defaultDist, int minDist,
-                           const wxArrayInt& BorderArray, int nMax,
-                           bool clipToMinMax);
-
 #define internalXToCol(x) XToCol(x, true)
-#define internalYToRow(y) CoordToRowOrCol(y, m_defaultRowHeight, \
-                                          m_minAcceptableRowHeight, \
-                                          m_rowBottoms, m_numRows, true)
+#define internalYToRow(y) YToRow(y, true)
 
 /////////////////////////////////////////////////////////////////////
 
@@ -4279,28 +4635,16 @@ void wxGrid::Create()
 }
 
 bool wxGrid::CreateGrid( int numRows, int numCols,
-                         wxGrid::wxGridSelectionModes selmode )
+                         wxGridSelectionModes selmode )
 {
     wxCHECK_MSG( !m_created,
                  false,
                  wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
 
-    m_numRows = numRows;
-    m_numCols = numCols;
-
-    m_table = new wxGridStringTable( m_numRows, m_numCols );
-    m_table->SetView( this );
-    m_ownTable = true;
-    m_selection = new wxGridSelection( this, selmode );
-
-    CalcDimensions();
-
-    m_created = true;
-
-    return m_created;
+    return SetTable(new wxGridStringTable(numRows, numCols), true, selmode);
 }
 
-void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode)
+void wxGrid::SetSelectionMode(wxGridSelectionModes selmode)
 {
     wxCHECK_RET( m_created,
                  wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") );
@@ -4310,14 +4654,16 @@ void wxGrid::SetSelectionMode(wxGrid::wxGridSelectionModes selmode)
 
 wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const
 {
-    wxCHECK_MSG( m_created, wxGrid::wxGridSelectCells,
+    wxCHECK_MSG( m_created, wxGridSelectCells,
                  wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") );
 
     return m_selection->GetSelectionMode();
 }
 
-bool wxGrid::SetTable( wxGridTableBase *table, bool takeOwnership,
-                       wxGrid::wxGridSelectionModes selmode )
+bool
+wxGrid::SetTable(wxGridTableBase *table,
+                 bool takeOwnership,
+                 wxGrid::wxGridSelectionModes selmode )
 {
     bool checkSelection = false;
     if ( m_created )
@@ -4457,7 +4803,7 @@ void wxGrid::Init()
     m_canDragColMove = false;
 
     m_cursorMode  = WXGRID_CURSOR_SELECT_CELL;
-    m_winCapture = (wxWindow *)NULL;
+    m_winCapture = NULL;
     m_canDragRowSize = true;
     m_canDragColSize = true;
     m_canDragGridSize = true;
@@ -5833,7 +6179,7 @@ void wxGrid::ChangeCursorMode(CursorMode mode,
     {
         if (m_winCapture->HasCapture())
             m_winCapture->ReleaseMouse();
-        m_winCapture = (wxWindow *)NULL;
+        m_winCapture = NULL;
     }
 
     m_cursorMode = mode;
@@ -6251,124 +6597,106 @@ void wxGrid::ProcessGridCellMouseEvent( wxMouseEvent& event )
     }
 }
 
-void wxGrid::DoEndDragResizeRow()
+void wxGrid::DoEndDragResizeLine(const wxGridOperations& oper)
 {
-    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 );
+    if ( m_dragLastPos == -1 )
+        return;
 
-        wxClientDC dc( m_gridWin );
-        PrepareDC( dc );
-        dc.SetLogicalFunction( wxINVERT );
-        dc.DrawLine( left, m_dragLastPos, left + cw, m_dragLastPos );
-        HideCellEditControl();
-        SaveEditControlValue();
+    const wxGridOperations& doper = oper.Dual();
 
-        int rowTop = GetRowTop(m_dragRowOrCol);
-        SetRowSize( m_dragRowOrCol,
-                    wxMax( m_dragLastPos - rowTop, m_minAcceptableRowHeight ) );
+    const wxSize size = m_gridWin->GetClientSize();
 
-        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;
+    const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0));
 
-            // 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 );
-        }
+    // erase the last line we drew
+    wxClientDC dc(m_gridWin);
+    PrepareDC(dc);
+    dc.SetLogicalFunction(wxINVERT);
 
-        ShowCellEditControl();
-    }
-}
+    const int posLineStart = oper.Select(ptOrigin);
+    const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size);
 
+    oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos);
 
-void wxGrid::DoEndDragResizeCol()
-{
-    if ( m_dragLastPos >= 0 )
+    // temporarily hide the edit control before resizing
+    HideCellEditControl();
+    SaveEditControlValue();
+
+    // do resize the line
+    const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol);
+    oper.SetLineSize(this, m_dragRowOrCol,
+                     wxMax(m_dragLastPos - lineStart,
+                           oper.GetMinimalLineSize(this, m_dragRowOrCol)));
+
+    // refresh now if we're not frozen
+    if ( !GetBatchCount() )
     {
-        // erase the last line and resize the col
-        //
-        int cw, ch, dummy, top;
-        m_gridWin->GetClientSize( &cw, &ch );
-        CalcUnscrolledPosition( 0, 0, &dummy, &top );
+        // we need to refresh everything beyond the resized line in the header
+        // window
 
-        wxClientDC dc( m_gridWin );
-        PrepareDC( dc );
-        dc.SetLogicalFunction( wxINVERT );
-        dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch );
-        HideCellEditControl();
-        SaveEditControlValue();
+        // get the position from which to refresh in the other direction
+        wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0)));
+        rect.SetPosition(CalcScrolledPosition(rect.GetPosition()));
 
-        int colLeft = GetColLeft(m_dragRowOrCol);
-        SetColSize( m_dragRowOrCol,
-                    wxMax( m_dragLastPos - colLeft,
-                           GetColMinimalWidth(m_dragRowOrCol) ) );
+        // we only need the ordinate (for rows) or abscissa (for columns) here,
+        // and need to cover the entire window in the other direction
+        oper.Select(rect) = 0;
 
-        if ( !GetBatchCount() )
+        wxRect rectHeader(rect.GetPosition(),
+                          oper.MakeSize
+                               (
+                                    oper.GetHeaderWindowSize(this),
+                                    doper.Select(size) - doper.Select(rect)
+                               ));
+
+        oper.GetHeaderWindow(this)->Refresh(true, &rectHeader);
+
+
+        // also refresh the grid window: extend the rectangle
+        if ( m_table )
         {
-            // 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;
+            oper.SelectSize(rect) = oper.Select(size);
 
-            // if there is a multicell block, paint all of it
-            if (m_table)
+            int subtractLines = 0;
+            const int lineStart = oper.PosToLine(this, posLineStart);
+            if ( lineStart >= 0 )
             {
-                int i, cell_rows, cell_cols, subtract_cols = 0;
-                int topRow = YToRow(top);
-                int bottomRow = internalYToRow(top + cw);
-                if (topRow >= 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);
+                for ( int line = lineStart; line < lineEnd; line++ )
                 {
-                    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;
+                    int cellLines = oper.Select(
+                        GetCellSize(oper.MakeCoords(m_dragRowOrCol, line)));
+                    if ( cellLines < subtractLines )
+                        subtractLines = cellLines;
                 }
             }
 
-            m_gridWin->Refresh( false, &rect );
-        }
+            int startPos =
+                oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines);
+            startPos = doper.CalcScrolledPosition(this, startPos);
+
+            doper.Select(rect) = startPos;
+            doper.SelectSize(rect) = doper.Select(size) - startPos;
 
-        ShowCellEditControl();
+            m_gridWin->Refresh(false, &rect);
+        }
     }
+
+    // show the edit control back again
+    ShowCellEditControl();
+}
+
+void wxGrid::DoEndDragResizeRow()
+{
+    DoEndDragResizeLine(wxGridRowOperations());
+}
+
+void wxGrid::DoEndDragResizeCol()
+{
+    DoEndDragResizeLine(wxGridColumnOperations());
 }
 
 void wxGrid::DoEndDragMoveCol()
@@ -6505,7 +6833,7 @@ bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
 
 // 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
+// behaviour is to replace all cell contents with wxEmptyString but
 // not to change the number of rows or cols.
 //
 void wxGrid::ClearGrid()
@@ -6521,150 +6849,52 @@ void wxGrid::ClearGrid()
     }
 }
 
-bool wxGrid::InsertRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+bool
+wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t),
+                      int pos, int num, bool WXUNUSED(updateLabels) )
 {
-    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;
-}
+    wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
 
-bool wxGrid::AppendRows( int numRows, bool WXUNUSED(updateLabels) )
-{
-    if ( !m_created )
-    {
-        wxFAIL_MSG( wxT("Called wxGrid::AppendRows() before calling CreateGrid()") );
+    if ( !m_table )
         return false;
-    }
 
-    if ( m_table )
-    {
-        bool done = m_table && m_table->AppendRows( numRows );
-        return done;
+    if ( IsCellEditControlEnabled() )
+        DisableCellEditControl();
 
-        // the table will have sent the results of the append row
-        // operation to this view object as a grid table message
-    }
+    return (m_table->*funcModify)(pos, num);
 
-    return false;
+    // the table will have sent the results of the insert row
+    // operation to this view object as a grid table message
 }
 
-bool wxGrid::DeleteRows( int pos, int numRows, bool WXUNUSED(updateLabels) )
+bool
+wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t),
+                      int num, bool WXUNUSED(updateLabels))
 {
-    if ( !m_created )
-    {
-        wxFAIL_MSG( wxT("Called wxGrid::DeleteRows() before calling CreateGrid()") );
-        return false;
-    }
-
-    if ( m_table )
-    {
-        if (IsCellEditControlEnabled())
-            DisableCellEditControl();
+    wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
 
-        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
-    }
+    if ( !m_table )
+        return false;
 
-    return false;
+    return (m_table->*funcAppend)(num);
 }
 
-bool wxGrid::InsertCols( int pos, int numCols, bool WXUNUSED(updateLabels) )
+//
+// ----- 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 )
 {
-    if ( !m_created )
-    {
-        wxFAIL_MSG( wxT("Called wxGrid::InsertCols() before calling CreateGrid()") );
-        return false;
-    }
+   bool claimed, vetoed;
 
-    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) )
-{
-    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) )
-{
-    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, vetoed;
-
-   if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
-   {
-       int rowOrCol = (row == -1 ? col : row);
+   if ( type == wxEVT_GRID_ROW_SIZE || type == wxEVT_GRID_COL_SIZE )
+   {
+       int rowOrCol = (row == -1 ? col : row);
 
        wxGridSizeEvent gridEvt( GetId(),
                type,
@@ -7189,9 +7419,8 @@ void wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
     attr->DecRef();
 }
 
-void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCol )
+void wxGrid::HighlightBlock(int topRow, int leftCol, int bottomRow, int rightCol)
 {
-    int temp;
     wxGridCellCoords updateTopLeft, updateBottomRight;
 
     if ( m_selection )
@@ -7208,19 +7437,8 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo
         }
     }
 
-    if ( topRow > bottomRow )
-    {
-        temp = topRow;
-        topRow = bottomRow;
-        bottomRow = temp;
-    }
-
-    if ( leftCol > rightCol )
-    {
-        temp = leftCol;
-        leftCol = rightCol;
-        rightCol = temp;
-    }
+    EnsureFirstLessThanSecond(topRow, bottomRow);
+    EnsureFirstLessThanSecond(leftCol, rightCol);
 
     updateTopLeft = wxGridCellCoords( topRow, leftCol );
     updateBottomRight = wxGridCellCoords( bottomRow, rightCol );
@@ -7257,30 +7475,10 @@ void wxGrid::HighlightBlock( int topRow, int leftCol, int bottomRow, int rightCo
         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;
-        }
+        EnsureFirstLessThanSecond(oldLeft, leftCol);
+        EnsureFirstLessThanSecond(oldTop, topRow);
+        EnsureFirstLessThanSecond(rightCol, oldRight);
+        EnsureFirstLessThanSecond(bottomRow, oldBottom);
 
         // Now, either the stuff marked old is the outer
         // rectangle or we don't have a situation where one
@@ -7614,27 +7812,6 @@ void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
         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
 }
 
 wxPen wxGrid::GetDefaultGridLinePen()
@@ -8177,8 +8354,6 @@ bool wxGrid::Enable(bool enable)
 
 void wxGrid::EnableEditing( bool edit )
 {
-    // TODO: improve this ?
-    //
     if ( edit != m_editable )
     {
         if (!edit)
@@ -8482,188 +8657,145 @@ void wxGrid::XYToCell( int x, int y, wxGridCellCoords& coords ) const
     }
 }
 
-// Internal Helper function for computing row or column from some
-// (unscrolled) coordinate value, using either
-// m_defaultRowHeight/m_defaultColWidth or binary search on array
-// of m_rowBottoms/m_ColRights to speed up the search!
-
-static int CoordToRowOrCol(int coord, int defaultDist, int minDist,
-                           const wxArrayInt& BorderArray, int nMax,
-                           bool clipToMinMax)
+// compute row or column from some (unscrolled) coordinate value, using either
+// m_defaultRowHeight/m_defaultColWidth or binary search on array of
+// m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used
+// for large grids)
+int
+wxGrid::PosToLine(int coord,
+                  bool clipToMinMax,
+                  const wxGridOperations& oper) const
 {
-    if (coord < 0)
-        return clipToMinMax && (nMax > 0) ? 0 : -1;
+    const int numLines = oper.GetNumberOfLines(this);
 
-    if (!defaultDist)
-        defaultDist = 1;
+    if ( coord < 0 )
+        return clipToMinMax && numLines > 0 ? oper.GetLineAt(this, 0) : -1;
 
-    size_t i_max = coord / defaultDist,
-           i_min = 0;
+    const int defaultLineSize = oper.GetDefaultLineSize(this);
+    wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
 
-    if (BorderArray.IsEmpty())
-    {
-        if ((int) i_max < nMax)
-            return i_max;
-        return clipToMinMax ? nMax - 1 : -1;
-    }
+    int maxPos = coord / defaultLineSize,
+        minPos = 0;
 
-    if ( i_max >= BorderArray.GetCount())
-    {
-        i_max = BorderArray.GetCount() - 1;
-    }
-    else
+    // check for the simplest case: if we have no explicit line sizes
+    // configured, then we already know the line this position falls in
+    const wxArrayInt& lineEnds = oper.GetLineEnds(this);
+    if ( lineEnds.empty() )
     {
-        if ( coord >= BorderArray[i_max])
-        {
-            i_min = i_max;
-            if (minDist)
-                i_max = coord / minDist;
-            else
-                i_max =  BorderArray.GetCount() - 1;
-        }
-
-        if ( i_max >= BorderArray.GetCount())
-            i_max = BorderArray.GetCount() - 1;
-    }
+        if ( maxPos < numLines )
+            return maxPos;
 
-    if ( coord >= BorderArray[i_max])
-        return clipToMinMax ? (int)i_max : -1;
-    if ( coord < BorderArray[0] )
-        return 0;
-
-    while ( i_max - i_min > 0 )
-    {
-        wxCHECK_MSG(BorderArray[i_min] <= coord && coord < BorderArray[i_max],
-                    0, _T("wxGrid: internal error in CoordToRowOrCol"));
-        if (coord >=  BorderArray[ i_max - 1])
-            return i_max;
-        else
-            i_max--;
-        int median = i_min + (i_max - i_min + 1) / 2;
-        if (coord < BorderArray[median])
-            i_max = median;
-        else
-            i_min = median;
+        return clipToMinMax ? numLines - 1 : -1;
     }
 
-    return i_max;
-}
-
-int wxGrid::YToRow( int y ) const
-{
-    return CoordToRowOrCol(y, m_defaultRowHeight,
-                           m_minAcceptableRowHeight, m_rowBottoms, m_numRows, false);
-}
-
-int wxGrid::XToCol( int x, bool clipToMinMax ) const
-{
-    if (x < 0)
-        return clipToMinMax && (m_numCols > 0) ? GetColAt( 0 ) : -1;
-
-    wxASSERT_MSG(m_defaultColWidth > 0, wxT("Default column width can not be zero"));
 
-    int maxPos = x / m_defaultColWidth;
-    int minPos = 0;
-
-    if (m_colRights.IsEmpty())
+    // adjust maxPos before starting the binary search
+    if ( maxPos >= numLines )
     {
-        if(maxPos < m_numCols)
-            return GetColAt( maxPos );
-        return clipToMinMax ? GetColAt( m_numCols - 1 ) : -1;
+        maxPos = numLines  - 1;
     }
-
-    if ( maxPos >= m_numCols)
-        maxPos = m_numCols - 1;
     else
     {
-        if ( x >= m_colRights[GetColAt( maxPos )])
+        if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)])
         {
             minPos = maxPos;
-            if (m_minAcceptableColWidth)
-                maxPos = x / m_minAcceptableColWidth;
+            const int minDist = oper.GetMinimalAcceptableLineSize(this);
+            if ( minDist )
+                maxPos = coord / minDist;
             else
-                maxPos =  m_numCols - 1;
+                maxPos = numLines - 1;
         }
-        if ( maxPos >= m_numCols)
-            maxPos = m_numCols - 1;
+
+        if ( maxPos >= numLines )
+            maxPos = numLines  - 1;
     }
 
-    //X is beyond the last column
-    if ( x >= m_colRights[GetColAt( maxPos )])
-        return clipToMinMax ? GetColAt( maxPos ) : -1;
+    // check if the position is beyond the last column
+    const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
+    if ( coord >= lineEnds[lineAtMaxPos] )
+        return clipToMinMax ? lineAtMaxPos : -1;
 
-    //X is before the first column
-    if ( x < m_colRights[GetColAt( 0 )] )
-        return GetColAt( 0 );
+    // or before the first one
+    const int lineAt0 = oper.GetLineAt(this, 0);
+    if ( coord < lineEnds[lineAt0] )
+        return lineAt0;
 
-    //Perform a binary search
-    while ( maxPos - minPos > 0 )
+
+    // finally do perform the binary search
+    while ( minPos < maxPos )
     {
-        wxCHECK_MSG(m_colRights[GetColAt( minPos )] <= x && x < m_colRights[GetColAt( maxPos )],
-                    0, _T("wxGrid: internal error in XToCol"));
+        wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
+                        coord < lineEnds[oper.GetLineAt(this, maxPos)],
+                     -1,
+                     "wxGrid: internal error in PosToLine()" );
 
-        if (x >=  m_colRights[GetColAt( maxPos - 1 )])
-            return GetColAt( maxPos );
+        if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
+            return oper.GetLineAt(this, maxPos);
         else
             maxPos--;
-        int median = minPos + (maxPos - minPos + 1) / 2;
-        if (x < m_colRights[GetColAt( median )])
+
+        const int median = minPos + (maxPos - minPos + 1) / 2;
+        if ( coord < lineEnds[oper.GetLineAt(this, median)] )
             maxPos = median;
         else
             minPos = median;
     }
-    return GetColAt( maxPos );
+
+    return oper.GetLineAt(this, maxPos);
+}
+
+int wxGrid::YToRow(int y, bool clipToMinMax) const
+{
+    return PosToLine(y, clipToMinMax, wxGridRowOperations());
 }
 
-// return the row number that that the y coord is near
-//  the edge of, or -1 if not near an edge.
+int wxGrid::XToCol(int x, bool clipToMinMax) const
+{
+    return PosToLine(x, clipToMinMax, wxGridColumnOperations());
+}
+
+// return the row number that that the y coord is near the edge of, or -1 if
+// not near an edge.
+//
 // coords can only possibly be near an edge if
 //    (a) the row/column is large enough to still allow for an "inner" area
-//        that is _not_ nead the edge (i.e., if the height/width is smaller
+//        that is _not_ near the edge (i.e., if the height/width is smaller
 //        than WXGRID_LABEL_EDGE_ZONE, coords are _never_ considered to be
 //        near the edge).
 //   and
 //    (b) resizing rows/columns (the thing for which edge detection is
 //        relevant at all) is enabled.
 //
-int wxGrid::YToEdgeOfRow( int y ) const
+int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
 {
-    int i;
-    i = internalYToRow(y);
+    if ( !oper.CanResizeLines(this) )
+        return -1;
 
-    if ( GetRowHeight(i) > WXGRID_LABEL_EDGE_ZONE && CanDragRowSize() )
+    const int line = oper.PosToLine(this, pos, true);
+
+    if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE )
     {
-        // We know that we are in row i, test whether we are
-        // close enough to lower or upper border, respectively.
-        if ( abs(GetRowBottom(i) - y) < WXGRID_LABEL_EDGE_ZONE )
-            return i;
-        else if ( i > 0 && y - GetRowTop(i) < WXGRID_LABEL_EDGE_ZONE )
-            return i - 1;
+        // We know that we are in this line, test whether we are close enough
+        // to start or end border, respectively.
+        if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE )
+            return line;
+        else if ( line > 0 &&
+                    pos - oper.GetLineStartPos(this,
+                                               line) < WXGRID_LABEL_EDGE_ZONE )
+            return line - 1;
     }
 
     return -1;
 }
 
-// return the col number that that the x coord is near the edge of, or
-// -1 if not near an edge
-// See comment at YToEdgeOfRow for conditions on edge detection.
-//
-int wxGrid::XToEdgeOfCol( int x ) const
+int wxGrid::YToEdgeOfRow(int y) const
 {
-    int i;
-    i = internalXToCol(x);
-
-    if ( GetColWidth(i) > WXGRID_LABEL_EDGE_ZONE && CanDragColSize() )
-    {
-        // We know that we are in column i; test whether we are
-        // close enough to right or left border, respectively.
-        if ( abs(GetColRight(i) - x) < WXGRID_LABEL_EDGE_ZONE )
-            return i;
-        else if ( i > 0 && x - GetColLeft(i) < WXGRID_LABEL_EDGE_ZONE )
-            return i - 1;
-    }
+    return PosToEdgeOfLine(y, wxGridRowOperations());
+}
 
-    return -1;
+int wxGrid::XToEdgeOfCol(int x) const
+{
+    return PosToEdgeOfLine(x, wxGridColumnOperations());
 }
 
 wxRect wxGrid::CellToRect( int row, int col ) const
@@ -8816,463 +8948,200 @@ void wxGrid::MakeCellVisible( int row, int col )
 // ------ Grid cursor movement functions
 //
 
-bool wxGrid::MoveCursorUp( bool expandSelection )
+bool
+wxGrid::DoMoveCursor(bool expandSelection,
+                     const wxGridDirectionOperations& diroper)
 {
-    if ( m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetRow() >= 0 )
+    if ( m_currentCellCoords == wxGridNoCellCoords )
+        return false;
+
+    if ( expandSelection )
     {
-        if ( expandSelection )
-        {
-            if ( m_selectingKeyboard == wxGridNoCellCoords )
-                m_selectingKeyboard = m_currentCellCoords;
-            if ( m_selectingKeyboard.GetRow() > 0 )
-            {
-                m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() - 1 );
-                MakeCellVisible( m_selectingKeyboard.GetRow(),
-                                 m_selectingKeyboard.GetCol() );
-                HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-            }
-        }
-        else if ( m_currentCellCoords.GetRow() > 0 )
-        {
-            int row = m_currentCellCoords.GetRow() - 1;
-            int col = m_currentCellCoords.GetCol();
-            ClearSelection();
-            MakeCellVisible( row, col );
-            SetCurrentCell( row, col );
-        }
-        else
-            return false;
+        if ( m_selectingKeyboard == wxGridNoCellCoords )
+            m_selectingKeyboard = m_currentCellCoords;
 
-        return true;
-    }
+        if ( diroper.IsAtBoundary(m_selectingKeyboard) )
+            return false;
 
-    return false;
-}
+        diroper.Advance(m_selectingKeyboard);
 
-bool wxGrid::MoveCursorDown( bool expandSelection )
-{
-    if ( m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetRow() < m_numRows )
+        MakeCellVisible(m_selectingKeyboard);
+        HighlightBlock(m_currentCellCoords, m_selectingKeyboard);
+    }
+    else
     {
-        if ( expandSelection )
-        {
-            if ( m_selectingKeyboard == wxGridNoCellCoords )
-                m_selectingKeyboard = m_currentCellCoords;
-            if ( m_selectingKeyboard.GetRow() < m_numRows - 1 )
-            {
-                m_selectingKeyboard.SetRow( m_selectingKeyboard.GetRow() + 1 );
-                MakeCellVisible( m_selectingKeyboard.GetRow(),
-                        m_selectingKeyboard.GetCol() );
-                HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-            }
-        }
-        else if ( m_currentCellCoords.GetRow() < m_numRows - 1 )
-        {
-            int row = m_currentCellCoords.GetRow() + 1;
-            int col = m_currentCellCoords.GetCol();
-            ClearSelection();
-            MakeCellVisible( row, col );
-            SetCurrentCell( row, col );
-        }
-        else
+        if ( diroper.IsAtBoundary(m_currentCellCoords) )
             return false;
 
-        return true;
+        ClearSelection();
+
+        wxGridCellCoords coords = m_currentCellCoords;
+        diroper.Advance(coords);
+        MakeCellVisible(coords);
+        SetCurrentCell(coords);
     }
 
-    return false;
+    return true;
 }
 
-bool wxGrid::MoveCursorLeft( bool expandSelection )
+bool wxGrid::MoveCursorUp(bool expandSelection)
 {
-    if ( m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetCol() >= 0 )
-    {
-        if ( expandSelection )
-        {
-            if ( m_selectingKeyboard == wxGridNoCellCoords )
-                m_selectingKeyboard = m_currentCellCoords;
-            if ( m_selectingKeyboard.GetCol() > 0 )
-            {
-                m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() - 1 );
-                MakeCellVisible( m_selectingKeyboard.GetRow(),
-                        m_selectingKeyboard.GetCol() );
-                HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-            }
-        }
-        else if ( GetColPos( m_currentCellCoords.GetCol() ) > 0 )
-        {
-            int row = m_currentCellCoords.GetRow();
-            int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) - 1 );
-            ClearSelection();
-
-            MakeCellVisible( row, col );
-            SetCurrentCell( row, col );
-        }
-        else
-            return false;
-
-        return true;
-    }
-
-    return false;
+    return DoMoveCursor(expandSelection,
+                        wxGridBackwardOperations(this, wxGridRowOperations()));
 }
 
-bool wxGrid::MoveCursorRight( bool expandSelection )
+bool wxGrid::MoveCursorDown(bool expandSelection)
 {
-    if ( m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetCol() < m_numCols )
-    {
-        if ( expandSelection )
-        {
-            if ( m_selectingKeyboard == wxGridNoCellCoords )
-                m_selectingKeyboard = m_currentCellCoords;
-            if ( m_selectingKeyboard.GetCol() < m_numCols - 1 )
-            {
-                m_selectingKeyboard.SetCol( m_selectingKeyboard.GetCol() + 1 );
-                MakeCellVisible( m_selectingKeyboard.GetRow(),
-                        m_selectingKeyboard.GetCol() );
-                HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-            }
-        }
-        else if ( GetColPos( m_currentCellCoords.GetCol() ) < m_numCols - 1 )
-        {
-            int row = m_currentCellCoords.GetRow();
-            int col = GetColAt( GetColPos( m_currentCellCoords.GetCol() ) + 1 );
-            ClearSelection();
-
-            MakeCellVisible( row, col );
-            SetCurrentCell( row, col );
-        }
-        else
-            return false;
+    return DoMoveCursor(expandSelection,
+                        wxGridForwardOperations(this, wxGridRowOperations()));
+}
 
-        return true;
-    }
+bool wxGrid::MoveCursorLeft(bool expandSelection)
+{
+    return DoMoveCursor(expandSelection,
+                        wxGridBackwardOperations(this, wxGridColumnOperations()));
+}
 
-    return false;
+bool wxGrid::MoveCursorRight(bool expandSelection)
+{
+    return DoMoveCursor(expandSelection,
+                        wxGridForwardOperations(this, wxGridColumnOperations()));
 }
 
-bool wxGrid::MovePageUp()
+bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper)
 {
     if ( m_currentCellCoords == wxGridNoCellCoords )
         return false;
 
-    int row = m_currentCellCoords.GetRow();
-    if ( row > 0 )
-    {
-        int cw, ch;
-        m_gridWin->GetClientSize( &cw, &ch );
-
-        int y = GetRowTop(row);
-        int newRow = internalYToRow( y - ch + 1 );
+    if ( diroper.IsAtBoundary(m_currentCellCoords) )
+        return false;
 
-        if ( newRow == row )
-        {
-            // row > 0, so newRow can never be less than 0 here.
-            newRow = row - 1;
-        }
+    const int oldRow = m_currentCellCoords.GetRow();
+    int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y);
+    if ( newRow == oldRow )
+    {
+        wxGridCellCoords coords(m_currentCellCoords);
+        diroper.Advance(coords);
+        newRow = coords.GetRow();
+    }
 
-        MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
-        SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
+    MakeCellVisible(newRow, m_currentCellCoords.GetCol());
+    SetCurrentCell(newRow, m_currentCellCoords.GetCol());
 
-        return true;
-    }
+    return true;
+}
 
-    return false;
+bool wxGrid::MovePageUp()
+{
+    return DoMoveCursorByPage(
+                wxGridBackwardOperations(this, wxGridRowOperations()));
 }
 
 bool wxGrid::MovePageDown()
 {
-    if ( m_currentCellCoords == wxGridNoCellCoords )
-        return false;
+    return DoMoveCursorByPage(
+                wxGridForwardOperations(this, wxGridColumnOperations()));
+}
 
-    int row = m_currentCellCoords.GetRow();
-    if ( (row + 1) < m_numRows )
+// helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper
+// until we find a non-empty cell or reach the grid end
+void
+wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords,
+                              const wxGridDirectionOperations& diroper)
+{
+    while ( !diroper.IsAtBoundary(coords) )
     {
-        int cw, ch;
-        m_gridWin->GetClientSize( &cw, &ch );
-
-        int y = GetRowTop(row);
-        int newRow = internalYToRow( y + ch );
-        if ( newRow == row )
-        {
-            // row < m_numRows, so newRow can't overflow here.
-            newRow = row + 1;
-        }
-
-        MakeCellVisible( newRow, m_currentCellCoords.GetCol() );
-        SetCurrentCell( newRow, m_currentCellCoords.GetCol() );
-
-        return true;
+        diroper.Advance(coords);
+        if ( !m_table->IsEmpty(coords) )
+            break;
     }
-
-    return false;
 }
 
-bool wxGrid::MoveCursorUpBlock( bool expandSelection )
+bool
+wxGrid::DoMoveCursorByBlock(bool expandSelection,
+                            const wxGridDirectionOperations& diroper)
 {
-    if ( m_table &&
-         m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetRow() > 0 )
-    {
-        int row = m_currentCellCoords.GetRow();
-        int col = m_currentCellCoords.GetCol();
-
-        if ( m_table->IsEmptyCell(row, col) )
-        {
-            // starting in an empty cell: find the next block of
-            // non-empty cells
-            //
-            while ( row > 0 )
-            {
-                row--;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else if ( m_table->IsEmptyCell(row - 1, col) )
-        {
-            // starting at the top of a block: find the next block
-            //
-            row--;
-            while ( row > 0 )
-            {
-                row--;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else
-        {
-            // starting within a block: find the top of the block
-            //
-            while ( row > 0 )
-            {
-                row--;
-                if ( m_table->IsEmptyCell(row, col) )
-                {
-                    row++;
-                    break;
-                }
-            }
-        }
+    if ( !m_table || m_currentCellCoords == wxGridNoCellCoords )
+        return false;
 
-        MakeCellVisible( row, col );
-        if ( expandSelection )
-        {
-            m_selectingKeyboard = wxGridCellCoords( row, col );
-            HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-        }
-        else
-        {
-            ClearSelection();
-            SetCurrentCell( row, col );
-        }
+    if ( diroper.IsAtBoundary(m_currentCellCoords) )
+        return false;
 
-        return true;
+    wxGridCellCoords coords(m_currentCellCoords);
+    if ( m_table->IsEmpty(coords) )
+    {
+        // we are in an empty cell: find the next block of non-empty cells
+        AdvanceToNextNonEmpty(coords, diroper);
     }
-
-    return false;
-}
-
-bool wxGrid::MoveCursorDownBlock( bool expandSelection )
-{
-    if ( m_table &&
-         m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetRow() < m_numRows - 1 )
+    else // current cell is not empty
     {
-        int row = m_currentCellCoords.GetRow();
-        int col = m_currentCellCoords.GetCol();
-
-        if ( m_table->IsEmptyCell(row, col) )
+        diroper.Advance(coords);
+        if ( m_table->IsEmpty(coords) )
         {
-            // starting in an empty cell: find the next block of
-            // non-empty cells
-            //
-            while ( row < m_numRows - 1 )
-            {
-                row++;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else if ( m_table->IsEmptyCell(row + 1, col) )
-        {
-            // starting at the bottom of a block: find the next block
-            //
-            row++;
-            while ( row < m_numRows - 1 )
-            {
-                row++;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
+            // we started at the end of a block, find the next one
+            AdvanceToNextNonEmpty(coords, diroper);
         }
-        else
+        else // we're in a middle of a block
         {
-            // starting within a block: find the bottom of the block
-            //
-            while ( row < m_numRows - 1 )
+            // go to the end of it, i.e. find the last cell before the next
+            // empty one
+            while ( !diroper.IsAtBoundary(coords) )
             {
-                row++;
-                if ( m_table->IsEmptyCell(row, col) )
-                {
-                    row--;
+                wxGridCellCoords coordsNext(coords);
+                diroper.Advance(coordsNext);
+                if ( m_table->IsEmpty(coordsNext) )
                     break;
-                }
-            }
-        }
 
-        MakeCellVisible( row, col );
-        if ( expandSelection )
-        {
-            m_selectingKeyboard = wxGridCellCoords( row, col );
-            HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-        }
-        else
-        {
-            ClearSelection();
-            SetCurrentCell( row, col );
+                coords = coordsNext;
+            }
         }
+    }
 
-        return true;
+    MakeCellVisible(coords);
+    if ( expandSelection )
+    {
+        m_selectingKeyboard = coords;
+        HighlightBlock(m_currentCellCoords, m_selectingKeyboard);
+    }
+    else
+    {
+        ClearSelection();
+        SetCurrentCell(coords);
     }
 
-    return false;
+    return true;
 }
 
-bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
+bool wxGrid::MoveCursorUpBlock(bool expandSelection)
 {
-    if ( m_table &&
-         m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetCol() > 0 )
-    {
-        int row = m_currentCellCoords.GetRow();
-        int col = m_currentCellCoords.GetCol();
-
-        if ( m_table->IsEmptyCell(row, col) )
-        {
-            // starting in an empty cell: find the next block of
-            // non-empty cells
-            //
-            while ( col > 0 )
-            {
-                col--;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else if ( m_table->IsEmptyCell(row, col - 1) )
-        {
-            // starting at the left of a block: find the next block
-            //
-            col--;
-            while ( col > 0 )
-            {
-                col--;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else
-        {
-            // starting within a block: find the left of the block
-            //
-            while ( col > 0 )
-            {
-                col--;
-                if ( m_table->IsEmptyCell(row, col) )
-                {
-                    col++;
-                    break;
-                }
-            }
-        }
-
-        MakeCellVisible( row, col );
-        if ( expandSelection )
-        {
-            m_selectingKeyboard = wxGridCellCoords( row, col );
-            HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-        }
-        else
-        {
-            ClearSelection();
-            SetCurrentCell( row, col );
-        }
+    return DoMoveCursorByBlock(
+                expandSelection,
+                wxGridBackwardOperations(this, wxGridRowOperations())
+           );
+}
 
-        return true;
-    }
+bool wxGrid::MoveCursorDownBlock( bool expandSelection )
+{
+    return DoMoveCursorByBlock(
+                expandSelection,
+                wxGridForwardOperations(this, wxGridRowOperations())
+           );
+}
 
-    return false;
+bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
+{
+    return DoMoveCursorByBlock(
+                expandSelection,
+                wxGridBackwardOperations(this, wxGridColumnOperations())
+           );
 }
 
 bool wxGrid::MoveCursorRightBlock( bool expandSelection )
 {
-    if ( m_table &&
-         m_currentCellCoords != wxGridNoCellCoords &&
-         m_currentCellCoords.GetCol() < m_numCols - 1 )
-    {
-        int row = m_currentCellCoords.GetRow();
-        int col = m_currentCellCoords.GetCol();
-
-        if ( m_table->IsEmptyCell(row, col) )
-        {
-            // starting in an empty cell: find the next block of
-            // non-empty cells
-            //
-            while ( col < m_numCols - 1 )
-            {
-                col++;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else if ( m_table->IsEmptyCell(row, col + 1) )
-        {
-            // starting at the right of a block: find the next block
-            //
-            col++;
-            while ( col < m_numCols - 1 )
-            {
-                col++;
-                if ( !(m_table->IsEmptyCell(row, col)) )
-                    break;
-            }
-        }
-        else
-        {
-            // starting within a block: find the right of the block
-            //
-            while ( col < m_numCols - 1 )
-            {
-                col++;
-                if ( m_table->IsEmptyCell(row, col) )
-                {
-                    col--;
-                    break;
-                }
-            }
-        }
-
-        MakeCellVisible( row, col );
-        if ( expandSelection )
-        {
-            m_selectingKeyboard = wxGridCellCoords( row, col );
-            HighlightBlock( m_currentCellCoords, m_selectingKeyboard );
-        }
-        else
-        {
-            ClearSelection();
-            SetCurrentCell( row, col );
-        }
-
-        return true;
-    }
-
-    return false;
+    return DoMoveCursorByBlock(
+                expandSelection,
+                wxGridForwardOperations(this, wxGridColumnOperations())
+           );
 }
 
 //
@@ -9908,7 +9777,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
         if ( !LookupAttr(row, col, &attr) )
         {
             attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any)
-                           : (wxGridCellAttr *)NULL;
+                           : NULL;
             CacheAttr(row, col, attr);
         }
     }
@@ -9928,7 +9797,7 @@ wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
 
 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
 {
-    wxGridCellAttr *attr = (wxGridCellAttr *)NULL;
+    wxGridCellAttr *attr = NULL;
     bool canHave = ((wxGrid*)this)->CanHaveAttributes();
 
     wxCHECK_MSG( canHave, attr, _T("Cell attributes not allowed"));
@@ -10804,46 +10673,40 @@ void wxGrid::SelectAll()
 // cell, row and col deselection
 // ----------------------------------------------------------------------------
 
-void wxGrid::DeselectRow( int row )
+void wxGrid::DeselectLine(int line, const wxGridOperations& oper)
 {
     if ( !m_selection )
         return;
 
-    if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectRows )
+    const wxGridSelectionModes mode = m_selection->GetSelectionMode();
+    if ( mode == oper.GetSelectionMode() )
     {
-        if ( m_selection->IsInSelection(row, 0 ) )
-            m_selection->ToggleCellSelection(row, 0);
+        const wxGridCellCoords c(oper.MakeCoords(line, 0));
+        if ( m_selection->IsInSelection(c) )
+            m_selection->ToggleCellSelection(c);
     }
-    else
+    else if ( mode != oper.Dual().GetSelectionMode() )
     {
-        int nCols = GetNumberCols();
-        for ( int i = 0; i < nCols; i++ )
+        const int nOther = oper.Dual().GetNumberOfLines(this);
+        for ( int i = 0; i < nOther; i++ )
         {
-            if ( m_selection->IsInSelection(row, i ) )
-                m_selection->ToggleCellSelection(row, i);
+            const wxGridCellCoords c(oper.MakeCoords(line, i));
+            if ( m_selection->IsInSelection(c) )
+                m_selection->ToggleCellSelection(c);
         }
     }
+    //else: can only select orthogonal lines so no lines in this direction
+    //      could have been selected anyhow
 }
 
-void wxGrid::DeselectCol( int col )
+void wxGrid::DeselectRow(int row)
 {
-    if ( !m_selection )
-        return;
+    DeselectLine(row, wxGridRowOperations());
+}
 
-    if ( m_selection->GetSelectionMode() == wxGrid::wxGridSelectColumns )
-    {
-        if ( m_selection->IsInSelection(0, col ) )
-            m_selection->ToggleCellSelection(0, col);
-    }
-    else
-    {
-        int nRows = GetNumberRows();
-        for ( int i = 0; i < nRows; i++ )
-        {
-            if ( m_selection->IsInSelection(i, col ) )
-                m_selection->ToggleCellSelection(i, col);
-        }
-    }
+void wxGrid::DeselectCol(int col)
+{
+    DeselectLine(col, wxGridColumnOperations());
 }
 
 void wxGrid::DeselectCell( int row, int col )