]> git.saurik.com Git - wxWidgets.git/commitdiff
start of the great grid folding: introduce wxGridOperations class and use it to avoid...
authorVadim Zeitlin <vadim@wxwidgets.org>
Tue, 16 Sep 2008 08:32:12 +0000 (08:32 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Tue, 16 Sep 2008 08:32:12 +0000 (08:32 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@55652 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/generic/grid.h
include/wx/generic/gridsel.h
src/generic/grid.cpp

index 3ed65db5baa252a5c5f8768b0ff3b1ddc2fd4284..767635a16fcf20f7d3eb61c68d3d481bc7f41efc 100644 (file)
@@ -84,6 +84,10 @@ class WXDLLIMPEXP_FWD_CORE wxTextCtrl;
 class WXDLLIMPEXP_FWD_CORE wxSpinCtrl;
 #endif
 
+class wxGridOperations;
+class wxGridRowOperations;
+class wxGridColumnOperations;
+
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -1264,7 +1268,7 @@ public:
     //  coordinates for mouse events etc.
     //
     void XYToCell( int x, int y, wxGridCellCoords& ) const;
-    int  YToRow( int y ) const;
+    int  YToRow( int y, bool clipToMinMax = false ) const;
     int  XToCol( int x, bool clipToMinMax = false ) const;
 
     int  YToEdgeOfRow( int y ) const;
@@ -1405,6 +1409,12 @@ public:
     bool     GetDefaultCellOverflow() const;
     bool     GetCellOverflow( int row, int col ) const;
     void     GetCellSize( int row, int col, int *num_rows, int *num_cols ) const;
+    wxSize GetCellSize(const wxGridCellCoords& coords)
+    {
+        wxSize s;
+        GetCellSize(coords.GetRow(), coords.GetCol(), &s.x, &s.y);
+        return s;
+    }
 
     void     SetDefaultRowSize( int height, bool resizeExistingRows = false );
     void     SetRowSize( int row, int height );
@@ -1982,8 +1992,13 @@ protected:
     bool    m_canDragColMove;
     bool    m_canDragGridSize;
     bool    m_canDragCell;
+
+    // the last position (horizontal or vertical depending on whether the user
+    // is resizing a column or a row) where a row or column separator line was
+    // dragged by the user or -1 of there is no drag operation in progress
     int     m_dragLastPos;
     int     m_dragRowOrCol;
+
     bool    m_isDragging;
     wxPoint m_startDragPos;
 
@@ -2042,11 +2057,21 @@ protected:
     bool SetModelValues();
 
     friend class WXDLLIMPEXP_FWD_ADV wxGridSelection;
+    friend class wxGridRowOperations;
+    friend class wxGridColumnOperations;
 
 private:
     // implement wxScrolledWindow method to return m_gridWin size
     virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size);
 
+
+    // common implementations of methods defined for both rows and columns
+    void DeselectLine(int line, const wxGridOperations& oper);
+    void DoEndDragResizeLine(const wxGridOperations& oper);
+    int  PosToLine(int pos, bool clipToMinMax,
+                   const wxGridOperations& oper) const;
+
+
     DECLARE_DYNAMIC_CLASS( wxGrid )
     DECLARE_EVENT_TABLE()
     DECLARE_NO_COPY_CLASS(wxGrid)
index fff21a3684d5d8f6638745e9379c4fb2fcbf4380..ab83260f917b9bbfcacae1f4162d1b1468ccf0bb 100644 (file)
 class WXDLLIMPEXP_ADV wxGridSelection
 {
 public:
-    wxGridSelection( wxGrid * grid, wxGrid::wxGridSelectionModes sel =
-                     wxGrid::wxGridSelectCells );
+    wxGridSelection(wxGrid *grid,
+                    wxGrid::wxGridSelectionModes sel = wxGrid::wxGridSelectCells);
+
     bool IsSelection();
-    bool IsInSelection ( int row, int col );
+    bool IsInSelection(int row, int col);
+    bool IsInSelection(const wxGridCellCoords& coords)
+    {
+        return IsInSelection(coords.GetRow(), coords.GetCol());
+    }
+
     void SetSelectionMode(wxGrid::wxGridSelectionModes selmode);
     wxGrid::wxGridSelectionModes GetSelectionMode() { return m_selectionMode; }
     void SelectRow( int row,
@@ -46,6 +52,15 @@ public:
                               bool ControlDown = false,
                               bool ShiftDown = false,
                               bool AltDown = false, bool MetaDown = false );
+    void ToggleCellSelection( const wxGridCellCoords& coords,
+                              bool ControlDown = false,
+                              bool ShiftDown = false,
+                              bool AltDown = false, bool MetaDown = false )
+    {
+        ToggleCellSelection(coords.GetRow(), coords.GetCol(),
+                            ControlDown, ShiftDown, AltDown, MetaDown);
+    }
+
     void ClearSelection();
 
     void UpdateRows( size_t pos, int numRows );
index 3506da69f04928605ee18bdbc86b2effcdf32177..0f8f95ea1b252c770ad2f548db08becfe69cdf44 100644 (file)
@@ -9,6 +9,13 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
+/*
+    TODO:
+
+    - Replace use of wxINVERT with wxOverlay
+    - Make Begin/EndBatch() the same as the generic Freeze/Thaw()
+ */
+
 // For compilers that support precompilation, includes "wx/wx.h".
 #include "wx/wxprec.h"
 
@@ -388,7 +395,7 @@ WX_DEFINE_ARRAY_WITH_DECL_PTR(wxGridDataTypeInfo*, wxGridDataTypeInfoArray,
 class WXDLLIMPEXP_ADV wxGridTypeRegistry
 {
 public:
-  wxGridTypeRegistry() {}
+    wxGridTypeRegistry() {}
     ~wxGridTypeRegistry();
 
     void RegisterDataType(const wxString& typeName,
@@ -414,6 +421,211 @@ 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 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;
+
+
+    // 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(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 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;
+
+
+    // 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;
+};
+
+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 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 DrawParallelLine(wxDC& dc, int start, int end, int pos) const
+        { dc.DrawLine(start, pos, end, pos); }
+
+    virtual int PosToLine(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 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 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 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 DrawParallelLine(wxDC& dc, int start, int end, int pos) const
+        { dc.DrawLine(pos, start, pos, end); }
+
+    virtual int PosToLine(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 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 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;
+}
+
+
 // ----------------------------------------------------------------------------
 // globals
 // ----------------------------------------------------------------------------
@@ -4057,23 +4269,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)
 
 /////////////////////////////////////////////////////////////////////
 
@@ -6258,124 +6455,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()
@@ -8457,137 +8636,101 @@ 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 ( maxPos < numLines )
+            return maxPos;
 
-        if ( i_max >= BorderArray.GetCount())
-            i_max = BorderArray.GetCount() - 1;
+        return clipToMinMax ? numLines - 1 : -1;
     }
 
-    if ( coord >= BorderArray[i_max])
-        return clipToMinMax ? (int)i_max : -1;
-    if ( coord < BorderArray[0] )
-        return 0;
 
-    while ( i_max - i_min > 0 )
+    // adjust maxPos before starting the binary search
+    if ( maxPos >= numLines )
     {
-        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;
+        maxPos = numLines  - 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())
-    {
-        if(maxPos < m_numCols)
-            return GetColAt( maxPos );
-        return clipToMinMax ? GetColAt( m_numCols - 1 ) : -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());
+}
+
+int wxGrid::XToCol(int x, bool clipToMinMax) const
+{
+    return PosToLine(x, clipToMinMax, wxGridColumnOperations());
 }
 
 // return the row number that that the y coord is near
@@ -10779,46 +10922,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 )