From cf7d6329530d0a9f181ac24dcc722d276885f05e Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Fri, 30 May 2003 21:08:31 +0000
Subject: [PATCH] added wxVScrolledWindow

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@20772 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/category.tex  |   1 +
 docs/latex/wx/classes.tex   |   1 +
 docs/latex/wx/scrolwin.tex  |   3 +-
 docs/latex/wx/vscroll.tex   | 194 ++++++++++++++++++++++
 include/wx/vscroll.h        | 167 +++++++++++++++++++
 samples/vscroll/vscroll.dsp | 155 +++++++++++++++++
 samples/vscroll/vstest.cpp  | 286 ++++++++++++++++++++++++++++++++
 src/generic/vscroll.cpp     | 322 ++++++++++++++++++++++++++++++++++++
 8 files changed, 1128 insertions(+), 1 deletion(-)
 create mode 100644 docs/latex/wx/vscroll.tex
 create mode 100644 include/wx/vscroll.h
 create mode 100644 samples/vscroll/vscroll.dsp
 create mode 100644 samples/vscroll/vstest.cpp
 create mode 100644 src/generic/vscroll.cpp

diff --git a/docs/latex/wx/category.tex b/docs/latex/wx/category.tex
index c04aeb916a..f41578b7c2 100644
--- a/docs/latex/wx/category.tex
+++ b/docs/latex/wx/category.tex
@@ -43,6 +43,7 @@ The following are a variety of classes that are derived from wxWindow.
 \twocolitem{\helpref{wxPlotWindow}{wxplotwindow}}{A class to display data.}
 \twocolitem{\helpref{wxSashWindow}{wxsashwindow}}{Window with four optional sashes that can be dragged}
 \twocolitem{\helpref{wxSashLayoutWindow}{wxsashlayoutwindow}}{Window that can be involved in an IDE-like layout arrangement}
+\twocolitem{\helpref{wxVScrolledWindow}{wxvscrolledwindow}}{As wxScrolledWindow but supports lines of variable height}
 \twocolitem{\helpref{wxWizardPage}{wxwizardpage}}{A base class for the page in wizard dialog.}
 \twocolitem{\helpref{wxWizardPageSimple}{wxwizardpagesimple}}{A page in wizard dialog.}
 \end{twocollist}
diff --git a/docs/latex/wx/classes.tex b/docs/latex/wx/classes.tex
index ffacfd8ab7..4d374a4d41 100644
--- a/docs/latex/wx/classes.tex
+++ b/docs/latex/wx/classes.tex
@@ -308,6 +308,7 @@
 \input validatr.tex
 \input variant.tex
 \input view.tex
+\input vscroll.tex
 \input wave.tex
 \input window.tex
 \input windowdc.tex
diff --git a/docs/latex/wx/scrolwin.tex b/docs/latex/wx/scrolwin.tex
index 52ccf938eb..58a60fc533 100644
--- a/docs/latex/wx/scrolwin.tex
+++ b/docs/latex/wx/scrolwin.tex
@@ -97,7 +97,8 @@ to build your own scroll behaviour.
 
 \wxheading{See also}
 
-\helpref{wxScrollBar}{wxscrollbar}, \helpref{wxClientDC}{wxclientdc}, \helpref{wxPaintDC}{wxpaintdc}
+\helpref{wxScrollBar}{wxscrollbar}, \helpref{wxClientDC}{wxclientdc},\\
+\helpref{wxPaintDC}{wxpaintdc}, \helpref{wxVScrolledWindow}{wxvscrolledwindow}
 
 \latexignore{\rtfignore{\wxheading{Members}}}
 
diff --git a/docs/latex/wx/vscroll.tex b/docs/latex/wx/vscroll.tex
new file mode 100644
index 0000000000..3489ab5885
--- /dev/null
+++ b/docs/latex/wx/vscroll.tex
@@ -0,0 +1,194 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Name:        vscroll.tex
+%% Purpose:     wxVScrolledWindow documentation
+%% Author:      Vadim Zeitlin
+%% Modified by:
+%% Created:     30.05.03
+%% RCS-ID:      $Id$
+%% Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
+%% License:     wxWindows license
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\section{\class{wxVScrolledWindow}}\label{wxvscrolledwindow}
+
+In the name of this class, "V" may stand for "variable" because it can be
+used for scrolling lines of variable heights; "virtual" because it is not
+necessary to know the heights of all lines in advance -- only those which
+are shown on the screen need to be measured; or, even, "vertical" because
+this class only supports scrolling in one direction currently (this could
+and probably will change in the future however).
+
+In any case, this is a generalization of the 
+\helpref{wxScrolledWindow}{wxScrolledWindow} class which can be only used when
+all lines have the same height. It lacks some other wxScrolledWindow features
+however, notably there is currently no support for horizontal scrolling; it
+can't scroll another window nor only a rectangle of the window and not its
+entire client area.
+ 
+To use this class, you need to derive from it and implement 
+\helpref{OnGetLineHeight()}{wxvscrolledwindowongetlineheight} pure virtual
+method. You also must call \helpref{SetLineCount}{wxvscrolledwindowsetlinecount} 
+to let the base class know how many lines it should display but from that
+moment on the scrolling is handled entirely by wxVScrolledWindow, you only
+need to draw the visible part of contents in your {\tt OnPaint()} method as
+usual. You should use \helpref{GetFirstVisibleLine()}{wxvscrolledwindowgetfirstvisibleline} 
+and \helpref{GetLastVisibleLine()}{wxvscrolledwindowgetlastvisibleline} to
+select the lines to display. ote that the device context origin is not shifted
+so the first visible line always appears at the point $(0, 0)$ in physical as
+well as logical coordinates.
+
+\wxheading{Derived from}
+
+\helpref{wxPanel}{wxpanel}
+
+\wxheading{Include files}
+
+<wx/vscroll.h>
+
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+
+\membersection{wxVScrolledWindow::wxVScrolledWindow}\label{wxvscrolledwindowctor}
+
+\func{}{wxVScrolledWindow}{\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} is always automatically added to our style, there is
+no need to specify it explicitly.
+
+\func{}{wxVScrolledWindow}{\void}
+
+Default constructor, you must call \helpref{Create()}{wxvscrolledwindowcreate} 
+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{wxVScrolledWindow::Create}\label{wxvscrolledwindowcreate}
+
+\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}{wxvscrolledwindowctor} but returns
+status code: {\tt true} if ok, {\tt false} if the window couldn't have been created.
+
+Just as with the ctor above, {\tt wxVSCROLL| style is always used, there is no
+need to specify it explicitly.
+
+
+\membersection{wxVScrolledWindow::GetFirstVisibleLine}\label{wxvscrolledwindowgetfirstvisibleline}
+
+\constfunc{size\_t}{GetFirstVisibleLine}{\void}
+
+Returns the index of the first currently visible line.
+
+
+\membersection{wxVScrolledWindow::GetLastVisibleLine}\label{wxvscrolledwindowgetlastvisibleline}
+
+\constfunc{size\_t}{GetLastVisibleLine}{\void}
+
+Returns the index of the last currently visible line.
+
+
+\membersection{wxVScrolledWindow::GetLineCount}\label{wxvscrolledwindowgetlinecount}
+
+\constfunc{size\_t}{GetLineCount}{\void}
+
+Get the number of lines this window contains (previously set by 
+\helpref{SetLineCount()}{wxvscrolledwindowsetlinecount})
+
+
+\membersection{wxVScrolledWindow::OnGetLineHeight}\label{wxvscrolledwindowongetlineheight}
+
+\constfunc{wxCoord}{OnGetLineHeight}{\param{size\_t }{n}}
+
+This protected virtual function must be overridden in the derived class and it
+should return the height of the given line in pixels.
+
+\wxheading{See also}
+
+\helpref{OnGetLinesHint}{wxvscrolledwindowongetlineshint}
+
+
+\membersection{wxVScrolledWindow::OnGetLinesHint}\label{wxvscrolledwindowongetlineshint}
+
+\constfunc{void}{OnGetLinesHint}{\param{size\_t }{lineMin}, \param{size\_t }{lineMax}}
+
+This function doesn't have to be overridden but it may be useful to do
+it if calculating the lines heights is a relatively expensive operation
+as it gives the user code a possibility to calculate several of them at
+once.
+
+{\tt OnGetLinesHint()} is normally called just before 
+\helpref{OnGetLineHeight()}{wxvscrolledwindowongetlineheight} but you
+shouldn't rely on the latter being called for all lines in the interval
+specified here. It is also possible that OnGetLineHeight() will be
+called for the lines outside of this interval, so this is really just a
+hint, not a promise.
+
+Finally note that {\it lineMin} is inclusive, while {\it lineMax} is exclusive,
+as usual.
+
+
+\membersection{wxVScrolledWindow::ScrollLines}\label{wxvscrolledwindowscrolllines}
+
+\func{bool}{ScrollLines}{\param{int }{lines}}
+
+Scroll by the specified number of lines 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
+line).
+
+\wxheading{See also}
+
+\helpref{LineUp}{wxwindowlineup}, \helpref{LineDown}{wxwindowlinedown}
+
+
+\membersection{wxVScrolledWindow::ScrollPages}\label{wxvscrolledwindowscrollpages}
+
+\func{bool}{ScrollPages}{\param{int }{pages}}
+
+Scroll by the specified number of pages which may be positive (to scroll down)
+or negative (to scroll up).
+
+\wxheading{See also}
+
+\helpref{ScrollLines}{wxvscrolledwindowscrolllines},\\
+\helpref{PageUp}{wxwindowpageup}, \helpref{PageDown}{wxwindowpagedown}
+
+
+\membersection{wxVScrolledWindow::ScrollToLine}\label{wxvscrolledwindowscrolltoline}
+
+\func{bool}{ScrollToLine}{\param{size\_t }{line}}
+
+Scroll to the specified line: it will become the first visible line in
+the window.
+
+Return {\tt true} if we scrolled the window, {\tt false} if nothing was done.
+
+
+\membersection{wxVScrolledWindow::SetLineCount}\label{wxvscrolledwindowsetlinecount}
+
+\func{void}{SetLineCount}{\param{size\_t }{count}}
+
+Set the number of lines the window contains: the derived class must
+provide the heights for all lines with indices up to the one given here
+in its \helpref{OnGetLineHeight()}{wxvscrolledwindowongetlineheight}.
+
+
diff --git a/include/wx/vscroll.h b/include/wx/vscroll.h
new file mode 100644
index 0000000000..9bf5cce2d9
--- /dev/null
+++ b/include/wx/vscroll.h
@@ -0,0 +1,167 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        include/wx/vscroll.h
+// Purpose:     wxVScrolledWindow: generalization of wxScrolledWindow
+// Author:      Vadim Zeitlin
+// Modified by:
+// Created:     30.05.03
+// RCS-ID:      $Id$
+// Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_VSCROLL_H_
+#define _WX_VSCROLL_H_
+
+#include "wx/panel.h"           // base class
+
+// ----------------------------------------------------------------------------
+// wxVScrolledWindow
+// ----------------------------------------------------------------------------
+
+/*
+   In the name of this class, "V" may stand for "variable" because it can be
+   used for scrolling lines of variable heights; "virtual" because it is not
+   necessary to know the heights of all lines in advance -- only those which
+   are shown on the screen need to be measured; or, even, "vertical" because
+   this class only supports scrolling in one direction currently (this could
+   and probably will change in the future however).
+
+   In any case, this is a generalization of the wxScrolledWindow class which
+   can be only used when all lines have the same height. It lacks some other
+   wxScrolledWindow features however, notably it currently lacks support for
+   horizontal scrolling; it can't scroll another window nor only a rectangle
+   of the window and not its entire client area.
+ */
+class wxVScrolledWindow : public wxPanel
+{
+public:
+    // constructors and such
+    // ---------------------
+
+    // default ctor, you must call Create() later
+    wxVScrolledWindow() { Init(); }
+
+    // normal ctor, no need to call Create() after this one
+    //
+    // note that wxVSCROLL is always automatically added to our style, there is
+    // no need to specify it explicitly
+    wxVScrolledWindow(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 style is always used, there is no
+    // need to specify it
+    bool Create(wxWindow *parent,
+                wxWindowID id,
+                const wxPoint& pos = wxDefaultPosition,
+                const wxSize& size = wxDefaultSize,
+                long style = 0,
+                const wxString& name = wxPanelNameStr)
+    {
+        return wxPanel::Create(parent, id, pos, size, style | wxVSCROLL, name);
+    }
+
+
+    // operations
+    // ----------
+
+    // set the number of lines the window contains: the derived class must
+    // provide the heights for all lines with indices up to the one given here
+    // in its OnGetLineHeight()
+    void SetLineCount(size_t count);
+
+    // 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 ScrollToLine(size_t line);
+
+    // scroll by the specified number of lines/pages
+    virtual bool ScrollLines(int lines);
+    virtual bool ScrollPages(int pages);
+
+
+    // accessors
+    // ---------
+
+    // get the number of lines this window contains (previously set by
+    // SetLineCount())
+    size_t GetLineCount() const { return m_lineMax; }
+
+    // get the first currently visible line
+    size_t GetFirstVisibleLine() const { return m_lineFirst; }
+
+    // get the last currently visible line
+    size_t GetLastVisibleLine() const { return m_lineFirst + m_nVisible - 1; }
+
+
+//protected:
+    // this function must be overridden in the derived class and it should
+    // return the height of the given line in pixels
+    virtual wxCoord OnGetLineHeight(size_t n) const = 0;
+
+    // this function doesn't have to be overridden but it may be useful to do
+    // it if calculating the lines heights is a relatively expensive operation
+    // as it gives the user code a possibility to calculate several of them at
+    // once
+    //
+    // OnGetLinesHint() is normally called just before OnGetLineHeight() but you
+    // shouldn't rely on the latter being called for all lines in the interval
+    // specified here. It is also possible that OnGetLineHeight() will be
+    // called for the lines outside of this interval, so this is really just a
+    // hint, not a promise.
+    //
+    // finally note that lineMin is inclusive, while lineMax is exclusive, as
+    // usual
+    virtual void OnGetLinesHint(size_t lineMin, size_t lineMax) const { }
+
+protected:
+    // the event handlers
+    void OnSize(wxSizeEvent& event);
+    void OnScroll(wxScrollWinEvent& event);
+
+    // find the index of the line we need to show at the top of the window such
+    // that the last line shown is the given one
+    size_t FindFirstFromBottom(size_t lineLast);
+
+    // get the total height of the lines between lineMin (inclusive) and
+    // lineMax (exclusive)
+    wxCoord GetLinesHeight(size_t lineMin, size_t lineMax) const;
+
+    // update the thumb size shown by the scrollbar
+    void UpdateScrollbar();
+
+private:
+    // common part of all ctors
+    void Init();
+
+
+    // the total number of (logical) lines
+    size_t m_lineMax;
+
+    // the total (estimated) height
+    wxCoord m_heightTotal;
+
+    // the first currently visible line
+    size_t m_lineFirst;
+
+    // the number of currently visible lines (including the last, possibly only
+    // partly, visible one)
+    size_t m_nVisible;
+
+
+    DECLARE_EVENT_TABLE()
+};
+
+#endif // _WX_VSCROLL_H_
+
diff --git a/samples/vscroll/vscroll.dsp b/samples/vscroll/vscroll.dsp
new file mode 100644
index 0000000000..33a4fc318e
--- /dev/null
+++ b/samples/vscroll/vscroll.dsp
@@ -0,0 +1,155 @@
+# Microsoft Developer Studio Project File - Name="vscroll" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vscroll - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "vscroll.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "vscroll.mak" CFG="vscroll - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "vscroll - Win32 Release DLL" (based on "Win32 (x86) Application")
+!MESSAGE "vscroll - Win32 Debug DLL" (based on "Win32 (x86) Application")
+!MESSAGE "vscroll - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vscroll - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "vscroll - Win32 Release DLL"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ReleaseDll"
+# PROP BASE Intermediate_Dir "ReleaseDll"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ReleaseDll"
+# PROP Intermediate_Dir "ReleaseDll"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W4 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c
+# ADD CPP /nologo /MD /W4 /O2 /I "../../include" /I "..\..\lib\mswdll" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /D "WXUSINGDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD BASE RSC /l 0x409 /i "../../include" /d "NDEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib ..\..\lib\wxmsw250.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF  "$(CFG)" == "vscroll - Win32 Debug DLL"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "DebugDll"
+# PROP BASE Intermediate_Dir "DebugDll"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "DebugDll"
+# PROP Intermediate_Dir "DebugDll"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W4 /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c
+# ADD CPP /nologo /MDd /W4 /Zi /Od /I "../../include" /I "..\..\lib\mswdlld" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /D "__WXDEBUG__" /D WXDEBUG=1 /D "WXUSINGDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD BASE RSC /l 0x409 /i "../../include" /d "_DEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib ..\..\lib\wxmsw250d.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ELSEIF  "$(CFG)" == "vscroll - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W4 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c
+# ADD CPP /nologo /MD /W4 /O2 /I "../../include" /I "..\..\lib\msw" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD BASE RSC /l 0x409 /i "../../include" /d "NDEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib ..\..\lib\zlib.lib ..\..\lib\regex.lib ..\..\lib\png.lib ..\..\lib\jpeg.lib ..\..\lib\tiff.lib ..\..\lib\wxmsw.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF  "$(CFG)" == "vscroll - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W4 /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /YX /FD /c
+# ADD CPP /nologo /MDd /W4 /Zi /Od /I "../../include" /I "..\..\lib\mswd" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D WINVER=0x400 /D "_MT" /D wxUSE_GUI=1 /D "__WXDEBUG__" /D WXDEBUG=1 /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD BASE RSC /l 0x409 /i "../../include" /d "_DEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib ..\..\lib\zlibd.lib ..\..\lib\regexd.lib ..\..\lib\pngd.lib ..\..\lib\jpegd.lib ..\..\lib\tiffd.lib ..\..\lib\wxmswd.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "vscroll - Win32 Release DLL"
+# Name "vscroll - Win32 Debug DLL"
+# Name "vscroll - Win32 Release"
+# Name "vscroll - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\vscroll.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\vstest.cpp
+# End Source File
+# End Target
+# End Project
diff --git a/samples/vscroll/vstest.cpp b/samples/vscroll/vstest.cpp
new file mode 100644
index 0000000000..04255ef947
--- /dev/null
+++ b/samples/vscroll/vstest.cpp
@@ -0,0 +1,286 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        vscroll.cpp
+// Purpose:     VScroll wxWindows sample
+// Author:      Vadim Zeitlin
+// Modified by:
+// Created:     04/01/98
+// RCS-ID:      $Id$
+// Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWindows headers)
+#ifndef WX_PRECOMP
+    #include "wx/app.h"
+    #include "wx/frame.h"
+#endif
+
+// we need to include the headers not included from wx/wx.h explicitly anyhow
+#include "wx/vscroll.h"
+
+// ----------------------------------------------------------------------------
+// resources
+// ----------------------------------------------------------------------------
+
+// the application icon (under Windows and OS/2 it is in resources)
+#if !defined(__WXMSW__) && !defined(__WXOS2__)
+    #include "mondrian.xpm"
+#endif
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// Define a new application type, each program should derive a class from wxApp
+class VScrollApp : public wxApp
+{
+public:
+    // create our main window
+    virtual bool OnInit();
+};
+
+// Define a new frame type: this is going to be our main frame
+class VScrollFrame : public wxFrame
+{
+public:
+    // ctor
+    VScrollFrame();
+
+    // event handlers (these functions should _not_ be virtual)
+    void OnQuit(wxCommandEvent& event);
+    void OnAbout(wxCommandEvent& event);
+
+    void OnSize(wxSizeEvent& event)
+    {
+        // show current size in the status bar
+#if wxUSE_STATUSBAR
+        if ( m_frameStatusBar )
+        {
+            wxSize sz = GetClientSize();
+            SetStatusText(wxString::Format("%dx%d", sz.x, sz.y), 1);
+        }
+#endif // wxUSE_STATUSBAR
+
+        event.Skip();
+    }
+
+private:
+    // any class wishing to process wxWindows events must use this macro
+    DECLARE_EVENT_TABLE()
+};
+
+class VScrollWindow : public wxVScrolledWindow
+{
+public:
+    VScrollWindow(wxFrame *frame) : wxVScrolledWindow(frame, -1)
+    {
+        m_frame = frame;
+
+        SetLineCount(200);
+
+        m_changed = true;
+    }
+
+    void OnIdle(wxIdleEvent&)
+    {
+        m_frame->SetStatusText(wxString::Format
+                               (
+                                    "Page size = %d, pos = %d, max = %d",
+                                    GetScrollThumb(wxVERTICAL),
+                                    GetScrollPos(wxVERTICAL),
+                                    GetScrollRange(wxVERTICAL)
+                               ));
+        m_changed = false;
+    }
+
+    void OnPaint(wxPaintEvent&)
+    {
+        wxPaintDC dc(this);
+
+        dc.SetPen(*wxBLACK_DASHED_PEN);
+
+        const size_t lineFirst = GetFirstVisibleLine(),
+                     lineLast = GetLastVisibleLine();
+
+        const wxCoord hText = dc.GetCharHeight();
+
+        wxCoord y = 0;
+        for ( size_t line = lineFirst; line <= lineLast; line++ )
+        {
+            dc.DrawLine(0, y, 1000, y);
+
+            wxCoord hLine = OnGetLineHeight(line);
+            dc.DrawText(wxString::Format(_T("Line %lu"), (unsigned long)line),
+                        0, y + (hLine - hText) / 2);
+
+            y += hLine;
+            dc.DrawLine(0, y, 1000, y);
+        }
+    }
+
+    void OnScroll(wxScrollWinEvent& event)
+    {
+        m_changed = true;
+
+        event.Skip();
+    }
+
+
+    virtual wxCoord OnGetLineHeight(size_t n) const
+    {
+        wxASSERT( n < GetLineCount() );
+
+        return n % 2 ? 15 : 30; // 15 + 2*n
+    }
+
+private:
+    wxFrame *m_frame;
+
+    bool m_changed;
+
+    DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(VScrollWindow, wxVScrolledWindow)
+    EVT_IDLE(VScrollWindow::OnIdle)
+    EVT_PAINT(VScrollWindow::OnPaint)
+    EVT_SCROLLWIN(VScrollWindow::OnScroll)
+END_EVENT_TABLE()
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// IDs for the controls and the menu commands
+enum
+{
+    // menu items
+    VScroll_Quit = 1,
+
+    // 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
+};
+
+// ----------------------------------------------------------------------------
+// event tables and other macros for wxWindows
+// ----------------------------------------------------------------------------
+
+// the event tables connect the wxWindows 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)
+END_EVENT_TABLE()
+
+// Create a new application object: this macro will allow wxWindows 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
+// not wxApp)
+IMPLEMENT_APP(VScrollApp)
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// the application class
+// ----------------------------------------------------------------------------
+
+// 'Main program' equivalent: the program execution "starts" here
+bool VScrollApp::OnInit()
+{
+    // create the main application window
+    VScrollFrame *frame = new VScrollFrame;
+
+    // and show it (the frames, unlike simple controls, are not shown when
+    // created initially)
+    frame->Show(TRUE);
+
+    // ok
+    return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// main frame
+// ----------------------------------------------------------------------------
+
+// frame constructor
+VScrollFrame::VScrollFrame()
+            : wxFrame(NULL,
+                      -1,
+                      _T("VScroll wxWindows Sample"),
+                      wxDefaultPosition,
+                      wxSize(400, 350))
+{
+    // set the frame icon
+    SetIcon(wxICON(mondrian));
+
+#if wxUSE_MENUS
+    // create a menu bar
+    wxMenu *menuFile = 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"));
+
+    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(menuHelp, _T("&Help"));
+
+    // ... and attach this menu bar to the frame
+    SetMenuBar(menuBar);
+#endif // wxUSE_MENUS
+
+#if wxUSE_STATUSBAR
+    // create a status bar just for fun (by default with 1 pane only)
+    CreateStatusBar(2);
+    SetStatusText(_T("Welcome to wxWindows!"));
+#endif // wxUSE_STATUSBAR
+
+    // create our one and only child -- it will take our entire client area
+    new VScrollWindow(this);
+}
+
+// ----------------------------------------------------------------------------
+// event handlers
+// ----------------------------------------------------------------------------
+
+void VScrollFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+{
+    // TRUE is to force the frame to close
+    Close(TRUE);
+}
+
+void VScrollFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+{
+    wxMessageBox(_T("VScroll shows how to implement scrolling with\n")
+                 _T("variable line heights.\n")
+                 _T("© 2003 Vadim Zeitlin"),
+                 _T("About VScroll"),
+                 wxOK | wxICON_INFORMATION,
+                 this);
+}
diff --git a/src/generic/vscroll.cpp b/src/generic/vscroll.cpp
new file mode 100644
index 0000000000..aa52fab013
--- /dev/null
+++ b/src/generic/vscroll.cpp
@@ -0,0 +1,322 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        src/generic/vscroll.cpp
+// Purpose:     wxVScrolledWindow implementation
+// Author:      Vadim Zeitlin
+// Modified by:
+// Created:     30.05.03
+// RCS-ID:      $Id$
+// Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#include "wx/vscroll.h"
+
+// ----------------------------------------------------------------------------
+// event tables
+// ----------------------------------------------------------------------------
+
+BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
+    EVT_SIZE(wxVScrolledWindow::OnSize)
+    EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
+END_EVENT_TABLE()
+
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// initialization
+// ----------------------------------------------------------------------------
+
+void wxVScrolledWindow::Init()
+{
+    // we're initially empty
+    m_lineMax =
+    m_lineFirst = 0;
+
+    // this one should always be strictly positive
+    m_nVisible = 1;
+
+    m_heightTotal = 0;
+}
+
+// ----------------------------------------------------------------------------
+// various helpers
+// ----------------------------------------------------------------------------
+
+wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
+{
+    if ( lineMin == lineMax )
+        return 0;
+    else if ( lineMin > lineMax )
+        return -GetLinesHeight(lineMax, lineMin);
+    //else: lineMin < lineMax
+
+    // let the user code know that we're going to need all these lines
+    OnGetLinesHint(lineMin, lineMax);
+
+    // do sum up their heights
+    wxCoord height = 0;
+    for ( size_t line = lineMin; line < lineMax; line++ )
+    {
+        height += OnGetLineHeight(line);
+    }
+
+    return height;
+}
+
+size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast)
+{
+    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 = lineLast;
+    wxCoord h = 0;
+    for ( ;; )
+    {
+        h += OnGetLineHeight(lineFirst);
+
+        if ( h > hWindow )
+        {
+            lineFirst++;
+
+            break;
+        }
+
+        if ( !lineFirst )
+            break;
+
+        lineFirst--;
+    }
+
+    return lineFirst;
+}
+
+void wxVScrolledWindow::UpdateScrollbar()
+{
+    // see how many lines can we fit on screen
+    const wxCoord hWindow = GetClientSize().y;
+
+    wxCoord h = 0;
+    size_t line;
+    for ( line = m_lineFirst; line < m_lineMax; line++ )
+    {
+        if ( h > hWindow )
+            break;
+
+        h += OnGetLineHeight(line);
+    }
+
+    m_nVisible = line - m_lineFirst;
+
+    int pageSize = m_nVisible;
+    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_lineMax the
+        // scrollbar is not shown at all under MSW
+        pageSize--;
+    }
+
+    // set the scrollbar parameters to reflect this
+    SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
+}
+
+// ----------------------------------------------------------------------------
+// operations
+// ----------------------------------------------------------------------------
+
+void wxVScrolledWindow::SetLineCount(size_t count)
+{
+    // save the number of lines
+    m_lineMax = count;
+
+
+    // 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;
+
+    if ( count < 3*NUM_LINES_TO_SAMPLE )
+    {
+        // in this case calculating exactly is faster and more correct than
+        // guessing
+        m_heightTotal = GetLinesHeight(0, m_lineMax);
+    }
+    else // too many lines to calculate exactly
+    {
+        // look at some lines in the beginning/middle/end
+        m_heightTotal =
+            GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
+                GetLinesHeight(count - NUM_LINES_TO_SAMPLE, count) +
+                    GetLinesHeight(count/2 - NUM_LINES_TO_SAMPLE/2,
+                                   count/2 + NUM_LINES_TO_SAMPLE/2);
+
+        // use the height of the lines we looked as the average
+        m_heightTotal = ((float)m_heightTotal / (3*NUM_LINES_TO_SAMPLE)) *
+                            m_lineMax;
+    }
+
+
+    // recalculate the scrollbars parameters
+    ScrollToLine(0);
+}
+
+// ----------------------------------------------------------------------------
+// scrolling
+// ----------------------------------------------------------------------------
+
+bool wxVScrolledWindow::ScrollToLine(size_t line)
+{
+    if ( !m_lineMax )
+    {
+        // 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_lineMax - 1);
+    if ( line > lineFirstLast )
+        line = lineFirstLast;
+
+    // anything to do?
+    if ( line == m_lineFirst )
+    {
+        // no
+        return false;
+    }
+
+
+    // remember the currently shown lines for the refresh code below
+    size_t lineFirstOld = GetFirstVisibleLine(),
+           lineLastOld = GetLastVisibleLine();
+
+    m_lineFirst = 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 ( GetFirstVisibleLine() > lineLastOld ||
+            GetLastVisibleLine() < 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(GetFirstVisibleLine(), lineFirstOld));
+    }
+
+    return true;
+}
+
+bool wxVScrolledWindow::ScrollLines(int lines)
+{
+    lines += m_lineFirst;
+    if ( lines < 0 )
+        lines = 0;
+
+    return ScrollToLine(lines);
+}
+
+bool wxVScrolledWindow::ScrollPages(int pages)
+{
+    bool didSomething = false;
+
+    while ( pages )
+    {
+        int line;
+        if ( pages > 0 )
+        {
+            line = GetLastVisibleLine();
+            pages--;
+        }
+        else // pages < 0
+        {
+            line = FindFirstFromBottom(GetFirstVisibleLine());
+            pages++;
+        }
+
+        didSomething = ScrollToLine(line);
+    }
+
+    return didSomething;
+}
+
+// ----------------------------------------------------------------------------
+// event handling
+// ----------------------------------------------------------------------------
+
+void wxVScrolledWindow::OnSize(wxSizeEvent& event)
+{
+    UpdateScrollbar();
+
+    event.Skip();
+}
+
+void wxVScrolledWindow::OnScroll(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 = GetLastVisibleLine();
+    }
+    else // unknown scroll event?
+    {
+        if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
+        {
+            lineFirstNew = event.GetPosition();
+        }
+        else
+        {
+            wxASSERT_MSG( evtType == wxEVT_SCROLLWIN_THUMBTRACK,
+                            _T("unknown scroll event type?") );
+
+            // don't do anything, otherwise dragging the thumb around would
+            // be too slow
+            return;
+        }
+    }
+
+    ScrollToLine(lineFirstNew);
+}
+
-- 
2.50.0

