1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/vscroll.cpp
3 // Purpose: wxVScrolledWindow implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #include "wx/vscroll.h"
29 // ----------------------------------------------------------------------------
31 // ----------------------------------------------------------------------------
33 BEGIN_EVENT_TABLE(wxVScrolledWindow
, wxPanel
)
34 EVT_SIZE(wxVScrolledWindow::OnSize
)
35 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll
)
37 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel
)
42 // ============================================================================
44 // ============================================================================
46 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow
, wxPanel
)
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 void wxVScrolledWindow::Init()
54 // we're initially empty
58 // this one should always be strictly positive
64 m_sumWheelRotation
= 0;
68 // ----------------------------------------------------------------------------
70 // ----------------------------------------------------------------------------
72 wxCoord
wxVScrolledWindow::EstimateTotalHeight() const
74 // estimate the total height: it is impossible to call
75 // OnGetLineHeight() for every line because there may be too many of
76 // them, so we just make a guess using some lines in the beginning,
77 // some in the end and some in the middle
78 static const size_t NUM_LINES_TO_SAMPLE
= 10;
81 if ( m_lineMax
< 3*NUM_LINES_TO_SAMPLE
)
83 // in this case calculating exactly is faster and more correct than
85 heightTotal
= GetLinesHeight(0, m_lineMax
);
87 else // too many lines to calculate exactly
89 // look at some lines in the beginning/middle/end
91 GetLinesHeight(0, NUM_LINES_TO_SAMPLE
) +
92 GetLinesHeight(m_lineMax
- NUM_LINES_TO_SAMPLE
, m_lineMax
) +
93 GetLinesHeight(m_lineMax
/2 - NUM_LINES_TO_SAMPLE
/2,
94 m_lineMax
/2 + NUM_LINES_TO_SAMPLE
/2);
96 // use the height of the lines we looked as the average
97 heightTotal
= (wxCoord
)
98 (((float)heightTotal
/ (3*NUM_LINES_TO_SAMPLE
)) * m_lineMax
);
104 wxCoord
wxVScrolledWindow::GetLinesHeight(size_t lineMin
, size_t lineMax
) const
106 if ( lineMin
== lineMax
)
108 else if ( lineMin
> lineMax
)
109 return -GetLinesHeight(lineMax
, lineMin
);
110 //else: lineMin < lineMax
112 // let the user code know that we're going to need all these lines
113 OnGetLinesHint(lineMin
, lineMax
);
115 // do sum up their heights
117 for ( size_t line
= lineMin
; line
< lineMax
; line
++ )
119 height
+= OnGetLineHeight(line
);
125 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast
, bool full
)
127 const wxCoord hWindow
= GetClientSize().y
;
129 // go upwards until we arrive at a line such that lineLast is not visible
130 // any more when it is shown
131 size_t lineFirst
= lineLast
;
135 h
+= OnGetLineHeight(lineFirst
);
139 // for this line to be fully visible we need to go one line
140 // down, but if it is enough for it to be only partly visible then
141 // this line will do as well
159 void wxVScrolledWindow::UpdateScrollbar()
161 // see how many lines can we fit on screen
162 const wxCoord hWindow
= GetClientSize().y
;
166 for ( line
= m_lineFirst
; line
< m_lineMax
; line
++ )
171 h
+= OnGetLineHeight(line
);
174 m_nVisible
= line
- m_lineFirst
;
176 int pageSize
= m_nVisible
;
179 // last line is only partially visible, we still need the scrollbar and
180 // so we have to "fix" pageSize because if it is equal to m_lineMax the
181 // scrollbar is not shown at all under MSW
185 // set the scrollbar parameters to reflect this
186 SetScrollbar(wxVERTICAL
, m_lineFirst
, pageSize
, m_lineMax
);
189 // ----------------------------------------------------------------------------
191 // ----------------------------------------------------------------------------
193 void wxVScrolledWindow::SetLineCount(size_t count
)
195 // save the number of lines
198 // and our estimate for their total height
199 m_heightTotal
= EstimateTotalHeight();
201 // recalculate the scrollbars parameters
202 m_lineFirst
= 1; // make sure it is != 0
206 void wxVScrolledWindow::RefreshLine(size_t line
)
208 // is this line visible?
209 if ( !IsVisible(line
) )
211 // no, it is useless to do anything
215 // calculate the rect occupied by this line on screen
217 rect
.width
= GetClientSize().x
;
218 rect
.height
= OnGetLineHeight(line
);
219 for ( size_t n
= GetVisibleBegin(); n
< line
; n
++ )
221 rect
.y
+= OnGetLineHeight(n
);
228 void wxVScrolledWindow::RefreshLines(size_t from
, size_t to
)
230 wxASSERT_MSG( from
<= to
, _T("RefreshLines(): empty range") );
232 // clump the range to just the visible lines -- it is useless to refresh
234 if ( from
< GetVisibleBegin() )
235 from
= GetVisibleBegin();
237 if ( to
>= GetVisibleEnd() )
238 to
= GetVisibleEnd();
242 // calculate the rect occupied by these lines on screen
244 rect
.width
= GetClientSize().x
;
245 for ( size_t nBefore
= GetVisibleBegin(); nBefore
< from
; nBefore
++ )
247 rect
.y
+= OnGetLineHeight(nBefore
);
250 for ( size_t nBetween
= from
; nBetween
< to
; nBetween
++ )
252 rect
.height
+= OnGetLineHeight(nBetween
);
259 void wxVScrolledWindow::RefreshAll()
266 int wxVScrolledWindow::HitTest(wxCoord
WXUNUSED(x
), wxCoord y
) const
268 const size_t lineMax
= GetVisibleEnd();
269 for ( size_t line
= GetVisibleBegin(); line
< lineMax
; line
++ )
271 y
-= OnGetLineHeight(line
);
279 // ----------------------------------------------------------------------------
281 // ----------------------------------------------------------------------------
283 bool wxVScrolledWindow::ScrollToLine(size_t line
)
287 // we're empty, code below doesn't make sense in this case
291 // determine the real first line to scroll to: we shouldn't scroll beyond
293 size_t lineFirstLast
= FindFirstFromBottom(m_lineMax
- 1, true);
294 if ( line
> lineFirstLast
)
295 line
= lineFirstLast
;
298 if ( line
== m_lineFirst
)
305 // remember the currently shown lines for the refresh code below
306 size_t lineFirstOld
= GetVisibleBegin(),
307 lineLastOld
= GetVisibleEnd();
312 // the size of scrollbar thumb could have changed
316 // finally refresh the display -- but only redraw as few lines as possible
318 if ( GetVisibleBegin() >= lineLastOld
||
319 GetVisibleEnd() <= lineFirstOld
)
321 // the simplest case: we don't have any old lines left, just redraw
325 else // overlap between the lines we showed before and should show now
327 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld
));
333 bool wxVScrolledWindow::ScrollLines(int lines
)
335 lines
+= m_lineFirst
;
339 return ScrollToLine(lines
);
342 bool wxVScrolledWindow::ScrollPages(int pages
)
344 bool didSomething
= false;
351 line
= GetVisibleEnd();
358 line
= FindFirstFromBottom(GetVisibleBegin());
362 didSomething
= ScrollToLine(line
);
368 // ----------------------------------------------------------------------------
370 // ----------------------------------------------------------------------------
372 void wxVScrolledWindow::OnSize(wxSizeEvent
& event
)
379 void wxVScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
383 const wxEventType evtType
= event
.GetEventType();
385 if ( evtType
== wxEVT_SCROLLWIN_TOP
)
389 else if ( evtType
== wxEVT_SCROLLWIN_BOTTOM
)
391 lineFirstNew
= m_lineMax
;
393 else if ( evtType
== wxEVT_SCROLLWIN_LINEUP
)
395 lineFirstNew
= m_lineFirst
? m_lineFirst
- 1 : 0;
397 else if ( evtType
== wxEVT_SCROLLWIN_LINEDOWN
)
399 lineFirstNew
= m_lineFirst
+ 1;
401 else if ( evtType
== wxEVT_SCROLLWIN_PAGEUP
)
403 lineFirstNew
= FindFirstFromBottom(m_lineFirst
);
405 else if ( evtType
== wxEVT_SCROLLWIN_PAGEDOWN
)
407 lineFirstNew
= GetVisibleEnd();
411 else if ( evtType
== wxEVT_SCROLLWIN_THUMBRELEASE
)
413 lineFirstNew
= event
.GetPosition();
415 else if ( evtType
== wxEVT_SCROLLWIN_THUMBTRACK
)
417 lineFirstNew
= event
.GetPosition();
420 else // unknown scroll event?
422 wxFAIL_MSG( _T("unknown scroll event type?") );
426 ScrollToLine(lineFirstNew
);
435 void wxVScrolledWindow::OnMouseWheel(wxMouseEvent
& event
)
437 m_sumWheelRotation
+= event
.GetWheelRotation();
438 int delta
= event
.GetWheelDelta();
440 // how much to scroll this time
441 int units_to_scroll
= -(m_sumWheelRotation
/delta
);
442 if ( !units_to_scroll
)
445 m_sumWheelRotation
+= units_to_scroll
*delta
;
447 if ( !event
.IsPageScroll() )
448 ScrollLines( units_to_scroll
*event
.GetLinesPerAction() );
450 // scroll pages instead of lines
451 ScrollPages( units_to_scroll
);