From d77836e48cf1a0f7575cd1ce66f59f5bc07e5b56 Mon Sep 17 00:00:00 2001
From: Robert Roebling <robert@roebling.de>
Date: Wed, 15 Feb 2006 09:59:39 +0000
Subject: [PATCH]   Applied wxVScrolledWindow patch

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@37591 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/classes.tex  |   1 +
 docs/latex/wx/hvscroll.tex | 557 +++++++++++++++++++++
 docs/latex/wx/vscroll.tex  |   2 +-
 include/wx/vscroll.h       | 269 +++++++++-
 samples/vscroll/vstest.cpp | 249 ++++++++--
 src/generic/vscroll.cpp    | 970 ++++++++++++++++++++++++++++++++++++-
 6 files changed, 1998 insertions(+), 50 deletions(-)
 create mode 100644 docs/latex/wx/hvscroll.tex

diff --git a/docs/latex/wx/classes.tex b/docs/latex/wx/classes.tex
index 0deb6b639e..54f94379d5 100644
--- a/docs/latex/wx/classes.tex
+++ b/docs/latex/wx/classes.tex
@@ -179,6 +179,7 @@
 \input htwinprs.tex
 \input htwinhnd.tex
 \input http.tex
+\input hvscroll.tex
 \input icon.tex
 \input iconbndl.tex
 \input iconloc.tex
diff --git a/docs/latex/wx/hvscroll.tex b/docs/latex/wx/hvscroll.tex
new file mode 100644
index 0000000000..b75501923c
--- /dev/null
+++ b/docs/latex/wx/hvscroll.tex
@@ -0,0 +1,557 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Name:        hvscroll.tex
+%% Purpose:     wxHVScrolledWindow documentation
+%% Author:      Vadim Zeitlin
+%% Modified by: Brad Anderson
+%% Created:     24.01.06
+%% RCS-ID:      $Id$
+%% Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
+%% License:     wxWindows license
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{\class{wxHVScrolledWindow}}\label{wxhvscrolledwindow}
+
+This class is strongly influenced by 
+\helpref{wxVScrolledWindow}{wxvscrolledwindow}. 
+Like wxVScrolledWindow, this class is here to provide an easy way to implement
+variable line sizes.  The difference is that wxVScrolledWindow only works with
+vertical scrolling.  This class extends the behavior of wxVScrolledWindow to
+the horizontal axis in addition to the vertical axis.
+
+The scrolling is also "virtual" in the sense that row widths and column heights
+only need to be known for the rows and columns that are currently visible.
+
+Like \helpref{wxVScrolledWindow}{wxvscrolledwindow}, this is a generalization
+of the \helpref{wxScrolledWindow}{wxscrolledwindow} class which can be only
+used when all rows have a constant height and columns have a constant width. 
+Like wxVScrolledWinow it lacks some of wxScrolledWindow features such as
+scrolling another window or only scrolling a rectangle of the window and not
+its entire client area.
+
+If only vertical scrolling is needed, wxVScrolledWindow is recommended
+because it is simpler to use.
+   
+There is no wxHScrolledWindow but horizontal-only scrolling is implemented
+easily enough with this class.
+
+To use this class, you need to derive from it and implement both the 
+\helpref{OnGetRowHeight()}{wxhvscrolledwindowongetrowheight} and the 
+\helpref{OnGetColumnWidth()}{wxhvscrolledwindowongetcolumnwidth} pure virtual
+methods. You also must call 
+\helpref{SetRowColumnCounts}{wxhvscrolledwindowsetrowcolumncounts} to let the
+base class know how many rows and columns it should display. After these
+requirements are met scrolling is handled entirely by wxHVScrolledWindow. You
+only need to draw the visible part of contents in your {\tt OnPaint()} method
+as usual. You should use 
+\helpref{GetVisibleRowsBegin()}{wxhvscrolledwindowgetvisiblerowsbegin}, 
+\helpref{GetVisibleColumnsBegin()}{wxhvscrolledwindowgetvisiblecolumnsbegin}, 
+\helpref{GetVisibleRowsEnd()}{wxhvscrolledwindowgetvisiblerowsend}, and 
+\helpref{GetVisibleColumnsEnd()}{wxhvscrolledwindowgetvisiblecolumnsend} to
+determine which lines to to display. If physical scrolling is enabled the
+device context origin is shifted by the scroll position (through
+{\tt PrepareDC()}), child windows are moved as the window scrolls, and the
+pixels on the screen are moved to minimize the region that requires painting.
+Physical scrolling is enabled by default.
+
+\wxheading{Derived from}
+
+\helpref{wxPanel}{wxpanel}
+
+\wxheading{Include files}
+
+<wx/vscroll.h>
+
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxHVScrolledWindow::wxHVScrolledWindow}\label{wxhvscrolledwindowctor}
+
+\func{}{wxHVScrolledWindow}{\param{wxWindow* }{parent}, \param{wxWindowID }{id = wxID\_ANY}, \param{const wxPoint\& }{pos = wxDefaultPosition}, \param{const wxSize\& }{size = wxDefaultSize}, \param{long }{style = 0}, \param{const wxString\& }{name = wxPanelNameStr}}
+
+This is the normal constructor, no need to call Create() after using this one.
+
+Note that {\tt wxVSCROLL} and {\tt wxHSCROLL} are always automatically added to
+our style, there is no need to specify them explicitly.
+
+\func{}{wxHVScrolledWindow}{\void}
+
+Default constructor, you must call \helpref{Create()}{wxhvscrolledwindowcreate}
+later.
+
+\wxheading{Parameters}
+
+\docparam{parent}{The parent window, must not be {\tt NULL}}
+
+\docparam{id}{The identifier of this window, {\tt wxID\_ANY} by default}
+
+\docparam{pos}{The initial window position}
+
+\docparam{size}{The initial window size}
+
+\docparam{style}{The window style. There are no special style bits defined for
+this class.}
+
+\docparam{name}{The name for this window; usually not used}
+
+
+\membersection{wxHVScrolledWindow::Create}\label{wxhvscrolledwindowcreate}
+
+\func{bool}{Create}{\param{wxWindow* }{parent}, \param{wxWindowID }{id = wxID\_ANY}, \param{const wxPoint\& }{pos = wxDefaultPosition}, \param{const wxSize\& }{size = wxDefaultSize}, \param{long }{style = 0}, \param{const wxString\& }{name = wxPanelNameStr}}
+
+Same as the \helpref{non default ctor}{wxhvscrolledwindowctor} but returns
+status code: {\tt true} if ok, {\tt false} if the window couldn't have been created.
+
+Just as with the ctor above, both the {\tt wxVSCROLL} and the {\tt wxHSCROLL}
+styles are always used. There is no need to specify either explicitly.
+
+\membersection{wxHVScrolledWindow::EnablePhysicalScrolling}\label{wxhvscrolledwindowenablephysicalscrolling}
+
+\func{\void}{EnablePhysicalScrolling}{\param{bool }{scrolling = true}}
+
+With physical scrolling enabled the device origin is changed properly when a
+wxDC is prepared using {\tt PrepareDC()}, children are actually moved and layed
+out according to the current scroll position, and the contents of the window
+(pixels) are actually moved to reduce the amount of redraw needed.
+
+Physical scrolling is enabled by default but can be disable or re-enabled at
+any time.  An example of when you'd want to disable it would be if you have
+statically positioned graphic elements or children you do not want to move
+while the window is being scrolled.  If you disable physical scrolling you must
+manually adjust positioning for items within the scrolled window yourself.
+Also note that an unprepared wxDC requires you to do the same, regardless of
+the physical scrolling state.
+
+
+\membersection{wxHVScrolledWindow::EstimateTotalHeight}\label{wxhvscrolledwindowestimatetotalheight}
+
+\constfunc{virtual wxCoord}{EstimateTotalHeight}{\void}
+
+This protected function is used internally by wxHVScrolledWindow to estimate the
+total height of the window when 
+\helpref{SetRowColumnCounts}{wxhvscrolledwindowsetrowcolumncounts}
+is called. The default implementation uses the brute force approach if the
+number of the items in the control is small enough. Otherwise, it tries to find
+the average row height using some rows in the beginning, middle and the end.
+
+If it is undesirable to query all these rows (some of which might be never
+shown) just for the total height calculation, you may override the function and
+provide your own guess using a better and/or faster method.
+
+Note that although returning a totally wrong value would still work, it risks
+causing some very strange scrollbar behaviour so this function should really
+try to make the best guess possible.
+
+
+\membersection{wxHVScrolledWindow::EstimateTotalWidth}\label{wxhvscrolledwindowestimatetotalwidth}
+
+\constfunc{virtual wxCoord}{EstimateTotalWidth}{\void}
+
+This protected function is used internally by wxHVScrolledWindow to estimate the
+total width of the window when 
+\helpref{SetRowColumnCounts}{wxhvscrolledwindowsetrowcolumncounts}
+is called. The default implementation uses the brute force approach if the
+number of the items in the control is small enough. Otherwise, it tries to find
+the average column width using some columns in the beginning, middle and the end.
+
+If it is undesirable to query all these columns (some of which might be never
+shown) just for the total width calculation, you may override the function and
+provide your own guess using a better and/or faster method.
+
+Note that although returning a totally wrong value would still work, it risks
+causing some very strange scrollbar behaviour so this function should really
+try to make the best guess possible.
+
+
+\membersection{wxHVScrolledWindow::GetColumnCount}\label{wxhvscrolledwindowgetcolumncount}
+
+\constfunc{wxSize}{GetColumnCount}{\void}
+
+Get the number of columns this window contains (previously set by 
+\helpref{SetRowColumnCounts()}{wxhvscrolledwindowsetrowcolumncounts})
+
+
+\membersection{wxHVScrolledWindow::GetRowCount}\label{wxhvscrolledwindowgetrowcount}
+
+\constfunc{wxSize}{GetRowCount}{\void}
+
+Get the number of rows this window contains (previously set by 
+\helpref{SetRowColumnCounts()}{wxhvscrolledwindowsetrowcolumncounts})
+
+
+\membersection{wxHVScrolledWindow::GetRowColumnCounts}\label{wxhvscrolledwindowgetrowcolumncounts}
+
+\constfunc{wxSize}{GetRowColumnCounts}{\void}
+
+Get the number of rows (X or width) and columns (Y or height) this window
+contains (previously set
+by \helpref{SetRowColumnCounts()}{wxhvscrolledwindowsetrowcolumncounts})
+
+
+\membersection{wxHVScrolledWindow::GetVisibleBegin}\label{wxhvscrolledwindowgetvisiblebegin}
+
+\constfunc{wxPoint}{GetVisibleBegin}{\void}
+
+Returns the indicies of the first visible row (Y) and column (X).
+
+\wxheading{See also}
+
+\helpref{GetVisibleRowsBegin}{wxhvscrolledwindowgetvisiblerowsbegin}, \helpref{GetVisibleColumnsBegin}{wxhvscrolledwindowgetvisiblecolumnsbegin}
+
+
+\membersection{wxHVScrolledWindow::GetVisibleColumnsBegin}\label{wxhvscrolledwindowgetvisiblecolumnsbegin}
+
+\constfunc{size\_t}{GetVisibleColumnsBegin}{\void}
+
+Returns the index of the first currently visible column.
+
+\wxheading{See also}
+
+\helpref{GetVisibleColumnsEnd}{wxhvscrolledwindowgetvisiblecolumnsend}
+
+
+\membersection{wxHVScrolledWindow::GetVisibleColumnsEnd}\label{wxhvscrolledwindowgetvisiblecolumnsend}
+
+\constfunc{size\_t}{GetVisibleColumnsEnd}{\void}
+
+Returns the index of the first column after the currently visible page. If the
+return value is $0$ it means that no columns are currently shown (which only
+happens if the control is empty). Note that the index returned by this method
+is not always a valid index as it may be equal to 
+\helpref{GetColumnCount}{wxhvscrolledwindowgetcolumncount}.
+
+\wxheading{See also}
+
+\helpref{GetVisibleColumnsBegin}{wxhvscrolledwindowgetvisiblecolumnsbegin}
+
+
+\membersection{wxHVScrolledWindow::GetVisibleEnd}\label{wxhvscrolledwindowgetvisiblebegin}
+
+\constfunc{wxPoint}{GetVisibleEnd}{\void}
+
+Returns the indicies of the row and column after the last visible row (Y) and
+last visible column (X), respectively.
+
+\wxheading{See also}
+
+\helpref{GetVisibleRowsEnd}{wxhvscrolledwindowgetvisiblerowsend}, \helpref{GetVisibleColumnsEnd}{wxhvscrolledwindowgetvisiblecolumnsend}
+
+
+\membersection{wxHVScrolledWindow::GetVisibleRowsBegin}\label{wxhvscrolledwindowgetvisiblerowsbegin}
+
+\constfunc{size\_t}{GetVisibleRowsBegin}{\void}
+
+Returns the index of the first currently visible row.
+
+\wxheading{See also}
+
+\helpref{GetVisibleRowsEnd}{wxhvscrolledwindowgetvisiblerowsend}
+
+
+\membersection{wxHVScrolledWindow::GetVisibleRowsEnd}\label{wxhvscrolledwindowgetvisiblerowsend}
+
+\constfunc{size\_t}{GetVisibleRowsEnd}{\void}
+
+Returns the index of the first row after the currently visible page. If the
+return value is $0$ it means that no rows are currently shown (which only
+happens if the control is empty). Note that the index returned by this method
+is not always a valid index as it may be equal to 
+\helpref{GetRowCount}{wxhvscrolledwindowgetrowcount}.
+
+\wxheading{See also}
+
+\helpref{GetVisibleRowsBegin}{wxhvscrolledwindowgetvisiblerowsbegin}
+
+
+\membersection{wxHVScrolledWindow::HitTest}\label{wxhvscrolledwindowhittest}
+
+\constfunc{wxPoint}{HitTest}{\param{wxCoord }{x}, \param{wxCoord }{y}}
+
+\constfunc{wxPoint}{HitTest}{\param{const wxPoint\& }{pt}}
+
+Return the position (X as column, Y as row) of the cell occupying the specified
+position (in physical coordinates). A value of {\tt wxNOT\_FOUND} in either X,
+Y, or X and Y means it is outside the range availible rows and/or columns.
+
+
+\membersection{wxHVScrolledWindow::IsColumnVisible}\label{wxhvscrolledwindowiscolumnvisible}
+
+\constfunc{bool}{IsColumnVisible}{\param{size\_t}{column}}
+
+Returns {\tt true} if the given column is at least partially visible or
+{\tt false} otherwise.
+
+
+\membersection{wxHVScrolledWindow::IsRowVisible}\label{wxhvscrolledwindowisrowvisible}
+
+\constfunc{bool}{IsRowVisible}{\param{size\_t }{row}}
+
+Returns {\tt true} if the given row is at least partially visible or {\tt false}
+otherwise.
+
+
+\membersection{wxHVScrolledWindow::IsVisible}\label{wxhvscrolledwindowisvisible}
+
+\constfunc{bool}{IsVisible}{\param{size\_t }{row}, \param{size\_t}{column}}
+
+Returns {\tt true} if the given row and column are both at least partially
+visible or {\tt false} otherwise.
+
+
+\membersection{wxHVScrolledWindow::OnGetColumnWidth}\label{wxhvscrolledwindowongetcolumnwidth}
+
+\constfunc{wxCoord}{OnGetColumnWidth}{\param{size\_t }{n}}
+
+This protected pure virtual function must be overridden in the derived class
+and should return the width of the given column in pixels.
+
+\wxheading{See also}
+
+\helpref{OnGetColumnsWidthHint}{wxhvscrolledwindowongetcolumnswidthhint}
+
+
+\membersection{wxHVScrolledWindow::OnGetColumnsWidthHint}\label{wxhvscrolledwindowongetcolumnswidthhint}
+
+\constfunc{void}{OnGetColumnsWidthHint}{\param{size\_t }{columnMin}, \param{size\_t }{columnMax}}
+
+This function doesn't have to be overridden but it may be useful to do
+it if calculating the columns' heights is a relatively expensive operation
+as it gives the user code a possibility to calculate several of them at
+once.
+
+{\tt OnGetColumnsWidthHint()} is normally called just before 
+\helpref{OnGetColumnWidth()}{wxhvscrolledwindowongetcolumnwidth} but you
+shouldn't rely on the latter being called for all columns in the interval
+specified here. It is also possible that OnGetColumnWidth() will be
+called for the columns outside of this interval, so this is really just a
+hint, not a promise.
+
+Finally note that {\it columnMin} is inclusive, while {\it columnMax} is exclusive,
+as usual.
+
+
+\membersection{wxHVScrolledWindow::OnGetRowHeight}\label{wxhvscrolledwindowongetrowheight}
+
+\constfunc{wxCoord}{OnGetRowHeight}{\param{size\_t }{n}}
+
+This protected pure virtual function must be overridden in the derived class
+and should return the height of the given row in pixels.
+
+\wxheading{See also}
+
+\helpref{OnGetRowsHeightHint}{wxhvscrolledwindowongetrowsheighthint}
+
+
+\membersection{wxHVScrolledWindow::OnGetRowsHeightHint}\label{wxhvscrolledwindowongetrowsheighthint}
+
+\constfunc{void}{OnGetRowsHeightHint}{\param{size\_t }{rowMin}, \param{size\_t }{rowMax}}
+
+This function doesn't have to be overridden but it may be useful to do
+it if calculating the row's heights is a relatively expensive operation
+as it gives the user code a possibility to calculate several of them at
+once.
+
+{\tt OnGetRowsHeightHint()} is normally called just before 
+\helpref{OnGetRowHeight()}{wxhvscrolledwindowongetrowheight} but you
+shouldn't rely on the latter being called for all rows in the interval
+specified here. It is also possible that OnGetRowHeight() will be
+called for the rows outside of this interval, so this is really just a
+hint, not a promise.
+
+Finally note that {\it rowMin} is inclusive, while {\it rowMax} is exclusive,
+as usual.
+
+
+\membersection{wxHVScrolledWindow::RefreshColumn}\label{wxhvscrolledwindowrefreshcolumn}
+
+\func{void}{RefreshColumn}{\param{size\_t }{column}}
+
+Refreshes the specified column -- it will be redrawn during the next main loop
+iteration.
+
+
+\membersection{wxHVScrolledWindow::RefreshRow}\label{wxhvscrolledwindowrefreshrow}
+
+\func{void}{RefreshRow}{\param{size\_t }{row}}
+
+Refreshes the specified row -- it will be redrawn during the next main loop
+iteration.
+
+
+\membersection{wxHVScrolledWindow::RefreshRowColumn}\label{wxhvscrolledwindowrefreshrowcolumn}
+
+\func{void}{RefreshRowColumn}{\param{size\_t }{row}, \param{size\_t }{column}}
+
+Refreshes the specified cell -- it will be redrawn during the next main loop
+iteration.
+
+\wxheading{See also}
+
+\helpref{RefreshRowsColumns}{wxhvscrolledwindowrefreshrowscolumns}
+
+
+\membersection{wxHVScrolledWindow::RefreshColumns}\label{wxhvscrolledwindowrefreshcolumns}
+
+\func{void}{RefreshColumns}{\param{size\_ t}{fromColumn}, \param{size\_t }{toColumn}}
+
+Refreshes the columns between {\it fromColumn} and {\it toColumn} (inclusive).
+{\it fromColumn} should be less than or equal to {\it toColumn}.
+
+\wxheading{See also}
+
+\helpref{RefreshColumn}{wxhvscrolledwindowrefreshcolumn}
+
+
+\membersection{wxHVScrolledWindow::RefreshRows}\label{wxhvscrolledwindowrefreshrows}
+
+\func{void}{RefreshRows}{\param{size\_ t}{fromRow}, \param{size\_t }{toRow}}
+
+Refreshes the rows between {\it fromRow} and {\it toRow} (inclusive).
+{\it fromRow} should be less than or equal to {\it toRow}.
+
+\wxheading{See also}
+
+\helpref{RefreshRow}{wxhvscrolledwindowrefreshrow}
+
+
+\membersection{wxHVScrolledWindow::RefreshRowsColumns}\label{wxhvscrolledwindowrefreshrowscolumns}
+
+\func{void}{RefreshRowsColumns}{\param{size\_t }{fromRow}, \param{size\_t }{toRow}, \param{size\_ t}{fromColumn}, \param{size\_t }{toColumn}}
+
+Refreshes the region of cells between {\it fromRow}, {\it fromColumn} and
+{\it toRow}, {\it toColumn} (inclusive). {\it fromRow} and {\it fromColumn}
+should be less than or equal to {\it toRow} and {\it toColumn}, respectively.
+
+\wxheading{See also}
+
+\helpref{RefreshRowColumn}{wxhvscrolledwindowrefreshrowcolumn}
+
+
+\membersection{wxHVScrolledWindow::RefreshAll}\label{wxhvscrolledwindowrefreshall}
+
+\func{void}{RefreshAll}{\void}
+
+This function completely refreshes the control, recalculating the number of
+items shown on screen and repainting them. It should be called when the values
+returned by either \helpref{OnGetRowHeight}{wxhvscrolledwindowongetrowheight} or 
+\helpref{OnGetColumnWidth}{wxhvscrolledwindowongetcolumnwidth} change for some
+reason and the window must be updated to reflect this.
+
+
+\membersection{wxHVScrolledWindow::ScrollColumns}\label{wxhvscrolledwindowscrollcolumns}
+
+\func{bool}{ScrollColumns}{\param{int }{columns}}
+
+Scroll by the specified number of columns which may be positive (to scroll
+right) or negative (to scroll left).
+
+Returns {\tt true} if the window was scrolled, {\tt false} otherwise (for
+example if we're trying to scroll right but we are already showing the last
+column).
+
+
+\membersection{wxHVScrolledWindow::ScrollRows}\label{wxhvscrolledwindowscrollrows}
+
+\func{bool}{ScrollRows}{\param{int }{rows}}
+
+Scroll by the specified number of rows which may be positive (to scroll
+down) or negative (to scroll up).
+
+Returns {\tt true} if the window was scrolled, {\tt false} otherwise (for
+example if we're trying to scroll down but we are already showing the last
+row).
+
+\wxheading{See also}
+
+\helpref{LineUp}{wxwindowlineup}, \helpref{LineDown}{wxwindowlinedown}
+
+
+\membersection{wxHVScrolledWindow::ScrollRowsColumns}\label{wxhvscrolledwindowscrollrowscolumns}
+
+\func{bool}{ScrollRowsColumns}{\param{int }{rows}, \param{int }{columns}}
+
+Scroll by the specified number of rows and columns which may be positive (to
+scroll down or right) or negative (to scroll up or left).
+
+Returns {\tt true} if the window was scrolled, {\tt false} otherwise (for
+example if we're trying to scroll down but we are already showing the last
+row).
+
+\wxheading{See also}
+
+\helpref{LineUp}{wxwindowlineup}, \helpref{LineDown}{wxwindowlinedown}
+
+
+\membersection{wxHVScrolledWindow::ScrollColumnPages}\label{wxhvscrolledwindowscrollcolumnpages}
+
+\func{bool}{ScrollColumnPages}{\param{int }{columnPages}}
+
+Scroll by the specified number of column pages, which may be positive (to
+scroll right) or negative (to scroll left).
+
+
+\membersection{wxHVScrolledWindow::ScrollPages}\label{wxhvscrolledwindowscrollpages}
+
+\func{bool}{ScrollPages}{\param{int }{rowPages}, \param{int }{columnPages}}
+
+Scroll by the specified number of row pages and column pages, both of which may
+be positive (to scroll down or right) or negative (to scroll up or left).
+
+\wxheading{See also}
+
+\helpref{ScrollRowsColumns}{wxhvscrolledwindowscrollrowscolumns},\\
+\helpref{PageUp}{wxwindowpageup}, \helpref{PageDown}{wxwindowpagedown}
+
+
+\membersection{wxHVScrolledWindow::ScrollRowPages}\label{wxhvscrolledwindowscrollrowpages}
+
+\func{bool}{ScrollRowPages}{\param{int }{rowPages}}
+
+Scroll by the specified number of row pages, which may be positive (to scroll
+down) or negative (to scroll up).
+
+\wxheading{See also}
+
+\helpref{PageUp}{wxwindowpageup}, \helpref{PageDown}{wxwindowpagedown}
+
+
+\membersection{wxHVScrolledWindow::ScrollToColumn}\label{wxhvscrolledwindowscrolltocolumn}
+
+\func{bool}{ScrollToColumn}{\param{size\_t }{column}}
+
+Scroll to the specified column. The specified column will be the first visible
+column on the left side afterwards.
+
+Return {\tt true} if we scrolled the window, {\tt false} if nothing was done.
+
+
+\membersection{wxHVScrolledWindow::ScrollToRow}\label{wxhvscrolledwindowscrolltorow}
+
+\func{bool}{ScrollToRow}{\param{size\_t }{row}}
+
+Scroll to the specified row. The specified column will be the first visible row
+on the top afterwards.
+
+Return {\tt true} if we scrolled the window, {\tt false} if nothing was done.
+
+
+\membersection{wxHVScrolledWindow::ScrollToRowColumn}\label{wxhvscrolledwindowscrolltorowcolumn}
+
+\func{bool}{ScrollToRowColumn}{\param{size\_t }{row}, \param{size\_t }{column}}
+
+Scroll to the specified row and column. The cell described will be the top left
+visible cell afterwards.
+
+Return {\tt true} if we scrolled the window, {\tt false} if nothing was done.
+
+
+\membersection{wxHVScrolledWindow::SetRowColumnCounts}\label{wxhvscrolledwindowsetrowcolumncounts}
+
+\func{void}{SetLineCount}{\param{size\_t }{row}, \param{size\_t }{column}}
+
+Set the number of rows and columns the window contains. The derived class must
+provide the heights for all rows and the widths for all columns with indices up
+to the respective values given here in its 
+\helpref{OnGetRowHeight()}{wxhvscrolledwindowongetrowheight} and 
+\helpref{OnGetColumnWidth()}{wxhvscrolledwindowongetcolumnwidth}
+implementations.
diff --git a/docs/latex/wx/vscroll.tex b/docs/latex/wx/vscroll.tex
index d304d6f172..95892a7a01 100644
--- a/docs/latex/wx/vscroll.tex
+++ b/docs/latex/wx/vscroll.tex
@@ -159,7 +159,7 @@ Returns the index of the first currently visible line.
 Returns the index of the first line after the currently visible one. If the
 return value is $0$ it means that no lines are currently shown (which only
 happens if the control is empty). Note that the index returned by this method
-is not always a valid index as it may be equal to \helpref{GetLineCount}{wxvscrolledwindowsetlinecount}.
+is not always a valid index as it may be equal to \helpref{GetLineCount}{wxvscrolledwindowgetlinecount}.
 
 \wxheading{See also}
 
diff --git a/include/wx/vscroll.h b/include/wx/vscroll.h
index b24615f27e..bead562926 100644
--- a/include/wx/vscroll.h
+++ b/include/wx/vscroll.h
@@ -2,7 +2,7 @@
 // Name:        include/wx/vscroll.h
 // Purpose:     wxVScrolledWindow: generalization of wxScrolledWindow
 // Author:      Vadim Zeitlin
-// Modified by:
+// Modified by: Brad Anderson
 // Created:     30.05.03
 // RCS-ID:      $Id$
 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwidgets.org>
@@ -135,6 +135,8 @@ public:
     // is kept for backwards compatibility
     size_t GetLastVisibleLine() const { return GetVisibleEnd() - 1; }
 
+    // layout the children (including the sizer if needed)
+    virtual bool Layout();
 
 protected:
     // this function must be overridden in the derived class and it should
@@ -214,5 +216,270 @@ private:
     DECLARE_ABSTRACT_CLASS(wxVScrolledWindow)
 };
 
+
+// ----------------------------------------------------------------------------
+// wxHVScrolledWindow
+// ----------------------------------------------------------------------------
+
+/*
+   This class is strongly influenced by wxVScrolledWindow. In fact, much of
+   code is line for line the same except it explicitly states which axis is
+   being worked on.  Like wxVScrolledWindow, this class is here to provide
+   an easy way to implement variable line sizes.  The difference is that
+   wxVScrolledWindow only works with vertical scrolling.  This class extends
+   the behavior of wxVScrolledWindow to the horizontal axis in addition to the
+   vertical axis.
+
+   The scrolling is also "virtual" in the sense that line widths and heights
+   only need to be known for lines that are currently visible.
+
+   Like wxVScrolledWindow, this is a generalization of the wxScrolledWindow
+   class which can be only used when all horizontal lines have the same width
+   and all of the vertical lines have the same height. Like wxVScrolledWinow
+   it lacks some of wxScrolledWindow features such as scrolling another window
+   or only scrolling a rectangle of the window and not its entire client area.
+
+   If only vertical scrolling is needed, wxVScrolledWindow is recommended
+   because it is simpler to use (and you get to type less).
+   
+   There is no wxHScrolledWindow but horizontal only scrolling is implemented
+   easily enough with this class.  If someone feels the need for such a class,
+   implementing it is trivial.
+ */
+class WXDLLEXPORT wxHVScrolledWindow : public wxPanel
+{
+public:
+    // constructors and such
+    // ---------------------
+
+    // default ctor, you must call Create() later
+    wxHVScrolledWindow() { Init(); }
+
+    // normal ctor, no need to call Create() after this one
+    //
+    // note that wxVSCROLL and wxHSCROLL are always automatically added to our
+    // style, there is no need to specify them explicitly
+    wxHVScrolledWindow(wxWindow *parent,
+                       wxWindowID id = wxID_ANY,
+                       const wxPoint& pos = wxDefaultPosition,
+                       const wxSize& size = wxDefaultSize,
+                       long style = 0,
+                       const wxString& name = wxPanelNameStr)
+    {
+        Init();
+
+        (void)Create(parent, id, pos, size, style, name);
+    }
+
+    // same as the previous ctor but returns status code: true if ok
+    //
+    // just as with the ctor above, wxVSCROLL and wxHSCROLL styles are always
+    // used, there is no need to specify them
+    bool Create(wxWindow *parent,
+                wxWindowID id = wxID_ANY,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                long style = 0,
+                const wxString& name = wxPanelNameStr)
+    {
+        return wxPanel::Create(parent, id, pos, size,
+                style | wxVSCROLL | wxHSCROLL, name);
+    }
+
+
+    // operations
+    // ----------
+
+    // set the number of lines the window contains for each axis: the derived
+    // class must provide the widths and heights for all lines with indices up
+    // to each of the one given here in its OnGetColumnWidth() and
+    // OnGetRowHeight()
+    void SetRowColumnCounts(size_t rowCount, size_t columnCount);
+
+    // with physical scrolling on, the device origin is changed properly when
+    // a wxPaintDC is prepared, children are actually moved and layed out
+    // properly, and the contents of the window (pixels) are actually moved
+    void EnablePhysicalScrolling(bool scrolling = true)
+        { m_physicalScrolling = scrolling; }
+
+    // scroll to the specified line: it will become the first visible line in
+    // the window
+    //
+    // return true if we scrolled the window, false if nothing was done
+    bool ScrollToRow(size_t row);
+    bool ScrollToColumn(size_t column);
+    bool ScrollToRowColumn(size_t row, size_t column);
+
+    // scroll by the specified number of lines/pages
+    virtual bool ScrollRows(int rows);
+    virtual bool ScrollColumns(int columns);
+    virtual bool ScrollRowsColumns(int rows, int columns);
+    virtual bool ScrollRowPages(int pages);
+    virtual bool ScrollColumnPages(int pages);
+    virtual bool ScrollPages(int rowPages, int columnPages);
+
+    // redraw the specified line
+    virtual void RefreshRow(size_t line);
+    virtual void RefreshColumn(size_t line);
+    virtual void RefreshRowColumn(size_t row, size_t column);
+
+    // redraw all lines in the specified range (inclusive)
+    virtual void RefreshRows(size_t from, size_t to);
+    virtual void RefreshColumns(size_t from, size_t to);
+    virtual void RefreshRowsColumns(size_t fromRow, size_t toRow,
+                                    size_t fromColumn, size_t toColumn);
+
+    // return the horizontal and vertical line within a wxPoint at the
+    // specified (in physical coordinates) position or.
+
+    // wxNOT_FOUND in either or both axes if no line is present at the
+    // requested coordinates, i.e. if it is past the last lines
+    wxPoint HitTest(wxCoord x, wxCoord y) const;
+    wxPoint HitTest(const wxPoint& pt) const { return HitTest(pt.x, pt.y); }
+
+    // recalculate all our parameters and redisplay all lines
+    virtual void RefreshAll();
+
+
+    // accessors
+    // ---------
+
+    // get the number of lines this window contains (previously set by
+    // SetLineCount())
+    size_t GetRowCount() const { return m_rowsMax; }
+    size_t GetColumnCount() const { return m_columnsMax; }
+    wxSize GetRowColumnCounts() const
+        { return wxSize((int)m_columnsMax, (int)m_rowsMax); }
+
+    // get the first currently visible line/lines
+    size_t GetVisibleRowsBegin() const { return m_rowsFirst; }
+    size_t GetVisibleColumnsBegin() const { return m_columnsFirst; }
+    wxPoint GetVisibleBegin() const
+        { return wxPoint((int)m_columnsFirst, (int)m_rowsFirst); }
+
+    // get the last currently visible line/lines
+    size_t GetVisibleRowsEnd() const
+        { return m_rowsFirst + m_nRowsVisible; }
+    size_t GetVisibleColumnsEnd() const
+        { return m_columnsFirst + m_nColumnsVisible; }
+    wxPoint GetVisibleEnd() const
+        { return wxPoint((int)(m_columnsFirst + m_nColumnsVisible),
+                         (int)(m_rowsFirst + m_nRowsVisible)); }
+
+    // is this line currently visible?
+    bool IsRowVisible(size_t row) const
+        { return row >= m_rowsFirst &&
+                 row < GetVisibleRowsEnd(); }
+    bool IsColumnVisible(size_t column) const
+        { return column >= m_columnsFirst &&
+                 column < GetVisibleColumnsEnd(); }
+    bool IsVisible(size_t row, size_t column) const
+        { return IsRowVisible(row) && IsColumnVisible(column); }
+
+    // layout the children (including the sizer if needed)
+    virtual bool Layout();
+
+protected:
+    // these functions must be overridden in the derived class and they should
+    // return the width or height of the given line in pixels
+    virtual wxCoord OnGetRowHeight(size_t n) const = 0;
+    virtual wxCoord OnGetColumnWidth(size_t n) const = 0;
+    
+    // the following functions don't need to be overridden but it may be useful
+    // to do if calculating the lines widths or heights is a relatively
+    // expensive operation as it gives the user code a possibility to calculate
+    // several of them at once
+    //
+    // OnGetRowsHeightHint() and OnGetColumnsWidthHint() are normally called
+    // just before OnGetRowHeight() and OnGetColumnWidth(), respectively, but
+    // you shouldn't rely on the latter methods being called for all lines in
+    // the interval specified here. It is also possible that OnGetRowHeight()
+    // or OnGetColumnWidth() will be called for the lines outside of this
+    // interval, so this is really just a hint, not a promise.
+    //
+    // finally note that min is inclusive, while max is exclusive, as usual
+    virtual void OnGetRowsHeightHint(size_t WXUNUSED(rowMin),
+                                     size_t WXUNUSED(rowMax)) const { }
+    virtual void OnGetColumnsWidthHint(size_t WXUNUSED(columnMin),
+                                       size_t WXUNUSED(columnMax)) const { }
+    
+    // when the number of lines changes, we try to estimate the total width or
+    // height of all lines which is a rather expensive operation in terms of
+    // lines access, so if the user code may estimate the average height
+    // better/faster than we do, it should override this function to implement
+    // its own logic
+    //
+    // this function should return the best guess for the total height it may
+    // make
+    virtual wxCoord EstimateTotalHeight() const;
+    virtual wxCoord EstimateTotalWidth() const;
+
+    // the event handlers
+    void OnSize(wxSizeEvent& event);
+    void OnScroll(wxScrollWinEvent& event);
+#if wxUSE_MOUSEWHEEL
+    void OnMouseWheel(wxMouseEvent& event);
+#endif
+
+
+    // find the index of the horizontal line we need to show at the top of the
+    // window such that the last (fully or partially) visible line is the given
+    // one
+    size_t FindFirstFromRight(size_t columnLast, bool fullyVisible = false);
+
+    // find the index of the vertical line we need to show at the top of the
+    // window such that the last (fully or partially) visible line is the given
+    // one
+    size_t FindFirstFromBottom(size_t rowLast, bool fullyVisible = false);
+
+
+    // get the total width or height of the lines between lineMin (inclusive)
+    // and lineMax (exclusive)
+    wxCoord GetRowsHeight(size_t rowMin, size_t rowMax) const;
+    wxCoord GetColumnsWidth(size_t columnMin, size_t columnMax) const;
+
+    // update the thumb size shown by the scrollbar
+    void UpdateScrollbars();
+
+    // shifts the specified dc by the scroll position
+    void PrepareDC(wxDC& dc);
+
+private:
+    // common part of all ctors
+    void Init();
+
+
+    // the total number of (logical) lines for each axis
+    size_t m_rowsMax;
+    size_t m_columnsMax;
+
+    // the total (estimated) height
+    wxCoord m_heightTotal;
+
+    // the total (estimated) width
+    wxCoord m_widthTotal;
+
+    // the first currently visible line on each axis
+    size_t m_rowsFirst;
+    size_t m_columnsFirst;
+
+    // the number of currently visible lines for each axis (including the last,
+    // possibly only partly, visible one)
+    size_t m_nRowsVisible;
+    size_t m_nColumnsVisible;
+
+    // accumulated mouse wheel rotation
+#if wxUSE_MOUSEWHEEL
+    int m_sumWheelRotation;
+#endif
+
+    // do child scrolling (used in DoPrepareDC())
+    bool m_physicalScrolling;
+
+    DECLARE_EVENT_TABLE()
+    DECLARE_NO_COPY_CLASS(wxHVScrolledWindow)
+    DECLARE_ABSTRACT_CLASS(wxHVScrolledWindow)
+};
+
 #endif // _WX_VSCROLL_H_
 
diff --git a/samples/vscroll/vstest.cpp b/samples/vscroll/vstest.cpp
index ff4b716705..2e23b2ec41 100644
--- a/samples/vscroll/vstest.cpp
+++ b/samples/vscroll/vstest.cpp
@@ -2,7 +2,7 @@
 // Name:        samples/vscroll/vstest.cpp
 // Purpose:     VScroll wxWidgets sample
 // Author:      Vadim Zeitlin
-// Modified by:
+// Modified by: Brad Anderson
 // Created:     04/01/98
 // RCS-ID:      $Id$
 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwidgets.org>
@@ -44,12 +44,18 @@
     #include "../sample.xpm"
 #endif
 
+// ----------------------------------------------------------------------------
+// definitions
+// ----------------------------------------------------------------------------
+
+#define MAX_LINES 200
+
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
 
 // Define a new application type, each program should derive a class from wxApp
-class VScrollApp : public wxApp
+class VarScrollApp : public wxApp
 {
 public:
     // create our main window
@@ -57,14 +63,16 @@ public:
 };
 
 // Define a new frame type: this is going to be our main frame
-class VScrollFrame : public wxFrame
+class VarScrollFrame : public wxFrame
 {
 public:
     // ctor
-    VScrollFrame();
+    VarScrollFrame();
 
     // event handlers (these functions should _not_ be virtual)
     void OnQuit(wxCommandEvent& event);
+    void OnModeVScroll(wxCommandEvent& event);
+    void OnModeHVScroll(wxCommandEvent& event);
     void OnAbout(wxCommandEvent& event);
 
     void OnSize(wxSizeEvent& event)
@@ -84,6 +92,9 @@ public:
 private:
     // any class wishing to process wxWidgets events must use this macro
     DECLARE_EVENT_TABLE()
+
+    // either a wxVScrolledWindow or a wxHVScrolled window, depending on current mode
+    wxPanel *m_scrollWindow;
 };
 
 class VScrollWindow : public wxVScrolledWindow
@@ -93,8 +104,12 @@ public:
     {
         m_frame = frame;
 
-        SetLineCount(200);
+        SetLineCount(MAX_LINES);
 
+        int i;
+        for ( i = 0; i < MAX_LINES; ++i )
+            m_heights[i] = rand()%25+16; // low: 15; high: 40
+    
         m_changed = true;
     }
 
@@ -116,21 +131,23 @@ public:
     {
         wxPaintDC dc(this);
 
-        dc.SetPen(*wxBLACK_DASHED_PEN);
+        dc.SetPen(*wxBLACK_PEN);
 
-        const size_t lineFirst = GetFirstVisibleLine(),
-                     lineLast = GetLastVisibleLine();
+        const size_t lineFirst = GetVisibleBegin(),
+                     lineLast = GetVisibleEnd();
 
         const wxCoord hText = dc.GetCharHeight();
 
+        wxSize clientSize = GetClientSize();
+
         wxCoord y = 0;
-        for ( size_t line = lineFirst; line <= lineLast; line++ )
+        for ( size_t line = lineFirst; line < lineLast; line++ )
         {
-            dc.DrawLine(0, y, 1000, y);
+            dc.DrawLine(0, y, clientSize.GetWidth(), y);
 
             wxCoord hLine = OnGetLineHeight(line);
             dc.DrawText(wxString::Format(_T("Line %lu"), (unsigned long)line),
-                        0, y + (hLine - hText) / 2);
+                        2, y + (hLine - hText) / 2);
 
             y += hLine;
             dc.DrawLine(0, y, 1000, y);
@@ -149,12 +166,14 @@ public:
     {
         wxASSERT( n < GetLineCount() );
 
-        return n % 2 ? 15 : 30; // 15 + 2*n
+        return m_heights[n];
     }
 
 private:
     wxFrame *m_frame;
 
+    int m_heights[MAX_LINES];
+
     bool m_changed;
 
     DECLARE_EVENT_TABLE()
@@ -166,6 +185,126 @@ BEGIN_EVENT_TABLE(VScrollWindow, wxVScrolledWindow)
     EVT_SCROLLWIN(VScrollWindow::OnScroll)
 END_EVENT_TABLE()
 
+class HVScrollWindow : public wxHVScrolledWindow
+{
+public:
+    HVScrollWindow(wxFrame *frame) : wxHVScrolledWindow(frame, wxID_ANY)
+    {
+        m_frame = frame;
+
+        SetRowColumnCounts(MAX_LINES, MAX_LINES);
+        
+        int i;
+        for ( i = 0; i < MAX_LINES; ++i )
+        {
+            m_heights[i] = rand()%30+31; // low: 30; high: 60
+            m_widths[i] = rand()%30+61;  // low: 60; high: 90
+        }
+
+        m_changed = true;
+    }
+
+    void OnIdle(wxIdleEvent&)
+    {
+#if wxUSE_STATUSBAR
+        m_frame->SetStatusText(wxString::Format
+                               (
+                                    _T("Page size = %d rows %d columns; pos = row: %d, column: %d; max = %d rows, %d columns"),
+                                    GetScrollThumb(wxVERTICAL),
+                                    GetScrollThumb(wxHORIZONTAL),
+                                    GetScrollPos(wxVERTICAL),
+                                    GetScrollPos(wxHORIZONTAL),
+                                    GetScrollRange(wxVERTICAL),
+                                    GetScrollRange(wxHORIZONTAL)
+                               ));
+#endif // wxUSE_STATUSBAR
+        m_changed = false;
+    }
+
+    void OnPaint(wxPaintEvent&)
+    {
+        wxPaintDC dc(this);
+
+        dc.SetPen(*wxBLACK_PEN);
+
+        const size_t rowFirst = GetVisibleRowsBegin(),
+                     rowLast = GetVisibleRowsEnd();
+        const size_t columnFirst = GetVisibleColumnsBegin(),
+                     columnLast = GetVisibleColumnsEnd();
+        
+        const wxCoord hText = dc.GetCharHeight();
+
+        wxSize clientSize = GetClientSize();
+
+        wxCoord y = 0;
+        wxCoord x = 0;
+        for ( size_t row = rowFirst; row < rowLast; row++ )
+        {
+            wxCoord rowHeight = OnGetRowHeight(row);
+            dc.DrawLine(0, y, clientSize.GetWidth(), y);
+
+            x = 0;
+            for ( size_t col = columnFirst; col < columnLast; col++ )
+            {
+                wxCoord colWidth = OnGetColumnWidth(col);
+
+                if ( row == rowFirst )
+                    dc.DrawLine(x, 0, x, clientSize.GetHeight());
+
+                dc.DrawText(wxString::Format(_T("Row %lu"), (unsigned long)row),
+                            x + 2, y + rowHeight / 2 - hText);
+                dc.DrawText(wxString::Format(_T("Col %lu"), (unsigned long)col),
+                            x + 2, y + rowHeight / 2);
+                
+                x += colWidth;
+                if ( row == rowFirst)
+                    dc.DrawLine(x, 0, x, clientSize.GetHeight());
+            }
+            
+            y += rowHeight;
+            dc.DrawLine(0, y, clientSize.GetWidth(), y);
+        }
+    }
+
+    void OnScroll(wxScrollWinEvent& event)
+    {
+        m_changed = true;
+
+        event.Skip();
+    }
+
+
+    virtual wxCoord OnGetRowHeight(size_t n) const
+    {
+        wxASSERT( n < GetRowCount() );
+
+        return m_heights[n];
+    }
+
+    virtual wxCoord OnGetColumnWidth(size_t n) const
+    {
+        wxASSERT( n < GetColumnCount() );
+
+        return m_widths[n];
+    }
+
+private:
+    wxFrame *m_frame;
+
+    int m_heights[MAX_LINES];
+    int m_widths[MAX_LINES];
+
+    bool m_changed;
+
+    DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(HVScrollWindow, wxHVScrolledWindow)
+    EVT_IDLE(HVScrollWindow::OnIdle)
+    EVT_PAINT(HVScrollWindow::OnPaint)
+    EVT_SCROLLWIN(HVScrollWindow::OnScroll)
+END_EVENT_TABLE()
+
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
@@ -179,7 +318,10 @@ enum
     // it is important for the id corresponding to the "About" command to have
     // this standard value as otherwise it won't be handled properly under Mac
     // (where it is special and put into the "Apple" menu)
-    VScroll_About = wxID_ABOUT
+    VScroll_About = wxID_ABOUT,
+
+    VScroll_VScrollMode = wxID_HIGHEST + 1,
+    VScroll_HVScrollMode
 };
 
 // ----------------------------------------------------------------------------
@@ -189,18 +331,20 @@ enum
 // the event tables connect the wxWidgets events with the functions (event
 // handlers) which process them. It can be also done at run-time, but for the
 // simple menu events like this the static method is much simpler.
-BEGIN_EVENT_TABLE(VScrollFrame, wxFrame)
-    EVT_MENU(VScroll_Quit,  VScrollFrame::OnQuit)
-    EVT_MENU(VScroll_About, VScrollFrame::OnAbout)
-    EVT_SIZE(VScrollFrame::OnSize)
+BEGIN_EVENT_TABLE(VarScrollFrame, wxFrame)
+    EVT_MENU(VScroll_Quit,  VarScrollFrame::OnQuit)
+    EVT_MENU(VScroll_VScrollMode, VarScrollFrame::OnModeVScroll)
+    EVT_MENU(VScroll_HVScrollMode, VarScrollFrame::OnModeHVScroll)
+    EVT_MENU(VScroll_About, VarScrollFrame::OnAbout)
+    EVT_SIZE(VarScrollFrame::OnSize)
 END_EVENT_TABLE()
 
 // Create a new application object: this macro will allow wxWidgets to create
 // the application object during program execution (it's better than using a
 // static object for many reasons) and also declares the accessor function
-// wxGetApp() which will return the reference of the right type (i.e. VScrollApp and
+// wxGetApp() which will return the reference of the right type (i.e. VarScrollApp and
 // not wxApp)
-IMPLEMENT_APP(VScrollApp)
+IMPLEMENT_APP(VarScrollApp)
 
 // ============================================================================
 // implementation
@@ -211,10 +355,10 @@ IMPLEMENT_APP(VScrollApp)
 // ----------------------------------------------------------------------------
 
 // 'Main program' equivalent: the program execution "starts" here
-bool VScrollApp::OnInit()
+bool VarScrollApp::OnInit()
 {
     // create the main application window
-    VScrollFrame *frame = new VScrollFrame;
+    VarScrollFrame *frame = new VarScrollFrame;
 
     // and show it (the frames, unlike simple controls, are not shown when
     // created initially)
@@ -229,12 +373,13 @@ bool VScrollApp::OnInit()
 // ----------------------------------------------------------------------------
 
 // frame constructor
-VScrollFrame::VScrollFrame()
-            : wxFrame(NULL,
-                      wxID_ANY,
-                      _T("VScroll wxWidgets Sample"),
-                      wxDefaultPosition,
-                      wxSize(400, 350))
+VarScrollFrame::VarScrollFrame()
+               : wxFrame(NULL,
+                         wxID_ANY,
+                         _T("VScroll wxWidgets Sample"),
+                         wxDefaultPosition,
+                         wxSize(400, 350)),
+                 m_scrollWindow(NULL)
 {
     // set the frame icon
     SetIcon(wxICON(sample));
@@ -243,15 +388,32 @@ VScrollFrame::VScrollFrame()
     // create a menu bar
     wxMenu *menuFile = new wxMenu;
 
+    wxMenu *menuMode = new wxMenu;
+
     // the "About" item should be in the help menu
     wxMenu *menuHelp = new wxMenu;
     menuHelp->Append(VScroll_About, _T("&About...\tF1"), _T("Show about dialog"));
 
+#ifdef wxHAS_RADIO_MENU_ITEMS
+    menuMode->AppendRadioItem(VScroll_VScrollMode, _T("&Vertical\tAlt-V"),
+                              _T("Vertical scrolling only"));
+    menuMode->AppendRadioItem(VScroll_HVScrollMode,
+                              _T("&Horizontal/Vertical\tAlt-H"),
+                              _T("Horizontal and vertical scrolling"));
+    menuMode->Check(VScroll_VScrollMode, true);
+#else
+    menuMode->Append(VScroll_VScrollMode, _T("&Vertical\tAlt-V"),
+                     _T("Vertical scrolling only"));
+    menuMode->Append(VScroll_HVScrollMode, _T("&Horizontal/Vertical\tAlt-H"),
+                     _T("Horizontal and vertical scrolling"));
+#endif
+
     menuFile->Append(VScroll_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
 
     // now append the freshly created menu to the menu bar...
     wxMenuBar *menuBar = new wxMenuBar;
     menuBar->Append(menuFile, _T("&File"));
+    menuBar->Append(menuMode, _T("&Mode"));
     menuBar->Append(menuHelp, _T("&Help"));
 
     // ... and attach this menu bar to the frame
@@ -262,26 +424,51 @@ VScrollFrame::VScrollFrame()
     // create a status bar just for fun (by default with 1 pane only)
     CreateStatusBar(2);
     SetStatusText(_T("Welcome to wxWidgets!"));
+    int widths[2];
+    widths[0] = -1;
+    widths[1] = 100;
+    SetStatusWidths(2, widths);
 #endif // wxUSE_STATUSBAR
 
     // create our one and only child -- it will take our entire client area
-    new VScrollWindow(this);
+    if ( menuMode->IsChecked(VScroll_VScrollMode) )
+        m_scrollWindow = new VScrollWindow(this);
+    else if ( menuMode->IsChecked(VScroll_VScrollMode) )
+        m_scrollWindow = new HVScrollWindow(this);
 }
 
 // ----------------------------------------------------------------------------
 // event handlers
 // ----------------------------------------------------------------------------
 
-void VScrollFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+void VarScrollFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
 {
     // true is to force the frame to close
     Close(true);
 }
 
-void VScrollFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+void VarScrollFrame::OnModeVScroll(wxCommandEvent& WXUNUSED(event))
+{
+    if ( m_scrollWindow )
+        m_scrollWindow->Destroy();
+    
+    m_scrollWindow = new VScrollWindow(this);
+    SendSizeEvent();
+}
+
+void VarScrollFrame::OnModeHVScroll(wxCommandEvent& WXUNUSED(event))
+{
+    if ( m_scrollWindow )
+        m_scrollWindow->Destroy();
+
+    m_scrollWindow = new HVScrollWindow(this);
+    SendSizeEvent();
+}
+
+void VarScrollFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
 {
     wxMessageBox(_T("VScroll shows how to implement scrolling with\n")
-                 _T("variable line heights.\n")
+                 _T("variable line widths and heights.\n")
                  _T("(c) 2003 Vadim Zeitlin"),
                  _T("About VScroll"),
                  wxOK | wxICON_INFORMATION,
diff --git a/src/generic/vscroll.cpp b/src/generic/vscroll.cpp
index 9267c8aad3..17ec846fa7 100644
--- a/src/generic/vscroll.cpp
+++ b/src/generic/vscroll.cpp
@@ -2,7 +2,7 @@
 // Name:        src/generic/vscroll.cpp
 // Purpose:     wxVScrolledWindow implementation
 // Author:      Vadim Zeitlin
-// Modified by:
+// Modified by: Brad Anderson
 // Created:     30.05.03
 // RCS-ID:      $Id$
 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
@@ -25,6 +25,8 @@
 #endif
 
 #include "wx/vscroll.h"
+#include "wx/sizer.h"
+#include "wx/dc.h"
 
 // ----------------------------------------------------------------------------
 // event tables
@@ -263,6 +265,25 @@ void wxVScrolledWindow::RefreshAll()
     Refresh();
 }
 
+bool wxVScrolledWindow::Layout()
+{
+    if(GetSizer())
+    {
+        // 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
+
+        y = -GetLinesHeight(0, GetFirstVisibleLine());
+        GetVirtualSize(&w, &h);
+        GetSizer()->SetDimension(0, y, w, h);
+        return true;
+    }
+
+    // fall back to default for LayoutConstraints
+    return wxPanel::Layout();
+}
+
 int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
 {
     const size_t lineMax = GetVisibleEnd();
@@ -303,8 +324,7 @@ bool wxVScrolledWindow::ScrollToLine(size_t line)
 
 
     // remember the currently shown lines for the refresh code below
-    size_t lineFirstOld = GetVisibleBegin(),
-           lineLastOld = GetVisibleEnd();
+    size_t lineFirstOld = GetVisibleBegin();
 
     m_lineFirst = line;
 
@@ -312,20 +332,8 @@ bool wxVScrolledWindow::ScrollToLine(size_t line)
     // the size of scrollbar thumb could have changed
     UpdateScrollbar();
 
-
-    // finally refresh the display -- but only redraw as few lines as possible
-    // to avoid flicker
-    if ( GetVisibleBegin() >= lineLastOld ||
-            GetVisibleEnd() <= lineFirstOld )
-    {
-        // the simplest case: we don't have any old lines left, just redraw
-        // everything
-        Refresh();
-    }
-    else // overlap between the lines we showed before and should show now
-    {
-        ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
-    }
+    // finally, scroll the actual window
+    ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
 
     return true;
 }
@@ -452,3 +460,931 @@ void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
 }
 
 #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()
+
+
+// ============================================================================
+// wxVarScrolled implementation
+// ============================================================================
+
+IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel)
+
+// ----------------------------------------------------------------------------
+// initialization
+// ----------------------------------------------------------------------------
+
+void wxHVScrolledWindow::Init()
+{
+    // 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
+}
+
+// ----------------------------------------------------------------------------
+// various helpers
+// ----------------------------------------------------------------------------
+
+wxCoord wxHVScrolledWindow::EstimateTotalHeight() 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,
+    // some in the end and some in the middle
+    static const size_t NUM_LINES_TO_SAMPLE = 10;
+
+    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
+    {
+        // 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);
+    }
+
+    return heightTotal;
+}
+
+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;
+
+    wxCoord widthTotal;
+    if ( m_columnsMax < 3*NUM_LINES_TO_SAMPLE )
+    {
+        // in this case calculating exactly is faster and more correct than
+        // guessing
+        widthTotal = GetColumnsWidth(0, m_columnsMax);
+    }
+    else // too many lines to calculate exactly
+    {
+        // 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);
+    }
+
+    return widthTotal;
+}
+
+wxCoord wxHVScrolledWindow::GetRowsHeight(size_t rowMin, size_t rowMax) const
+{
+    if ( rowMin == rowMax )
+        return 0;
+    else if ( rowMin > rowMax )
+        return -GetRowsHeight(rowMax, rowMin);
+    //else: lineMin < lineMax
+
+    // 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-1;
+
+    if(m_columnsFirst >= columnCount)
+        m_columnsFirst = columnCount-1;
+    
+    if(m_rowsFirst < 0)
+        m_rowsFirst = 0;
+    
+    if(m_columnsFirst < 0)
+        m_columnsFirst = 0;
+
+    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);
+
+    for ( size_t n = GetVisibleRowsBegin(); n < row; n++ )
+    {
+        rect.y += OnGetRowHeight(n);
+    }
+
+    for ( size_t 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
+    // the other ones
+    if ( fromRow < GetVisibleRowsBegin() )
+        fromRow = GetVisibleRowsBegin();
+
+    if ( toRow > GetVisibleRowsEnd() )
+        toRow = GetVisibleRowsEnd();
+
+    if ( fromColumn < GetVisibleColumnsBegin() )
+        fromColumn = GetVisibleColumnsBegin();
+
+    if ( toColumn > GetVisibleColumnsEnd() )
+        toColumn = GetVisibleColumnsEnd();
+
+    // calculate the rect occupied by these lines on screen
+    wxRect rect;
+    for ( size_t nBefore = GetVisibleRowsBegin();
+          nBefore < fromRow;
+          nBefore++ )
+    {
+        rect.y += OnGetRowHeight(nBefore);
+    }
+
+    for ( size_t nBetween = fromRow; nBetween <= toRow; nBetween++ )
+    {
+        rect.height += OnGetRowHeight(nBetween);
+    }
+
+    for ( size_t nBefore = GetVisibleColumnsBegin();
+          nBefore < fromColumn;
+          nBefore++ )
+    {
+        rect.x += OnGetColumnWidth(nBefore);
+    }
+
+    for ( size_t nBetween = fromColumn; nBetween <= toColumn; nBetween++ )
+    {
+        rect.width += OnGetColumnWidth(nBetween);
+    }
+
+    // do refresh it
+    RefreshRect(rect);
+}
+
+void wxHVScrolledWindow::RefreshAll()
+{
+    UpdateScrollbars();
+
+    Refresh();
+}
+
+bool wxHVScrolledWindow::Layout()
+{
+    if(GetSizer() && m_physicalScrolling)
+    {
+        // 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(0, y, w, h);
+        return true;
+    }
+
+    // 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++ )
+    {
+        y -= OnGetRowHeight(row);
+        if ( y < 0 )
+            hit.y = row;
+    }
+    
+    for ( size_t column = GetVisibleColumnsBegin();
+          column <= columnMax;
+          column++ )
+    {
+        x -= OnGetColumnWidth(column);
+        if ( x < 0 )
+            hit.x = column;
+    }
+
+    return hit;
+}
+
+// ----------------------------------------------------------------------------
+// scrolling
+// ----------------------------------------------------------------------------
+
+bool wxHVScrolledWindow::ScrollToRowColumn(size_t row, size_t column)
+{
+    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;
+}
+
+bool wxHVScrolledWindow::ScrollToRow(size_t row)
+{
+    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;
+}
+
+bool wxHVScrolledWindow::ScrollToColumn(size_t column)
+{
+    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 wxHVScrolledWindow::ScrollRows(int rows)
+{
+    rows += m_rowsFirst;
+    if ( rows < 0 )
+        rows = 0;
+
+    return ScrollToRow(rows);
+}
+
+bool wxHVScrolledWindow::ScrollColumns(int columns)
+{
+    columns += m_columnsFirst;
+    if ( columns < 0 )
+        columns = 0;
+
+    return ScrollToColumn(columns);
+}
+
+bool wxHVScrolledWindow::ScrollRowsColumns(int rows, int columns)
+{
+    rows += m_rowsFirst;
+    if ( rows < 0 )
+        rows = 0;
+
+    columns += m_columnsFirst;
+    if ( columns < 0 )
+        columns = 0;
+
+    return ScrollToRowColumn(rows, columns);
+}
+
+bool wxHVScrolledWindow::ScrollRowPages(int pages)
+{
+    bool didSomething = false;
+
+    while ( pages )
+    {
+        int line;
+        if ( pages > 0 )
+        {
+            line = GetVisibleRowsEnd();
+            if ( line )
+                line--;
+            pages--;
+        }
+        else // pages < 0
+        {
+            line = FindFirstFromBottom(GetVisibleRowsEnd());
+            pages++;
+        }
+
+        didSomething = ScrollToRow(line);
+    }
+
+    return didSomething;
+}
+
+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++;
+        }
+
+        didSomething = ScrollToColumn(line);
+    }
+
+    return didSomething;
+}
+
+bool wxHVScrolledWindow::ScrollPages(int rowPages, int columnPages)
+{
+    bool didSomething = false;
+
+    while ( rowPages )
+    {
+        int line;
+        if ( rowPages > 0 )
+        {
+            line = GetVisibleRowsEnd();
+            if ( line )
+                line--;
+            rowPages--;
+        }
+        else // rowPages < 0
+        {
+            line = FindFirstFromBottom(GetVisibleRowsBegin());
+            rowPages++;
+        }
+
+        didSomething = ScrollToRow(line);
+    }
+
+    while ( columnPages )
+    {
+        int line;
+        if ( columnPages > 0 )
+        {
+            line = GetVisibleColumnsEnd();
+            if ( line )
+                line--;
+            columnPages--;
+        }
+        else // columnPages < 0
+        {
+            line = FindFirstFromRight(GetVisibleColumnsBegin());
+            columnPages++;
+        }
+
+        didSomething |= ScrollToColumn(line);
+    }
+
+    return didSomething;
+}
+
+// ----------------------------------------------------------------------------
+// event handling
+// ----------------------------------------------------------------------------
+
+void wxHVScrolledWindow::OnSize(wxSizeEvent& event)
+{
+    UpdateScrollbars();
+    Layout();
+
+    event.Skip();
+}
+
+void wxHVScrolledWindow::OnScroll(wxScrollWinEvent& event)
+{
+    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)
+{
+    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;
+
+    if ( !event.IsPageScroll() )
+        ScrollRows( units_to_scroll*event.GetLinesPerAction() );
+    else
+        // scroll pages instead of lines
+        ScrollRowPages( units_to_scroll );
+}
+
+#endif
+
-- 
2.45.2

