// headers
// ----------------------------------------------------------------------------
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/sizer.h"
+#endif
+
#include "wx/vscroll.h"
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
EVT_SIZE(wxVScrolledWindow::OnSize)
EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
+#if wxUSE_MOUSEWHEEL
+ EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
+#endif
END_EVENT_TABLE()
// implementation
// ============================================================================
+IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
+
// ----------------------------------------------------------------------------
// initialization
// ----------------------------------------------------------------------------
m_nVisible = 1;
m_heightTotal = 0;
+
+#if wxUSE_MOUSEWHEEL
+ m_sumWheelRotation = 0;
+#endif
}
// ----------------------------------------------------------------------------
// various helpers
// ----------------------------------------------------------------------------
+wxCoord wxVScrolledWindow::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_lineMax < 3*NUM_LINES_TO_SAMPLE )
+ {
+ // in this case calculating exactly is faster and more correct than
+ // guessing
+ heightTotal = GetLinesHeight(0, m_lineMax);
+ }
+ else // too many lines to calculate exactly
+ {
+ // look at some lines in the beginning/middle/end
+ heightTotal =
+ GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
+ GetLinesHeight(m_lineMax - NUM_LINES_TO_SAMPLE, m_lineMax) +
+ GetLinesHeight(m_lineMax/2 - NUM_LINES_TO_SAMPLE/2,
+ m_lineMax/2 + NUM_LINES_TO_SAMPLE/2);
+
+ // use the height of the lines we looked as the average
+ heightTotal = (wxCoord)
+ (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
+ }
+
+ return heightTotal;
+}
+
wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
{
if ( lineMin == lineMax )
return height;
}
-size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast)
+size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
{
const wxCoord hWindow = GetClientSize().y;
if ( h > hWindow )
{
- lineFirst++;
+ // 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;
}
return lineFirst;
}
+void wxVScrolledWindow::RemoveScrollbar()
+{
+ m_lineFirst = 0;
+ m_nVisible = m_lineMax;
+ SetScrollbar(wxVERTICAL, 0, 0, 0);
+}
+
void wxVScrolledWindow::UpdateScrollbar()
{
// see how many lines can we fit on screen
h += OnGetLineHeight(line);
}
+ // if we still have remaining space below, maybe we can fit everything?
+ if ( h < hWindow )
+ {
+ wxCoord hAll = h;
+ for ( size_t lineFirst = m_lineFirst; lineFirst > 0; lineFirst-- )
+ {
+ hAll += OnGetLineHeight(m_lineFirst - 1);
+ if ( hAll > hWindow )
+ break;
+ }
+
+ if ( hAll < hWindow )
+ {
+ // we don't need scrollbar at all
+ RemoveScrollbar();
+ return;
+ }
+ }
+
m_nVisible = line - m_lineFirst;
int pageSize = m_nVisible;
// save the number of lines
m_lineMax = count;
+ // and our estimate for their total height
+ m_heightTotal = EstimateTotalHeight();
- // 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;
+ // recalculate the scrollbars parameters
+ if ( count )
+ {
+ m_lineFirst = 1; // make sure it is != 0
+ ScrollToLine(0);
+ }
+ else // no items
+ {
+ RemoveScrollbar();
+ }
+}
- if ( count < 3*NUM_LINES_TO_SAMPLE )
+void wxVScrolledWindow::RefreshLine(size_t line)
+{
+ // is this line visible?
+ if ( !IsVisible(line) )
{
- // in this case calculating exactly is faster and more correct than
- // guessing
- m_heightTotal = GetLinesHeight(0, m_lineMax);
+ // no, it is useless to do anything
+ return;
}
- else // too many lines to calculate exactly
+
+ // calculate the rect occupied by this line on screen
+ wxRect rect;
+ rect.width = GetClientSize().x;
+ rect.height = OnGetLineHeight(line);
+ for ( size_t n = GetVisibleBegin(); n < line; n++ )
{
- // 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);
+ rect.y += OnGetLineHeight(n);
+ }
- // use the height of the lines we looked as the average
- m_heightTotal = ((float)m_heightTotal / (3*NUM_LINES_TO_SAMPLE)) *
- m_lineMax;
+ // do refresh it
+ RefreshRect(rect);
+}
+
+void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
+{
+ wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
+
+ // clump the range to just the visible lines -- it is useless to refresh
+ // the other ones
+ if ( from < GetVisibleBegin() )
+ from = GetVisibleBegin();
+
+ if ( to >= GetVisibleEnd() )
+ to = GetVisibleEnd();
+ else
+ to++;
+
+ // calculate the rect occupied by these lines on screen
+ wxRect rect;
+ rect.width = GetClientSize().x;
+ for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
+ {
+ rect.y += OnGetLineHeight(nBefore);
}
+ for ( size_t nBetween = from; nBetween < to; nBetween++ )
+ {
+ rect.height += OnGetLineHeight(nBetween);
+ }
- // recalculate the scrollbars parameters
- ScrollToLine(0);
+ // do refresh it
+ RefreshRect(rect);
+}
+
+void wxVScrolledWindow::RefreshAll()
+{
+ UpdateScrollbar();
+
+ Refresh();
+}
+
+bool wxVScrolledWindow::Layout()
+{
+ if ( GetSizer() )
+ {
+ // adjust the sizer dimensions/position taking into account the
+ // virtual size and scrolled position of the window.
+
+ int w, h;
+ GetVirtualSize(&w, &h);
+
+ // x is always 0 so no variable needed
+ int y = -GetLinesHeight(0, GetFirstVisibleLine());
+
+ 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();
+ for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
+ {
+ y -= OnGetLineHeight(line);
+ if ( y < 0 )
+ return line;
+ }
+
+ return wxNOT_FOUND;
}
// ----------------------------------------------------------------------------
// determine the real first line to scroll to: we shouldn't scroll beyond
// the end
- size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1);
+ size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
if ( line > lineFirstLast )
line = lineFirstLast;
// remember the currently shown lines for the refresh code below
- size_t lineFirstOld = GetFirstVisibleLine(),
- lineLastOld = GetLastVisibleLine();
+ size_t lineFirstOld = GetVisibleBegin(),
+ lineLastOld = GetVisibleEnd();
m_lineFirst = line;
// finally refresh the display -- but only redraw as few lines as possible
// to avoid flicker
- if ( GetFirstVisibleLine() > lineLastOld ||
- GetLastVisibleLine() < lineFirstOld )
+ if ( GetVisibleBegin() >= lineLastOld ||
+ GetVisibleEnd() <= lineFirstOld )
{
// the simplest case: we don't have any old lines left, just redraw
// everything
}
else // overlap between the lines we showed before and should show now
{
- ScrollWindow(0, GetLinesHeight(GetFirstVisibleLine(), lineFirstOld));
+ ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
}
return true;
int line;
if ( pages > 0 )
{
- line = GetLastVisibleLine();
+ line = GetVisibleEnd();
+ if ( line )
+ line--;
pages--;
}
else // pages < 0
{
- line = FindFirstFromBottom(GetFirstVisibleLine());
+ line = FindFirstFromBottom(GetVisibleBegin());
pages++;
}
size_t lineFirstNew;
const wxEventType evtType = event.GetEventType();
+
if ( evtType == wxEVT_SCROLLWIN_TOP )
{
lineFirstNew = 0;
}
else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
{
- lineFirstNew = GetLastVisibleLine();
+ lineFirstNew = GetVisibleEnd();
+ if ( lineFirstNew )
+ lineFirstNew--;
}
- else // unknown scroll event?
+ else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
{
- if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
- {
- lineFirstNew = event.GetPosition();
- }
- else
- {
- wxASSERT_MSG( evtType == wxEVT_SCROLLWIN_THUMBTRACK,
- _T("unknown scroll event type?") );
+ lineFirstNew = event.GetPosition();
+ }
+ else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
+ {
+ lineFirstNew = event.GetPosition();
+ }
- // don't do anything, otherwise dragging the thumb around would
- // be too slow
- return;
- }
+ else // unknown scroll event?
+ {
+ wxFAIL_MSG( _T("unknown scroll event type?") );
+ return;
}
ScrollToLine(lineFirstNew);
+
+#ifdef __WXMAC__
+ Update();
+#endif // __WXMAC__
}
+#if wxUSE_MOUSEWHEEL
+
+void wxVScrolledWindow::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() )
+ ScrollLines( units_to_scroll*event.GetLinesPerAction() );
+ else
+ // scroll pages instead of lines
+ ScrollPages( units_to_scroll );
+}
+
+#endif // wxUSE_MOUSEWHEEL
+