]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/vscroll.cpp
Fixed wxRichTextCtrl caret test case
[wxWidgets.git] / src / generic / vscroll.cpp
index cee98cef45b63d912ef3c8854184094b9835c6d5..0572b8e07b30596d9ea5713f9857a14fd618adb4 100644 (file)
@@ -2,7 +2,7 @@
 // Name:        src/generic/vscroll.cpp
 // Purpose:     wxVScrolledWindow implementation
 // Author:      Vadim Zeitlin
-// Modified by: Brad Anderson
+// Modified by: Brad Anderson, David Warkentin
 // Created:     30.05.03
 // RCS-ID:      $Id$
 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
 #pragma hdrstop
 #endif
 
+#ifndef WX_PRECOMP
+    #include "wx/dc.h"
+    #include "wx/sizer.h"
+#endif
+
 #include "wx/vscroll.h"
-#include "wx/sizer.h"
-#include "wx/dc.h"
+
+#include "wx/utils.h"   // For wxMin/wxMax().
+
+// ============================================================================
+// wxVarScrollHelperEvtHandler declaration
+// ============================================================================
 
 // ----------------------------------------------------------------------------
-// event tables
+// wxScrollHelperEvtHandler: intercept the events from the window and forward
+// them to wxVarScrollHelperBase
 // ----------------------------------------------------------------------------
 
-BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
-    EVT_SIZE(wxVScrolledWindow::OnSize)
-    EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
-#if wxUSE_MOUSEWHEEL
-    EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
-#endif
-END_EVENT_TABLE()
+class WXDLLEXPORT wxVarScrollHelperEvtHandler : public wxEvtHandler
+{
+public:
+    wxVarScrollHelperEvtHandler(wxVarScrollHelperBase *scrollHelper)
+    {
+        m_scrollHelper = scrollHelper;
+    }
+
+    virtual bool ProcessEvent(wxEvent& event);
 
+private:
+    wxVarScrollHelperBase *m_scrollHelper;
+
+    wxDECLARE_NO_COPY_CLASS(wxVarScrollHelperEvtHandler);
+};
 
 // ============================================================================
-// implementation
+// wxVarScrollHelperEvtHandler implementation
 // ============================================================================
 
-IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
+bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
+{
+    wxEventType evType = event.GetEventType();
+
+    // pass it on to the real handler
+    bool processed = wxEvtHandler::ProcessEvent(event);
+
+    // always process the size events ourselves, even if the user code handles
+    // them as well, as we need to AdjustScrollbars()
+    //
+    // NB: it is important to do it after processing the event in the normal
+    //     way as HandleOnSize() may generate a wxEVT_SIZE itself if the
+    //     scrollbar[s] (dis)appear and it should be seen by the user code
+    //     after this one
+    if ( evType == wxEVT_SIZE )
+    {
+        m_scrollHelper->HandleOnSize((wxSizeEvent &)event);
+
+        return !event.GetSkipped();
+    }
+
+    if ( processed )
+    {
+        // normally, nothing more to do here - except if we have a command
+        // event
+        if ( event.IsCommandEvent() )
+        {
+            return true;
+        }
+    }
+
+    // reset the skipped flag (which might have been set to true in
+    // ProcessEvent() above) to be able to test it below
+    bool wasSkipped = event.GetSkipped();
+    if ( wasSkipped )
+        event.Skip(false);
+
+    if ( evType == wxEVT_SCROLLWIN_TOP ||
+         evType == wxEVT_SCROLLWIN_BOTTOM ||
+         evType == wxEVT_SCROLLWIN_LINEUP ||
+         evType == wxEVT_SCROLLWIN_LINEDOWN ||
+         evType == wxEVT_SCROLLWIN_PAGEUP ||
+         evType == wxEVT_SCROLLWIN_PAGEDOWN ||
+         evType == wxEVT_SCROLLWIN_THUMBTRACK ||
+         evType == wxEVT_SCROLLWIN_THUMBRELEASE )
+    {
+        m_scrollHelper->HandleOnScroll((wxScrollWinEvent &)event);
+        if ( !event.GetSkipped() )
+        {
+            // it makes sense to indicate that we processed the message as we
+            // did scroll the window (and also notice that wxAutoScrollTimer
+            // relies on our return value for continuous scrolling)
+            processed = true;
+            wasSkipped = false;
+        }
+    }
+#if wxUSE_MOUSEWHEEL
+    else if ( evType == wxEVT_MOUSEWHEEL )
+    {
+        m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event);
+    }
+#endif // wxUSE_MOUSEWHEEL
+
+    event.Skip(wasSkipped);
+
+    return processed;
+}
+
+
+// ============================================================================
+// wxVarScrollHelperBase implementation
+// ============================================================================
 
 // ----------------------------------------------------------------------------
-// initialization
+// wxVarScrollHelperBase initialization
 // ----------------------------------------------------------------------------
 
-void wxVScrolledWindow::Init()
+wxVarScrollHelperBase::wxVarScrollHelperBase(wxWindow *win)
 {
-    // we're initially empty
-    m_lineMax =
-    m_lineFirst = 0;
-
-    // this one should always be strictly positive
-    m_nVisible = 1;
-
-    m_heightTotal = 0;
+    wxASSERT_MSG( win, wxT("associated window can't be NULL in wxVarScrollHelperBase") );
 
 #if wxUSE_MOUSEWHEEL
     m_sumWheelRotation = 0;
 #endif
+
+    m_unitMax = 0;
+    m_sizeTotal = 0;
+    m_unitFirst = 0;
+
+    m_win =
+    m_targetWindow = NULL;
+
+    m_physicalScrolling = true;
+    m_handler = NULL;
+
+    m_win = win;
+
+    // by default, the associated window is also the target window
+    DoSetTargetWindow(win);
+
+}
+
+wxVarScrollHelperBase::~wxVarScrollHelperBase()
+{
+    DeleteEvtHandler();
 }
 
 // ----------------------------------------------------------------------------
-// various helpers
+// wxVarScrollHelperBase various helpers
 // ----------------------------------------------------------------------------
 
-wxCoord wxVScrolledWindow::EstimateTotalHeight() const
+void
+wxVarScrollHelperBase::AssignOrient(wxCoord& x,
+                                    wxCoord& y,
+                                    wxCoord first,
+                                    wxCoord second)
+{
+    if ( GetOrientation() == wxVERTICAL )
+    {
+        x = first;
+        y = second;
+    }
+    else // horizontal
+    {
+        x = second;
+        y = first;
+    }
+}
+
+void
+wxVarScrollHelperBase::IncOrient(wxCoord& x, wxCoord& y, wxCoord inc)
+{
+    if ( GetOrientation() == wxVERTICAL )
+        y += inc;
+    else
+        x += inc;
+}
+
+wxCoord wxVarScrollHelperBase::DoEstimateTotalSize() const
 {
     // estimate the total height: it is impossible to call
-    // OnGetLineHeight() for every line because there may be too many of
-    // them, so we just make a guess using some lines in the beginning,
+    // OnGetUnitSize() for every unit because there may be too many of
+    // them, so we just make a guess using some units in the beginning,
     // some in the end and some in the middle
-    static const size_t NUM_LINES_TO_SAMPLE = 10;
+    static const size_t NUM_UNITS_TO_SAMPLE = 10;
 
-    wxCoord heightTotal;
-    if ( m_lineMax < 3*NUM_LINES_TO_SAMPLE )
+    wxCoord sizeTotal;
+    if ( m_unitMax < 3*NUM_UNITS_TO_SAMPLE )
     {
-        // in this case calculating exactly is faster and more correct than
+        // in this case, full calculations are faster and more correct than
         // guessing
-        heightTotal = GetLinesHeight(0, m_lineMax);
+        sizeTotal = GetUnitsSize(0, m_unitMax);
     }
-    else // too many lines to calculate exactly
+    else // too many units to calculate exactly
     {
-        // look at some lines in the beginning/middle/end
-        heightTotal =
-            GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
-                GetLinesHeight(m_lineMax - NUM_LINES_TO_SAMPLE, m_lineMax) +
-                    GetLinesHeight(m_lineMax/2 - NUM_LINES_TO_SAMPLE/2,
-                                   m_lineMax/2 + NUM_LINES_TO_SAMPLE/2);
-
-        // use the height of the lines we looked as the average
-        heightTotal = (wxCoord)
-                (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
+        // look at some units in the beginning/middle/end
+        sizeTotal =
+            GetUnitsSize(0, NUM_UNITS_TO_SAMPLE) +
+                GetUnitsSize(m_unitMax - NUM_UNITS_TO_SAMPLE,
+                             m_unitMax) +
+                    GetUnitsSize(m_unitMax/2 - NUM_UNITS_TO_SAMPLE/2,
+                                 m_unitMax/2 + NUM_UNITS_TO_SAMPLE/2);
+
+        // use the height of the units we looked as the average
+        sizeTotal = (wxCoord)
+                (((float)sizeTotal / (3*NUM_UNITS_TO_SAMPLE)) * m_unitMax);
     }
 
-    return heightTotal;
+    return sizeTotal;
 }
 
-wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
+wxCoord wxVarScrollHelperBase::GetUnitsSize(size_t unitMin, size_t unitMax) const
 {
-    if ( lineMin == lineMax )
+    if ( unitMin == unitMax )
         return 0;
-    else if ( lineMin > lineMax )
-        return -GetLinesHeight(lineMax, lineMin);
-    //else: lineMin < lineMax
+    else if ( unitMin > unitMax )
+        return -GetUnitsSize(unitMax, unitMin);
+    //else: unitMin < unitMax
 
-    // let the user code know that we're going to need all these lines
-    OnGetLinesHint(lineMin, lineMax);
+    // let the user code know that we're going to need all these units
+    OnGetUnitsSizeHint(unitMin, unitMax);
 
-    // do sum up their heights
-    wxCoord height = 0;
-    for ( size_t line = lineMin; line < lineMax; line++ )
+    // sum up their sizes
+    wxCoord size = 0;
+    for ( size_t unit = unitMin; unit < unitMax; ++unit )
     {
-        height += OnGetLineHeight(line);
+        size += OnGetUnitSize(unit);
     }
 
-    return height;
+    return size;
 }
 
-size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
+size_t wxVarScrollHelperBase::FindFirstVisibleFromLast(size_t unitLast, bool full) const
 {
-    const wxCoord hWindow = GetClientSize().y;
+    const wxCoord sWindow = GetOrientationTargetSize();
 
-    // go upwards until we arrive at a line such that lineLast is not visible
+    // go upwards until we arrive at a unit such that unitLast is not visible
     // any more when it is shown
-    size_t lineFirst = lineLast;
-    wxCoord h = 0;
+    size_t unitFirst = unitLast;
+    wxCoord s = 0;
     for ( ;; )
     {
-        h += OnGetLineHeight(lineFirst);
+        s += OnGetUnitSize(unitFirst);
 
-        if ( h > hWindow )
+        if ( s > sWindow )
         {
-            // for this line to be fully visible we need to go one line
+            // for this unit to be fully visible we need to go one unit
             // down, but if it is enough for it to be only partly visible then
-            // this line will do as well
+            // this unit will do as well
             if ( full )
             {
-                lineFirst++;
+                ++unitFirst;
             }
 
             break;
         }
 
-        if ( !lineFirst )
+        if ( !unitFirst )
             break;
 
-        lineFirst--;
+        --unitFirst;
+    }
+
+    return unitFirst;
+}
+
+size_t wxVarScrollHelperBase::GetNewScrollPosition(wxScrollWinEvent& event) const
+{
+    wxEventType evtType = event.GetEventType();
+
+    if ( evtType == wxEVT_SCROLLWIN_TOP )
+    {
+        return 0;
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
+    {
+        return m_unitMax;
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
+    {
+        return m_unitFirst ? m_unitFirst - 1 : 0;
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
+    {
+        return m_unitFirst + 1;
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
+    {
+        // Page up should do at least as much as line up.
+        return wxMin(FindFirstVisibleFromLast(m_unitFirst),
+                    m_unitFirst ? m_unitFirst - 1 : 0);
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
+    {
+        // And page down should do at least as much as line down.
+        if ( GetVisibleEnd() )
+            return wxMax(GetVisibleEnd() - 1, m_unitFirst + 1);
+        else
+            return wxMax(GetVisibleEnd(), m_unitFirst + 1);
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
+    {
+        return event.GetPosition();
+    }
+    else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
+    {
+        return event.GetPosition();
     }
 
-    return lineFirst;
+    // unknown scroll event?
+    wxFAIL_MSG( wxT("unknown scroll event type?") );
+    return 0;
 }
 
-void wxVScrolledWindow::UpdateScrollbar()
+void wxVarScrollHelperBase::UpdateScrollbar()
 {
-    // see how many lines can we fit on screen
-    const wxCoord hWindow = GetClientSize().y;
+    // if there is nothing to scroll, remove the scrollbar
+    if ( !m_unitMax )
+    {
+        RemoveScrollbar();
+        return;
+    }
 
-    wxCoord h = 0;
-    size_t line;
-    for ( line = m_lineFirst; line < m_lineMax; line++ )
+    // see how many units can we fit on screen
+    const wxCoord sWindow = GetOrientationTargetSize();
+
+    // do vertical calculations
+    wxCoord s = 0;
+    size_t unit;
+    for ( unit = m_unitFirst; unit < m_unitMax; ++unit )
     {
-        if ( h > hWindow )
+        if ( s > sWindow )
             break;
 
-        h += OnGetLineHeight(line);
+        s += OnGetUnitSize(unit);
     }
 
-    m_nVisible = line - m_lineFirst;
+    m_nUnitsVisible = unit - m_unitFirst;
 
-    int pageSize = m_nVisible;
-    if ( h > hWindow )
+    int unitsPageSize = m_nUnitsVisible;
+    if ( s > sWindow )
     {
-        // last line is only partially visible, we still need the scrollbar and
-        // so we have to "fix" pageSize because if it is equal to m_lineMax the
-        // scrollbar is not shown at all under MSW
-        pageSize--;
+        // last unit is only partially visible, we still need the scrollbar and
+        // so we have to "fix" pageSize because if it is equal to m_unitMax
+        // the scrollbar is not shown at all under MSW
+        --unitsPageSize;
     }
 
     // set the scrollbar parameters to reflect this
-    SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
+    m_win->SetScrollbar(GetOrientation(), m_unitFirst, unitsPageSize, m_unitMax);
+}
+
+void wxVarScrollHelperBase::RemoveScrollbar()
+{
+    m_unitFirst = 0;
+    m_nUnitsVisible = m_unitMax;
+    m_win->SetScrollbar(GetOrientation(), 0, 0, 0);
+}
+
+void wxVarScrollHelperBase::DeleteEvtHandler()
+{
+    // search for m_handler in the handler list
+    if ( m_win && m_handler )
+    {
+        if ( m_win->RemoveEventHandler(m_handler) )
+        {
+            delete m_handler;
+        }
+        //else: something is very wrong, so better [maybe] leak memory than
+        //      risk a crash because of double deletion
+
+        m_handler = NULL;
+    }
+}
+
+void wxVarScrollHelperBase::DoSetTargetWindow(wxWindow *target)
+{
+    m_targetWindow = target;
+#ifdef __WXMAC__
+    target->MacSetClipChildren( true ) ;
+#endif
+
+    // install the event handler which will intercept the events we're
+    // interested in (but only do it for our real window, not the target window
+    // which we scroll - we don't need to hijack its events)
+    if ( m_targetWindow == m_win )
+    {
+        // if we already have a handler, delete it first
+        DeleteEvtHandler();
+
+        m_handler = new wxVarScrollHelperEvtHandler(this);
+        m_targetWindow->PushEventHandler(m_handler);
+    }
 }
 
 // ----------------------------------------------------------------------------
-// operations
+// wxVarScrollHelperBase operations
 // ----------------------------------------------------------------------------
 
-void wxVScrolledWindow::SetLineCount(size_t count)
+void wxVarScrollHelperBase::SetTargetWindow(wxWindow *target)
 {
-    // save the number of lines
-    m_lineMax = count;
+    wxCHECK_RET( target, wxT("target window must not be NULL") );
+
+    if ( target == m_targetWindow )
+        return;
+
+    DoSetTargetWindow(target);
+}
+
+void wxVarScrollHelperBase::SetUnitCount(size_t count)
+{
+    // save the number of units
+    m_unitMax = count;
 
     // and our estimate for their total height
-    m_heightTotal = EstimateTotalHeight();
+    m_sizeTotal = EstimateTotalSize();
 
-    // recalculate the scrollbars parameters
-    m_lineFirst = 1;    // make sure it is != 0
-    ScrollToLine(0);
+    // ScrollToUnit() will update the scrollbar itself if it changes the unit
+    // we pass to it because it's out of [new] range
+    size_t oldScrollPos = m_unitFirst;
+    DoScrollToUnit(m_unitFirst);
+    if ( oldScrollPos == m_unitFirst )
+    {
+        // but if it didn't do it, we still need to update the scrollbar to
+        // reflect the changed number of units ourselves
+        UpdateScrollbar();
+    }
 }
 
-void wxVScrolledWindow::RefreshLine(size_t line)
+void wxVarScrollHelperBase::RefreshUnit(size_t unit)
 {
-    // is this line visible?
-    if ( !IsVisible(line) )
+    // is this unit visible?
+    if ( !IsVisible(unit) )
     {
         // no, it is useless to do anything
         return;
     }
 
-    // calculate the rect occupied by this line on screen
+    // calculate the rect occupied by this unit on screen
     wxRect rect;
-    rect.width = GetClientSize().x;
-    rect.height = OnGetLineHeight(line);
-    for ( size_t n = GetVisibleBegin(); n < line; n++ )
+    AssignOrient(rect.width, rect.height,
+                 GetNonOrientationTargetSize(), OnGetUnitSize(unit));
+
+    for ( size_t n = GetVisibleBegin(); n < unit; ++n )
     {
-        rect.y += OnGetLineHeight(n);
+        IncOrient(rect.x, rect.y, OnGetUnitSize(n));
     }
 
     // do refresh it
-    RefreshRect(rect);
+    m_targetWindow->RefreshRect(rect);
 }
 
-void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
+void wxVarScrollHelperBase::RefreshUnits(size_t from, size_t to)
 {
-    wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
+    wxASSERT_MSG( from <= to, wxT("RefreshUnits(): empty range") );
 
-    // clump the range to just the visible lines -- it is useless to refresh
+    // clump the range to just the visible units -- it is useless to refresh
     // the other ones
     if ( from < GetVisibleBegin() )
         from = GetVisibleBegin();
 
-    if ( to >= GetVisibleEnd() )
+    if ( to > GetVisibleEnd() )
         to = GetVisibleEnd();
-    else
-        to++;
 
-    // calculate the rect occupied by these lines on screen
-    wxRect rect;
-    rect.width = GetClientSize().x;
-    for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
+    // calculate the rect occupied by these units on screen
+    int orient_size, nonorient_size, orient_pos;
+    orient_size = nonorient_size = orient_pos = 0;
+
+    nonorient_size = GetNonOrientationTargetSize();
+
+    for ( size_t nBefore = GetVisibleBegin();
+          nBefore < from;
+          nBefore++ )
     {
-        rect.y += OnGetLineHeight(nBefore);
+        orient_pos += OnGetUnitSize(nBefore);
     }
 
-    for ( size_t nBetween = from; nBetween < to; nBetween++ )
+    for ( size_t nBetween = from; nBetween <= to; nBetween++ )
     {
-        rect.height += OnGetLineHeight(nBetween);
+        orient_size += OnGetUnitSize(nBetween);
     }
 
+    wxRect rect;
+    AssignOrient(rect.x, rect.y, 0, orient_pos);
+    AssignOrient(rect.width, rect.height, nonorient_size, orient_size);
+
     // do refresh it
-    RefreshRect(rect);
+    m_targetWindow->RefreshRect(rect);
 }
 
-void wxVScrolledWindow::RefreshAll()
+void wxVarScrollHelperBase::RefreshAll()
 {
     UpdateScrollbar();
 
-    Refresh();
+    m_targetWindow->Refresh();
 }
 
-bool wxVScrolledWindow::Layout()
+bool wxVarScrollHelperBase::ScrollLayout()
 {
-    if(GetSizer())
+    if ( m_targetWindow->GetSizer() && m_physicalScrolling )
     {
         // adjust the sizer dimensions/position taking into account the
         // virtual size and scrolled position of the window.
 
-        int y, w, h; // x is always 0 so no variable needed
+        int x, y;
+        AssignOrient(x, y, 0, -GetScrollOffset());
 
-        y = -GetLinesHeight(0, GetFirstVisibleLine());
-        GetVirtualSize(&w, &h);
-        GetSizer()->SetDimension(0, y, w, h);
+        int w, h;
+        m_targetWindow->GetVirtualSize(&w, &h);
+
+        m_targetWindow->GetSizer()->SetDimension(x, y, w, h);
         return true;
     }
 
     // fall back to default for LayoutConstraints
-    return wxPanel::Layout();
+    return m_targetWindow->wxWindow::Layout();
 }
 
-int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
+int wxVarScrollHelperBase::VirtualHitTest(wxCoord coord) const
 {
-    const size_t lineMax = GetVisibleEnd();
-    for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
+    const size_t unitMax = GetVisibleEnd();
+    for ( size_t unit = GetVisibleBegin(); unit < unitMax; ++unit )
     {
-        y -= OnGetLineHeight(line);
-        if ( y < 0 )
-            return line;
+        coord -= OnGetUnitSize(unit);
+        if ( coord < 0 )
+            return unit;
     }
 
     return wxNOT_FOUND;
 }
 
 // ----------------------------------------------------------------------------
-// scrolling
+// wxVarScrollHelperBase scrolling
 // ----------------------------------------------------------------------------
 
-bool wxVScrolledWindow::ScrollToLine(size_t line)
+bool wxVarScrollHelperBase::DoScrollToUnit(size_t unit)
 {
-    if ( !m_lineMax )
+    if ( !m_unitMax )
     {
         // we're empty, code below doesn't make sense in this case
         return false;
     }
 
-    // determine the real first line to scroll to: we shouldn't scroll beyond
+    // determine the real first unit to scroll to: we shouldn't scroll beyond
     // the end
-    size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
-    if ( line > lineFirstLast )
-        line = lineFirstLast;
+    size_t unitFirstLast = FindFirstVisibleFromLast(m_unitMax - 1, true);
+    if ( unit > unitFirstLast )
+        unit = unitFirstLast;
 
     // anything to do?
-    if ( line == m_lineFirst )
+    if ( unit == m_unitFirst )
     {
         // no
         return false;
     }
 
 
-    // remember the currently shown lines for the refresh code below
-    size_t lineFirstOld = GetVisibleBegin();
+    // remember the currently shown units for the refresh code below
+    size_t unitFirstOld = GetVisibleBegin(),
+           unitLastOld = GetVisibleEnd();
 
-    m_lineFirst = line;
+    m_unitFirst = unit;
 
 
     // the size of scrollbar thumb could have changed
     UpdateScrollbar();
 
-    // finally, scroll the actual window
-    ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
+    // finally refresh the display -- but only redraw as few units as possible
+    // to avoid flicker.  We can't do this if we have children because they
+    // won't be scrolled
+    if ( m_targetWindow->GetChildren().empty() &&
+         (GetVisibleBegin() >= unitLastOld || GetVisibleEnd() <= unitFirstOld) )
+    {
+        // the simplest case: we don't have any old units left, just redraw
+        // everything
+        m_targetWindow->Refresh();
+    }
+    else // scroll the window
+    {
+        // Avoid scrolling visible parts of the screen on Mac
+#ifdef __WXMAC__
+        if (m_physicalScrolling && m_targetWindow->IsShownOnScreen())
+#else
+        if ( m_physicalScrolling )
+#endif
+        {
+            wxCoord dx = 0,
+                    dy = GetUnitsSize(GetVisibleBegin(), unitFirstOld);
+
+            if ( GetOrientation() == wxHORIZONTAL )
+            {
+                wxCoord tmp = dx;
+                dx = dy;
+                dy = tmp;
+            }
+
+            m_targetWindow->ScrollWindow(dx, dy);
+        }
+        else // !m_physicalScrolling
+        {
+            // we still need to invalidate but we can't use ScrollWindow
+            // because physical scrolling is disabled (the user either didn't
+            // want children scrolled and/or doesn't want pixels to be
+            // physically scrolled).
+            m_targetWindow->Refresh();
+        }
+    }
 
     return true;
 }
 
-bool wxVScrolledWindow::ScrollLines(int lines)
+bool wxVarScrollHelperBase::DoScrollUnits(int units)
 {
-    lines += m_lineFirst;
-    if ( lines < 0 )
-        lines = 0;
+    units += m_unitFirst;
+    if ( units < 0 )
+        units = 0;
 
-    return ScrollToLine(lines);
+    return DoScrollToUnit(units);
 }
 
-bool wxVScrolledWindow::ScrollPages(int pages)
+bool wxVarScrollHelperBase::DoScrollPages(int pages)
 {
     bool didSomething = false;
 
     while ( pages )
     {
-        int line;
+        int unit;
         if ( pages > 0 )
         {
-            line = GetVisibleEnd();
-            if ( line )
-                line--;
-            pages--;
+            unit = GetVisibleEnd();
+            if ( unit )
+                --unit;
+            --pages;
         }
         else // pages < 0
         {
-            line = FindFirstFromBottom(GetVisibleBegin());
-            pages++;
+            unit = FindFirstVisibleFromLast(GetVisibleEnd());
+            ++pages;
         }
 
-        didSomething = ScrollToLine(line);
+        didSomething = DoScrollToUnit(unit);
     }
 
     return didSomething;
@@ -377,71 +672,94 @@ bool wxVScrolledWindow::ScrollPages(int pages)
 // event handling
 // ----------------------------------------------------------------------------
 
-void wxVScrolledWindow::OnSize(wxSizeEvent& event)
+void wxVarScrollHelperBase::HandleOnSize(wxSizeEvent& event)
 {
+    if ( m_unitMax )
+    {
+        // sometimes change in varscrollable window's size can result in
+        // unused empty space after the last item. Fix it by decrementing
+        // first visible item position according to the available space.
+
+        // determine free space
+        const wxCoord sWindow = GetOrientationTargetSize();
+        wxCoord s = 0;
+        size_t unit;
+        for ( unit = m_unitFirst; unit < m_unitMax; ++unit )
+        {
+            if ( s > sWindow )
+                break;
+
+            s += OnGetUnitSize(unit);
+        }
+        wxCoord freeSpace = sWindow - s;
+
+        // decrement first visible item index as long as there is free space
+        size_t idealUnitFirst;
+        for ( idealUnitFirst = m_unitFirst;
+              idealUnitFirst > 0;
+              idealUnitFirst-- )
+        {
+            wxCoord us = OnGetUnitSize(idealUnitFirst-1);
+            if ( freeSpace < us )
+                break;
+            freeSpace -= us;
+        }
+        m_unitFirst = idealUnitFirst;
+    }
+
     UpdateScrollbar();
 
     event.Skip();
 }
 
-void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
+void wxVarScrollHelperBase::HandleOnScroll(wxScrollWinEvent& event)
 {
-    size_t lineFirstNew;
-
-    const wxEventType evtType = event.GetEventType();
-
-    if ( evtType == wxEVT_SCROLLWIN_TOP )
-    {
-        lineFirstNew = 0;
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
-    {
-        lineFirstNew = m_lineMax;
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
-    {
-        lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
-    {
-        lineFirstNew = m_lineFirst + 1;
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
-    {
-        lineFirstNew = FindFirstFromBottom(m_lineFirst);
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
-    {
-        lineFirstNew = GetVisibleEnd();
-        if ( lineFirstNew )
-            lineFirstNew--;
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
+    if (GetOrientation() != event.GetOrientation())
     {
-        lineFirstNew = event.GetPosition();
-    }
-    else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
-    {
-        lineFirstNew = event.GetPosition();
-    }
-
-    else // unknown scroll event?
-    {
-        wxFAIL_MSG( _T("unknown scroll event type?") );
+        event.Skip();
         return;
     }
 
-    ScrollToLine(lineFirstNew);
+    DoScrollToUnit(GetNewScrollPosition(event));
 
 #ifdef __WXMAC__
-    Update();
+    UpdateMacScrollWindow();
 #endif // __WXMAC__
 }
 
+void wxVarScrollHelperBase::DoPrepareDC(wxDC& dc)
+{
+    if ( m_physicalScrolling )
+    {
+        wxPoint pt = dc.GetDeviceOrigin();
+
+        IncOrient(pt.x, pt.y, -GetScrollOffset());
+
+        dc.SetDeviceOrigin(pt.x, pt.y);
+    }
+}
+
+int wxVarScrollHelperBase::DoCalcScrolledPosition(int coord) const
+{
+    return coord - GetScrollOffset();
+}
+
+int wxVarScrollHelperBase::DoCalcUnscrolledPosition(int coord) const
+{
+    return coord + GetScrollOffset();
+}
+
 #if wxUSE_MOUSEWHEEL
 
-void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
+void wxVarScrollHelperBase::HandleOnMouseWheel(wxMouseEvent& event)
 {
+    // we only want to process wheel events for vertical implementations.
+    // There is no way to determine wheel orientation (and on MSW horizontal
+    // wheel rotation just fakes scroll events, rather than sending a MOUSEWHEEL
+    // event).
+    if ( GetOrientation() != wxVERTICAL )
+        return;
+
     m_sumWheelRotation += event.GetWheelRotation();
     int delta = event.GetWheelDelta();
 
@@ -453,486 +771,88 @@ void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
     m_sumWheelRotation += units_to_scroll*delta;
 
     if ( !event.IsPageScroll() )
-        ScrollLines( units_to_scroll*event.GetLinesPerAction() );
-    else
-        // scroll pages instead of lines
-        ScrollPages( units_to_scroll );
+        DoScrollUnits( units_to_scroll*event.GetLinesPerAction() );
+    else // scroll pages instead of units
+        DoScrollPages( units_to_scroll );
 }
 
-#endif
-
-
-
-
-// ----------------------------------------------------------------------------
-// wxVarScrolled Window event tables
-// ----------------------------------------------------------------------------
-
-BEGIN_EVENT_TABLE(wxHVScrolledWindow, wxPanel)
-    EVT_SIZE(wxHVScrolledWindow::OnSize)
-    EVT_SCROLLWIN(wxHVScrolledWindow::OnScroll)
-#if wxUSE_MOUSEWHEEL
-    EVT_MOUSEWHEEL(wxHVScrolledWindow::OnMouseWheel)
-#endif
-END_EVENT_TABLE()
+#endif // wxUSE_MOUSEWHEEL
 
 
 // ============================================================================
-// wxVarScrolled implementation
+// wxVarHVScrollHelper implementation
 // ============================================================================
 
-IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel)
-
 // ----------------------------------------------------------------------------
-// initialization
+// wxVarHVScrollHelper operations
 // ----------------------------------------------------------------------------
 
-void wxHVScrolledWindow::Init()
+void wxVarHVScrollHelper::SetRowColumnCount(size_t rowCount, size_t columnCount)
 {
-    // we're initially empty
-    m_rowsMax =
-    m_columnsMax =
-    m_rowsFirst =
-    m_columnsFirst = 0;
-
-    // these should always be strictly positive
-    m_nRowsVisible =
-    m_nColumnsVisible = 1;
-
-    m_widthTotal =
-    m_heightTotal = 0;
-
-    m_physicalScrolling = true;
-
-#if wxUSE_MOUSEWHEEL
-    m_sumWheelRotation = 0;
-#endif
+    SetRowCount(rowCount);
+    SetColumnCount(columnCount);
 }
 
-// ----------------------------------------------------------------------------
-// various helpers
-// ----------------------------------------------------------------------------
-
-wxCoord wxHVScrolledWindow::EstimateTotalHeight() const
+bool wxVarHVScrollHelper::ScrollToRowColumn(size_t row, size_t column)
 {
-    // estimate the total height: it is impossible to call
-    // OnGetLineHeight() for every line because there may be too many of
-    // them, so we just make a guess using some lines in the beginning,
-    // some in the end and some in the middle
-    static const size_t NUM_LINES_TO_SAMPLE = 10;
+    bool result = false;
+    result |= ScrollToRow(row);
+    result |= ScrollToColumn(column);
+    return result;
+}
 
-    wxCoord heightTotal;
-    if ( m_rowsMax < 3*NUM_LINES_TO_SAMPLE )
-    {
-        // in this case calculating exactly is faster and more correct than
-        // guessing
-        heightTotal = GetRowsHeight(0, m_rowsMax);
-    }
-    else // too many lines to calculate exactly
+void wxVarHVScrollHelper::RefreshRowColumn(size_t row, size_t column)
+{
+    // is this unit visible?
+    if ( !IsRowVisible(row) || !IsColumnVisible(column) )
     {
-        // look at some lines in the beginning/middle/end
-        heightTotal =
-            GetRowsHeight(0, NUM_LINES_TO_SAMPLE) +
-                GetRowsHeight(m_rowsMax - NUM_LINES_TO_SAMPLE,
-                              m_rowsMax) +
-                    GetRowsHeight(m_rowsMax/2 - NUM_LINES_TO_SAMPLE/2,
-                                  m_rowsMax/2 + NUM_LINES_TO_SAMPLE/2);
-
-        // use the height of the lines we looked as the average
-        heightTotal = (wxCoord)
-                (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_rowsMax);
+        // no, it is useless to do anything
+        return;
     }
 
-    return heightTotal;
-}
+    // calculate the rect occupied by this cell on screen
+    wxRect v_rect, h_rect;
+    v_rect.height = OnGetRowHeight(row);
+    h_rect.width = OnGetColumnWidth(column);
 
-wxCoord wxHVScrolledWindow::EstimateTotalWidth() const
-{
-    // estimate the total width: it is impossible to call
-    // OnGetLineWidth() for every line because there may be too many of
-    // them, so we just make a guess using some lines in the beginning,
-    // some in the end and some in the middle
-    static const size_t NUM_LINES_TO_SAMPLE = 10;
+    size_t n;
 
-    wxCoord widthTotal;
-    if ( m_columnsMax < 3*NUM_LINES_TO_SAMPLE )
+    for ( n = GetVisibleRowsBegin(); n < row; n++ )
     {
-        // in this case calculating exactly is faster and more correct than
-        // guessing
-        widthTotal = GetColumnsWidth(0, m_columnsMax);
+        v_rect.y += OnGetRowHeight(n);
+    }
+
+    for ( n = GetVisibleColumnsBegin(); n < column; n++ )
+    {
+        h_rect.x += OnGetColumnWidth(n);
     }
-    else // too many lines to calculate exactly
+
+    // refresh but specialize the behaviour if we have a single target window
+    if ( wxVarVScrollHelper::GetTargetWindow() == wxVarHScrollHelper::GetTargetWindow() )
     {
-        // look at some lines in the beginning/middle/end
-        widthTotal =
-            GetColumnsWidth(0, NUM_LINES_TO_SAMPLE) +
-                GetColumnsWidth(m_columnsMax - NUM_LINES_TO_SAMPLE,
-                                m_columnsMax) +
-                    GetColumnsWidth(m_columnsMax/2 - NUM_LINES_TO_SAMPLE/2,
-                                    m_columnsMax/2 + NUM_LINES_TO_SAMPLE/2);
-
-        // use the width of the lines we looked as the average
-        widthTotal = (wxCoord)
-                (((float)widthTotal / (3*NUM_LINES_TO_SAMPLE)) * m_columnsMax);
+        v_rect.x = h_rect.x;
+        v_rect.width = h_rect.width;
+        wxVarVScrollHelper::GetTargetWindow()->RefreshRect(v_rect);
     }
+    else
+    {
+        v_rect.x = 0;
+        v_rect.width = wxVarVScrollHelper::GetNonOrientationTargetSize();
+        h_rect.y = 0;
+        h_rect.width = wxVarHScrollHelper::GetNonOrientationTargetSize();
 
-    return widthTotal;
+        wxVarVScrollHelper::GetTargetWindow()->RefreshRect(v_rect);
+        wxVarHScrollHelper::GetTargetWindow()->RefreshRect(h_rect);
+    }
 }
 
-wxCoord wxHVScrolledWindow::GetRowsHeight(size_t rowMin, size_t rowMax) const
+void wxVarHVScrollHelper::RefreshRowsColumns(size_t fromRow, size_t toRow,
+                                             size_t fromColumn, size_t toColumn)
 {
-    if ( rowMin == rowMax )
-        return 0;
-    else if ( rowMin > rowMax )
-        return -GetRowsHeight(rowMax, rowMin);
-    //else: lineMin < lineMax
+    wxASSERT_MSG( fromRow <= toRow || fromColumn <= toColumn,
+        wxT("RefreshRowsColumns(): empty range") );
 
-    // let the user code know that we're going to need all these lines
-    OnGetRowsHeightHint(rowMin, rowMax);
-
-    // do sum up their heights
-    wxCoord height = 0;
-    for ( size_t row = rowMin; row < rowMax; row++ )
-    {
-        height += OnGetRowHeight(row);
-    }
-
-    return height;
-}
-
-wxCoord wxHVScrolledWindow::GetColumnsWidth(size_t columnMin, size_t columnMax) const
-{
-    if ( columnMin == columnMax )
-        return 0;
-    else if ( columnMin > columnMax )
-        return -GetColumnsWidth(columnMax, columnMin);
-    //else: lineMin < lineMax
-
-    // let the user code know that we're going to need all these lines
-    OnGetColumnsWidthHint(columnMin, columnMax);
-
-    // do sum up their widths
-    wxCoord width = 0;
-    for ( size_t column = columnMin; column < columnMax; column++ )
-    {
-        width += OnGetColumnWidth(column);
-    }
-
-    return width;
-}
-
-size_t wxHVScrolledWindow::FindFirstFromBottom(size_t rowLast, bool full)
-{
-    const wxCoord hWindow = GetClientSize().y;
-
-    // go upwards until we arrive at a line such that lineLast is not visible
-    // any more when it is shown
-    size_t lineFirst = rowLast;
-    wxCoord h = 0;
-    for ( ;; )
-    {
-        h += OnGetRowHeight(lineFirst);
-
-        if ( h > hWindow )
-        {
-            // for this line to be fully visible we need to go one line
-            // down, but if it is enough for it to be only partly visible then
-            // this line will do as well
-            if ( full )
-            {
-                lineFirst++;
-            }
-
-            break;
-        }
-
-        if ( !lineFirst )
-            break;
-
-        lineFirst--;
-    }
-
-    return lineFirst;
-}
-
-size_t wxHVScrolledWindow::FindFirstFromRight(size_t columnLast, bool full)
-{
-    const wxCoord wWindow = GetClientSize().x;
-
-    // go upwards until we arrive at a line such that lineLast is not visible
-    // any more when it is shown
-    size_t lineFirst = columnLast;
-    wxCoord w = 0;
-    for ( ;; )
-    {
-        w += OnGetColumnWidth(lineFirst);
-
-        if ( w > wWindow )
-        {
-            // for this line to be fully visible we need to go one line
-            // down, but if it is enough for it to be only partly visible then
-            // this line will do as well
-            if ( full )
-            {
-                lineFirst++;
-            }
-
-            break;
-        }
-
-        if ( !lineFirst )
-            break;
-
-        lineFirst--;
-    }
-
-    return lineFirst;
-}
-
-void wxHVScrolledWindow::UpdateScrollbars()
-{
-    // see how many lines can we fit on screen (on both axes)
-    const wxCoord wWindow = GetClientSize().x;
-    const wxCoord hWindow = GetClientSize().y;
-
-    // first do the horizontal calculations
-    wxCoord w = 0;
-    size_t column;
-    for ( column = m_columnsFirst; column < m_columnsMax; column++ )
-    {
-        if ( w > wWindow )
-            break;
-
-        w += OnGetColumnWidth(column);
-    }
-
-    m_nColumnsVisible = column - m_columnsFirst;
-
-    int columnsPageSize = m_nColumnsVisible;
-    if ( w > wWindow )
-    {
-        // last line is only partially visible, we still need the scrollbar and
-        // so we have to "fix" pageSize because if it is equal to
-        // m_horizLineMax the scrollbar is not shown at all under MSW
-        columnsPageSize--;
-    }
-
-    // set the scrollbar parameters to reflect this
-    SetScrollbar(wxHORIZONTAL, m_columnsFirst, columnsPageSize, m_columnsMax);
-
-
-    // now do the vertical calculations
-    wxCoord h = 0;
-    size_t row;
-    for ( row = m_rowsFirst; row < m_rowsMax; row++ )
-    {
-        if ( h > hWindow )
-            break;
-
-        h += OnGetRowHeight(row);
-    }
-
-    m_nRowsVisible = row - m_rowsFirst;
-
-    int rowsPageSize = m_nRowsVisible;
-    if ( h > hWindow )
-    {
-        // last line is only partially visible, we still need the scrollbar and
-        // so we have to "fix" pageSize because if it is equal to m_vertLineMax
-        // the scrollbar is not shown at all under MSW
-        rowsPageSize--;
-    }
-
-    // set the scrollbar parameters to reflect this
-    SetScrollbar(wxVERTICAL, m_rowsFirst, rowsPageSize, m_rowsMax);
-}
-
-void wxHVScrolledWindow::PrepareDC(wxDC& dc)
-{
-    if(m_physicalScrolling)
-    {
-        dc.SetDeviceOrigin(-GetColumnsWidth(0, GetVisibleColumnsBegin()),
-                           -GetRowsHeight(0, GetVisibleRowsBegin()));
-    }
-}
-
-// ----------------------------------------------------------------------------
-// operations
-// ----------------------------------------------------------------------------
-
-void wxHVScrolledWindow::SetRowColumnCounts(size_t rowCount, size_t columnCount)
-{
-    // save the number of lines
-    m_rowsMax = rowCount;
-    m_columnsMax = columnCount;
-
-    // and our estimate for their total height and width
-    m_heightTotal = EstimateTotalHeight();
-    m_widthTotal = EstimateTotalWidth();
-
-    // recalculate the scrollbars parameters
-    if(m_rowsFirst >= rowCount)
-        m_rowsFirst = (rowCount > 0) ? rowCount - 1 : 0;
-
-    if(m_columnsFirst >= columnCount)
-        m_columnsFirst = (columnCount > 0) ? columnCount - 1 : 0;
-
-#if 0
-    // checks disabled due to size_t type of members
-    // but leave them here if anyone would want to do some debugging
-    if(m_rowsFirst < 0)
-        m_rowsFirst = 0;
-
-    if(m_columnsFirst < 0)
-        m_columnsFirst = 0;
-#endif
-
-    ScrollToRowColumn(m_rowsFirst, m_columnsFirst);
-}
-
-void wxHVScrolledWindow::RefreshColumn(size_t column)
-{
-    // is this line visible?
-    if ( !IsColumnVisible(column) )
-    {
-        // no, it is useless to do anything
-        return;
-    }
-
-    // calculate the rect occupied by this line on screen
-    wxRect rect;
-    rect.width = OnGetColumnWidth(column);
-    rect.height = GetClientSize().y;
-    for ( size_t n = GetVisibleColumnsBegin(); n < column; n++ )
-    {
-        rect.y += OnGetColumnWidth(n);
-    }
-
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshRow(size_t row)
-{
-    // is this line visible?
-    if ( !IsRowVisible(row) )
-    {
-        // no, it is useless to do anything
-        return;
-    }
-
-    // calculate the rect occupied by this line on screen
-    wxRect rect;
-    rect.width = GetClientSize().x;
-    rect.height = OnGetRowHeight(row);
-    for ( size_t n = GetVisibleRowsBegin(); n < row; n++ )
-    {
-        rect.y += OnGetRowHeight(n);
-    }
-
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshRowColumn(size_t row, size_t column)
-{
-    // is this line visible?
-    if ( !IsRowVisible(row) || !IsColumnVisible(column) )
-    {
-        // no, it is useless to do anything
-        return;
-    }
-
-    // calculate the rect occupied by this cell on screen
-    wxRect rect;
-    rect.height = OnGetRowHeight(row);
-    rect.width = OnGetColumnWidth(column);
-
-    size_t n;
-
-    for ( n = GetVisibleRowsBegin(); n < row; n++ )
-    {
-        rect.y += OnGetRowHeight(n);
-    }
-
-    for ( n = GetVisibleColumnsBegin(); n < column; n++ )
-    {
-        rect.x += OnGetColumnWidth(n);
-    }
-
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshRows(size_t from, size_t to)
-{
-    wxASSERT_MSG( from <= to, _T("RefreshRows(): empty range") );
-
-    // clump the range to just the visible lines -- it is useless to refresh
-    // the other ones
-    if ( from < GetVisibleRowsBegin() )
-        from = GetVisibleRowsBegin();
-
-    if ( to > GetVisibleRowsEnd() )
-        to = GetVisibleRowsEnd();
-
-    // calculate the rect occupied by these lines on screen
-    wxRect rect;
-    rect.width = GetClientSize().x;
-    for ( size_t nBefore = GetVisibleRowsBegin();
-          nBefore < from;
-          nBefore++ )
-    {
-        rect.y += OnGetRowHeight(nBefore);
-    }
-
-    for ( size_t nBetween = from; nBetween <= to; nBetween++ )
-    {
-        rect.height += OnGetRowHeight(nBetween);
-    }
-
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshColumns(size_t from, size_t to)
-{
-    wxASSERT_MSG( from <= to, _T("RefreshColumns(): empty range") );
-
-    // clump the range to just the visible lines -- it is useless to refresh
-    // the other ones
-    if ( from < GetVisibleColumnsBegin() )
-        from = GetVisibleColumnsBegin();
-
-    if ( to > GetVisibleColumnsEnd() )
-        to = GetVisibleColumnsEnd();
-
-    // calculate the rect occupied by these lines on screen
-    wxRect rect;
-    rect.height = GetClientSize().y;
-    for ( size_t nBefore = GetVisibleColumnsBegin();
-          nBefore < from;
-          nBefore++ )
-    {
-        rect.x += OnGetColumnWidth(nBefore);
-    }
-
-    for ( size_t nBetween = from; nBetween <= to; nBetween++ )
-    {
-        rect.width += OnGetColumnWidth(nBetween);
-    }
-
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshRowsColumns(size_t fromRow, size_t toRow,
-                                            size_t fromColumn, size_t toColumn)
-{
-    wxASSERT_MSG( fromRow <= toRow || fromColumn <= toColumn,
-        _T("RefreshRowsColumns(): empty range") );
-
-    // clump the range to just the visible lines -- it is useless to refresh
+    // clump the range to just the visible units -- it is useless to refresh
     // the other ones
     if ( fromRow < GetVisibleRowsBegin() )
         fromRow = GetVisibleRowsBegin();
@@ -946,452 +866,156 @@ void wxHVScrolledWindow::RefreshRowsColumns(size_t fromRow, size_t toRow,
     if ( toColumn > GetVisibleColumnsEnd() )
         toColumn = GetVisibleColumnsEnd();
 
-    // calculate the rect occupied by these lines on screen
-    wxRect rect;
+    // calculate the rect occupied by these units on screen
+    wxRect v_rect, h_rect;
     size_t nBefore, nBetween;
 
     for ( nBefore = GetVisibleRowsBegin();
           nBefore < fromRow;
           nBefore++ )
     {
-        rect.y += OnGetRowHeight(nBefore);
+        v_rect.y += OnGetRowHeight(nBefore);
     }
 
     for ( nBetween = fromRow; nBetween <= toRow; nBetween++ )
     {
-        rect.height += OnGetRowHeight(nBetween);
+        v_rect.height += OnGetRowHeight(nBetween);
     }
 
     for ( nBefore = GetVisibleColumnsBegin();
           nBefore < fromColumn;
           nBefore++ )
     {
-        rect.x += OnGetColumnWidth(nBefore);
+        h_rect.x += OnGetColumnWidth(nBefore);
     }
 
     for ( nBetween = fromColumn; nBetween <= toColumn; nBetween++ )
     {
-        rect.width += OnGetColumnWidth(nBetween);
+        h_rect.width += OnGetColumnWidth(nBetween);
     }
 
-    // do refresh it
-    RefreshRect(rect);
-}
-
-void wxHVScrolledWindow::RefreshAll()
-{
-    UpdateScrollbars();
-
-    Refresh();
-}
-
-bool wxHVScrolledWindow::Layout()
-{
-    if(GetSizer() && m_physicalScrolling)
+    // refresh but specialize the behaviour if we have a single target window
+    if ( wxVarVScrollHelper::GetTargetWindow() == wxVarHScrollHelper::GetTargetWindow() )
     {
-        // adjust the sizer dimensions/position taking into account the
-        // virtual size and scrolled position of the window.
-
-        int x, y, w, h;
-
-        y = -GetRowsHeight(0, GetVisibleRowsBegin());
-        x = -GetColumnsWidth(0, GetVisibleColumnsBegin());
-        GetVirtualSize(&w, &h);
-        GetSizer()->SetDimension(x, y, w, h);
-        return true;
+        v_rect.x = h_rect.x;
+        v_rect.width = h_rect.width;
+        wxVarVScrollHelper::GetTargetWindow()->RefreshRect(v_rect);
     }
-
-    // fall back to default for LayoutConstraints
-    return wxPanel::Layout();
-}
-
-wxPoint wxHVScrolledWindow::HitTest(wxCoord x, wxCoord y) const
-{
-    const size_t rowMax = GetVisibleRowsEnd();
-    const size_t columnMax = GetVisibleColumnsEnd();
-
-    wxPoint hit(wxNOT_FOUND, wxNOT_FOUND);
-    for ( size_t row = GetVisibleRowsBegin();
-          row <= rowMax;
-          row++ )
+    else
     {
-        y -= OnGetRowHeight(row);
-        if ( y < 0 )
-            hit.y = row;
-    }
+        v_rect.x = 0;
+        v_rect.width = wxVarVScrollHelper::GetNonOrientationTargetSize();
+        h_rect.y = 0;
+        h_rect.width = wxVarHScrollHelper::GetNonOrientationTargetSize();
 
-    for ( size_t column = GetVisibleColumnsBegin();
-          column <= columnMax;
-          column++ )
-    {
-        x -= OnGetColumnWidth(column);
-        if ( x < 0 )
-            hit.x = column;
+        wxVarVScrollHelper::GetTargetWindow()->RefreshRect(v_rect);
+        wxVarHScrollHelper::GetTargetWindow()->RefreshRect(h_rect);
     }
-
-    return hit;
 }
 
-// ----------------------------------------------------------------------------
-// scrolling
-// ----------------------------------------------------------------------------
-
-bool wxHVScrolledWindow::ScrollToRowColumn(size_t row, size_t column)
+wxPosition wxVarHVScrollHelper::VirtualHitTest(wxCoord x, wxCoord y) const
 {
-    if ( !m_rowsMax && !m_columnsMax )
-    {
-        // we're empty, code below doesn't make sense in this case
-        return false;
-    }
-
-    bool scrolled = false;
-    scrolled |= ScrollToRow(row);
-    scrolled |= ScrollToColumn(column);
-
-    return scrolled;
+    return wxPosition(wxVarVScrollHelper::VirtualHitTest(y),
+                      wxVarHScrollHelper::VirtualHitTest(x));
 }
 
-bool wxHVScrolledWindow::ScrollToRow(size_t row)
+void wxVarHVScrollHelper::DoPrepareDC(wxDC& dc)
 {
-    if ( !m_rowsMax )
-    {
-        // we're empty, code below doesn't make sense in this case
-        return false;
-    }
-
-    // determine the real first line to scroll to: we shouldn't scroll beyond
-    // the end
-    size_t lineFirstLast = FindFirstFromBottom(m_rowsMax - 1, true);
-    if ( row > lineFirstLast )
-        row = lineFirstLast;
-
-    // anything to do?
-    if ( row == m_rowsFirst )
-    {
-        // no
-        return false;
-    }
-
-
-    // remember the currently shown lines for the refresh code below
-    size_t lineFirstOld = GetVisibleRowsBegin();
-
-    m_rowsFirst = row;
-
-
-    // the size of scrollbar thumb could have changed
-    UpdateScrollbars();
-
-
-    // finally, scroll the actual window contents vertically
-    if(m_physicalScrolling)
-        ScrollWindow(0, GetRowsHeight(GetVisibleRowsBegin(), lineFirstOld));
-
-    return true;
+    wxVarVScrollHelper::DoPrepareDC(dc);
+    wxVarHScrollHelper::DoPrepareDC(dc);
 }
 
-bool wxHVScrolledWindow::ScrollToColumn(size_t column)
+bool wxVarHVScrollHelper::ScrollLayout()
 {
-    if ( !m_columnsMax )
-    {
-        // we're empty, code below doesn't make sense in this case
-        return false;
-    }
-
-    // determine the real first line to scroll to: we shouldn't scroll beyond
-    // the end
-    size_t lineFirstLast = FindFirstFromRight(m_columnsMax - 1, true);
-    if ( column > lineFirstLast )
-        column = lineFirstLast;
-
-    // anything to do?
-    if ( column == m_columnsFirst )
-    {
-        // no
-        return false;
-    }
-
-
-    // remember the currently shown lines for the refresh code below
-    size_t lineFirstOld = GetVisibleColumnsBegin();
-
-    m_columnsFirst = column;
-
-
-    // the size of scrollbar thumb could have changed
-    UpdateScrollbars();
-
-    // finally, scroll the actual window contents horizontally
-    if(m_physicalScrolling)
-        ScrollWindow(GetColumnsWidth(GetVisibleColumnsBegin(), lineFirstOld), 0);
-
-    return true;
+    bool layout_result = false;
+    layout_result |= wxVarVScrollHelper::ScrollLayout();
+    layout_result |= wxVarHScrollHelper::ScrollLayout();
+    return layout_result;
 }
 
-bool wxHVScrolledWindow::ScrollRows(int rows)
+wxSize wxVarHVScrollHelper::GetRowColumnCount() const
 {
-    rows += m_rowsFirst;
-    if ( rows < 0 )
-        rows = 0;
-
-    return ScrollToRow(rows);
+    return wxSize(GetColumnCount(), GetRowCount());
 }
 
-bool wxHVScrolledWindow::ScrollColumns(int columns)
+wxPosition wxVarHVScrollHelper::GetVisibleBegin() const
 {
-    columns += m_columnsFirst;
-    if ( columns < 0 )
-        columns = 0;
-
-    return ScrollToColumn(columns);
+    return wxPosition(GetVisibleRowsBegin(), GetVisibleColumnsBegin());
 }
 
-bool wxHVScrolledWindow::ScrollRowsColumns(int rows, int columns)
+wxPosition wxVarHVScrollHelper::GetVisibleEnd() const
 {
-    rows += m_rowsFirst;
-    if ( rows < 0 )
-        rows = 0;
-
-    columns += m_columnsFirst;
-    if ( columns < 0 )
-        columns = 0;
-
-    return ScrollToRowColumn(rows, columns);
+    return wxPosition(GetVisibleRowsEnd(), GetVisibleColumnsEnd());
 }
 
-bool wxHVScrolledWindow::ScrollRowPages(int pages)
+bool wxVarHVScrollHelper::IsVisible(size_t row, size_t column) const
 {
-    bool didSomething = false;
+    return IsRowVisible(row) && IsColumnVisible(column);
+}
 
-    while ( pages )
-    {
-        int line;
-        if ( pages > 0 )
-        {
-            line = GetVisibleRowsEnd();
-            if ( line )
-                line--;
-            pages--;
-        }
-        else // pages < 0
-        {
-            line = FindFirstFromBottom(GetVisibleRowsEnd());
-            pages++;
-        }
 
-        didSomething = ScrollToRow(line);
-    }
+// ============================================================================
+// wx[V/H/HV]ScrolledWindow implementations
+// ============================================================================
 
-    return didSomething;
-}
+IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
+IMPLEMENT_ABSTRACT_CLASS(wxHScrolledWindow, wxPanel)
+IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel)
 
-bool wxHVScrolledWindow::ScrollColumnPages(int pages)
-{
-    bool didSomething = false;
 
-    while ( pages )
-    {
-        int line;
-        if ( pages > 0 )
-        {
-            line = GetVisibleColumnsEnd();
-            if ( line )
-                line--;
-            pages--;
-        }
-        else // pages < 0
-        {
-            line = FindFirstFromRight(GetVisibleColumnsEnd());
-            pages++;
-        }
+#if WXWIN_COMPATIBILITY_2_8
 
-        didSomething = ScrollToColumn(line);
-    }
+// ===========================================================================
+// wxVarVScrollLegacyAdaptor
+// ===========================================================================
 
-    return didSomething;
-}
+size_t wxVarVScrollLegacyAdaptor::GetFirstVisibleLine() const
+{ return GetVisibleRowsBegin(); }
 
-bool wxHVScrolledWindow::ScrollPages(int rowPages, int columnPages)
-{
-    bool didSomething = false;
+size_t wxVarVScrollLegacyAdaptor::GetLastVisibleLine() const
+{ return GetVisibleRowsEnd() - 1; }
 
-    while ( rowPages )
-    {
-        int line;
-        if ( rowPages > 0 )
-        {
-            line = GetVisibleRowsEnd();
-            if ( line )
-                line--;
-            rowPages--;
-        }
-        else // rowPages < 0
-        {
-            line = FindFirstFromBottom(GetVisibleRowsBegin());
-            rowPages++;
-        }
+size_t wxVarVScrollLegacyAdaptor::GetLineCount() const
+{ return GetRowCount(); }
 
-        didSomething = ScrollToRow(line);
-    }
+void wxVarVScrollLegacyAdaptor::SetLineCount(size_t count)
+{ SetRowCount(count); }
 
-    while ( columnPages )
-    {
-        int line;
-        if ( columnPages > 0 )
-        {
-            line = GetVisibleColumnsEnd();
-            if ( line )
-                line--;
-            columnPages--;
-        }
-        else // columnPages < 0
-        {
-            line = FindFirstFromRight(GetVisibleColumnsBegin());
-            columnPages++;
-        }
+void wxVarVScrollLegacyAdaptor::RefreshLine(size_t line)
+{ RefreshRow(line); }
 
-        didSomething |= ScrollToColumn(line);
-    }
+void wxVarVScrollLegacyAdaptor::RefreshLines(size_t from, size_t to)
+{ RefreshRows(from, to); }
 
-    return didSomething;
-}
+bool wxVarVScrollLegacyAdaptor::ScrollToLine(size_t line)
+{ return ScrollToRow(line); }
 
-// ----------------------------------------------------------------------------
-// event handling
-// ----------------------------------------------------------------------------
+bool wxVarVScrollLegacyAdaptor::ScrollLines(int lines)
+{ return ScrollRows(lines); }
 
-void wxHVScrolledWindow::OnSize(wxSizeEvent& event)
-{
-    UpdateScrollbars();
-    Layout();
+bool wxVarVScrollLegacyAdaptor::ScrollPages(int pages)
+{ return ScrollRowPages(pages); }
 
-    event.Skip();
+wxCoord wxVarVScrollLegacyAdaptor::OnGetLineHeight(size_t WXUNUSED(n)) const
+{
+    wxFAIL_MSG( wxT("OnGetLineHeight() must be overridden if OnGetRowHeight() isn't!") );
+    return -1;
 }
 
-void wxHVScrolledWindow::OnScroll(wxScrollWinEvent& event)
+void wxVarVScrollLegacyAdaptor::OnGetLinesHint(size_t WXUNUSED(lineMin),
+                                               size_t WXUNUSED(lineMax)) const
 {
-    if(event.GetOrientation() == wxHORIZONTAL)
-    {
-        size_t columnsFirstNew;
-        const wxEventType evtType = event.GetEventType();
-
-        if ( evtType == wxEVT_SCROLLWIN_TOP )
-        {
-            columnsFirstNew = 0;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
-        {
-            columnsFirstNew = m_columnsMax;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
-        {
-            columnsFirstNew = m_columnsFirst ? m_columnsFirst - 1 : 0;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
-        {
-            columnsFirstNew = m_columnsFirst + 1;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
-        {
-            columnsFirstNew = FindFirstFromRight(m_columnsFirst);
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
-        {
-            columnsFirstNew = GetVisibleColumnsEnd();
-            if ( columnsFirstNew )
-                columnsFirstNew--;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
-        {
-            columnsFirstNew = event.GetPosition();
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
-        {
-            columnsFirstNew = event.GetPosition();
-        }
-
-        else // unknown scroll event?
-        {
-            wxFAIL_MSG( _T("unknown scroll event type?") );
-            return;
-        }
-
-        ScrollToColumn(columnsFirstNew);
-    }
-    else if(event.GetOrientation() == wxVERTICAL)
-    {
-        size_t rowsFirstNew;
-        const wxEventType evtType = event.GetEventType();
-
-        if ( evtType == wxEVT_SCROLLWIN_TOP )
-        {
-            rowsFirstNew = 0;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
-        {
-            rowsFirstNew = m_rowsMax;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
-        {
-            rowsFirstNew = m_rowsFirst ? m_rowsFirst - 1 : 0;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
-        {
-            rowsFirstNew = m_rowsFirst + 1;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
-        {
-            rowsFirstNew = FindFirstFromBottom(m_rowsFirst);
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
-        {
-            rowsFirstNew = GetVisibleRowsEnd();
-            if ( rowsFirstNew )
-                rowsFirstNew--;
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
-        {
-            rowsFirstNew = event.GetPosition();
-        }
-        else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
-        {
-            rowsFirstNew = event.GetPosition();
-        }
-
-        else // unknown scroll event?
-        {
-            wxFAIL_MSG( _T("unknown scroll event type?") );
-            return;
-        }
-
-        ScrollToRow(rowsFirstNew);
-    }
-
-
-#ifdef __WXMAC__
-    Update();
-#endif // __WXMAC__
 }
 
-#if wxUSE_MOUSEWHEEL
-
-void wxHVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
+wxCoord wxVarVScrollLegacyAdaptor::OnGetRowHeight(size_t n) const
 {
-    m_sumWheelRotation += event.GetWheelRotation();
-    int delta = event.GetWheelDelta();
-
-    // how much to scroll this time
-    int units_to_scroll = -(m_sumWheelRotation/delta);
-    if ( !units_to_scroll )
-        return;
-
-    m_sumWheelRotation += units_to_scroll*delta;
+    return OnGetLineHeight(n);
+}
 
-    if ( !event.IsPageScroll() )
-        ScrollRows( units_to_scroll*event.GetLinesPerAction() );
-    else
-        // scroll pages instead of lines
-        ScrollRowPages( units_to_scroll );
+void wxVarVScrollLegacyAdaptor::OnGetRowsHeightHint(size_t rowMin,
+                                                    size_t rowMax) const
+{
+    OnGetLinesHint(rowMin, rowMax);
 }
 
-#endif
+#endif // WXWIN_COMPATIBILITY_2_8