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"
31 #include "wx/vscroll.h"
33 // ----------------------------------------------------------------------------
35 // ----------------------------------------------------------------------------
37 BEGIN_EVENT_TABLE(wxVScrolledWindow
, wxPanel
)
38 EVT_SIZE(wxVScrolledWindow::OnSize
)
39 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll
)
41 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel
)
46 // ============================================================================
48 // ============================================================================
50 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow
, wxPanel
)
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 void wxVScrolledWindow::Init()
58 // we're initially empty
62 // this one should always be strictly positive
68 m_sumWheelRotation
= 0;
72 // ----------------------------------------------------------------------------
74 // ----------------------------------------------------------------------------
76 wxCoord
wxVScrolledWindow::EstimateTotalHeight() const
78 // estimate the total height: it is impossible to call
79 // OnGetLineHeight() for every line because there may be too many of
80 // them, so we just make a guess using some lines in the beginning,
81 // some in the end and some in the middle
82 static const size_t NUM_LINES_TO_SAMPLE
= 10;
85 if ( m_lineMax
< 3*NUM_LINES_TO_SAMPLE
)
87 // in this case calculating exactly is faster and more correct than
89 heightTotal
= GetLinesHeight(0, m_lineMax
);
91 else // too many lines to calculate exactly
93 // look at some lines in the beginning/middle/end
95 GetLinesHeight(0, NUM_LINES_TO_SAMPLE
) +
96 GetLinesHeight(m_lineMax
- NUM_LINES_TO_SAMPLE
, m_lineMax
) +
97 GetLinesHeight(m_lineMax
/2 - NUM_LINES_TO_SAMPLE
/2,
98 m_lineMax
/2 + NUM_LINES_TO_SAMPLE
/2);
100 // use the height of the lines we looked as the average
101 heightTotal
= (wxCoord
)
102 (((float)heightTotal
/ (3*NUM_LINES_TO_SAMPLE
)) * m_lineMax
);
108 wxCoord
wxVScrolledWindow::GetLinesHeight(size_t lineMin
, size_t lineMax
) const
110 if ( lineMin
== lineMax
)
112 else if ( lineMin
> lineMax
)
113 return -GetLinesHeight(lineMax
, lineMin
);
114 //else: lineMin < lineMax
116 // let the user code know that we're going to need all these lines
117 OnGetLinesHint(lineMin
, lineMax
);
119 // do sum up their heights
121 for ( size_t line
= lineMin
; line
< lineMax
; line
++ )
123 height
+= OnGetLineHeight(line
);
129 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast
, bool full
)
131 const wxCoord hWindow
= GetClientSize().y
;
133 // go upwards until we arrive at a line such that lineLast is not visible
134 // any more when it is shown
135 size_t lineFirst
= lineLast
;
139 h
+= OnGetLineHeight(lineFirst
);
143 // for this line to be fully visible we need to go one line
144 // down, but if it is enough for it to be only partly visible then
145 // this line will do as well
163 void wxVScrolledWindow::RemoveScrollbar()
166 m_nVisible
= m_lineMax
;
167 SetScrollbar(wxVERTICAL
, 0, 0, 0);
170 void wxVScrolledWindow::UpdateScrollbar()
172 // if there is nothing to scroll, remove the scrollbar
179 // see how many lines can we fit on screen
180 const wxCoord hWindow
= GetClientSize().y
;
184 for ( line
= m_lineFirst
; line
< m_lineMax
; line
++ )
189 h
+= OnGetLineHeight(line
);
192 // if we still have remaining space below, maybe we can fit everything?
196 for ( size_t lineFirst
= m_lineFirst
; lineFirst
> 0; lineFirst
-- )
198 hAll
+= OnGetLineHeight(m_lineFirst
- 1);
199 if ( hAll
> hWindow
)
203 if ( hAll
< hWindow
)
205 // we don't need scrollbar at all
211 m_nVisible
= line
- m_lineFirst
;
213 int pageSize
= m_nVisible
;
216 // last line is only partially visible, we still need the scrollbar and
217 // so we have to "fix" pageSize because if it is equal to m_lineMax the
218 // scrollbar is not shown at all under MSW
222 // set the scrollbar parameters to reflect this
223 SetScrollbar(wxVERTICAL
, m_lineFirst
, pageSize
, m_lineMax
);
226 // ----------------------------------------------------------------------------
228 // ----------------------------------------------------------------------------
230 void wxVScrolledWindow::SetLineCount(size_t count
)
232 // save the number of lines
235 // and our estimate for their total height
236 m_heightTotal
= EstimateTotalHeight();
238 // ScrollToLine() will update the scrollbar itself if it changes the line
239 // we pass to it because it's out of [new] range
240 size_t oldScrollPos
= m_lineFirst
;
241 ScrollToLine(m_lineFirst
);
242 if ( oldScrollPos
== m_lineFirst
)
244 // but if it didn't do it, we still need to update the scrollbar to
245 // reflect the changed number of lines ourselves
250 void wxVScrolledWindow::RefreshLine(size_t line
)
252 // is this line visible?
253 if ( !IsVisible(line
) )
255 // no, it is useless to do anything
259 // calculate the rect occupied by this line on screen
261 rect
.width
= GetClientSize().x
;
262 rect
.height
= OnGetLineHeight(line
);
263 for ( size_t n
= GetVisibleBegin(); n
< line
; n
++ )
265 rect
.y
+= OnGetLineHeight(n
);
272 void wxVScrolledWindow::RefreshLines(size_t from
, size_t to
)
274 wxASSERT_MSG( from
<= to
, _T("RefreshLines(): empty range") );
276 // clump the range to just the visible lines -- it is useless to refresh
278 if ( from
< GetVisibleBegin() )
279 from
= GetVisibleBegin();
281 if ( to
>= GetVisibleEnd() )
282 to
= GetVisibleEnd();
286 // calculate the rect occupied by these lines on screen
288 rect
.width
= GetClientSize().x
;
289 for ( size_t nBefore
= GetVisibleBegin(); nBefore
< from
; nBefore
++ )
291 rect
.y
+= OnGetLineHeight(nBefore
);
294 for ( size_t nBetween
= from
; nBetween
< to
; nBetween
++ )
296 rect
.height
+= OnGetLineHeight(nBetween
);
303 void wxVScrolledWindow::RefreshAll()
310 bool wxVScrolledWindow::Layout()
314 // adjust the sizer dimensions/position taking into account the
315 // virtual size and scrolled position of the window.
318 GetVirtualSize(&w
, &h
);
320 // x is always 0 so no variable needed
321 int y
= -GetLinesHeight(0, GetFirstVisibleLine());
323 GetSizer()->SetDimension(0, y
, w
, h
);
327 // fall back to default for LayoutConstraints
328 return wxPanel::Layout();
331 int wxVScrolledWindow::HitTest(wxCoord
WXUNUSED(x
), wxCoord y
) const
333 const size_t lineMax
= GetVisibleEnd();
334 for ( size_t line
= GetVisibleBegin(); line
< lineMax
; line
++ )
336 y
-= OnGetLineHeight(line
);
344 // ----------------------------------------------------------------------------
346 // ----------------------------------------------------------------------------
348 bool wxVScrolledWindow::ScrollToLine(size_t line
)
352 // we're empty, code below doesn't make sense in this case
356 // determine the real first line to scroll to: we shouldn't scroll beyond
358 size_t lineFirstLast
= FindFirstFromBottom(m_lineMax
- 1, true);
359 if ( line
> lineFirstLast
)
360 line
= lineFirstLast
;
363 if ( line
== m_lineFirst
)
370 // remember the currently shown lines for the refresh code below
371 size_t lineFirstOld
= GetVisibleBegin(),
372 lineLastOld
= GetVisibleEnd();
377 // the size of scrollbar thumb could have changed
381 // finally refresh the display -- but only redraw as few lines as possible
383 if ( GetVisibleBegin() >= lineLastOld
||
384 GetVisibleEnd() <= lineFirstOld
)
386 // the simplest case: we don't have any old lines left, just redraw
390 else // overlap between the lines we showed before and should show now
392 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld
));
398 bool wxVScrolledWindow::ScrollLines(int lines
)
400 lines
+= m_lineFirst
;
404 return ScrollToLine(lines
);
407 bool wxVScrolledWindow::ScrollPages(int pages
)
409 bool didSomething
= false;
416 line
= GetVisibleEnd();
423 line
= FindFirstFromBottom(GetVisibleBegin());
427 didSomething
= ScrollToLine(line
);
433 // ----------------------------------------------------------------------------
435 // ----------------------------------------------------------------------------
437 void wxVScrolledWindow::OnSize(wxSizeEvent
& event
)
444 void wxVScrolledWindow::OnScroll(wxScrollWinEvent
& event
)
448 const wxEventType evtType
= event
.GetEventType();
450 if ( evtType
== wxEVT_SCROLLWIN_TOP
)
454 else if ( evtType
== wxEVT_SCROLLWIN_BOTTOM
)
456 lineFirstNew
= m_lineMax
;
458 else if ( evtType
== wxEVT_SCROLLWIN_LINEUP
)
460 lineFirstNew
= m_lineFirst
? m_lineFirst
- 1 : 0;
462 else if ( evtType
== wxEVT_SCROLLWIN_LINEDOWN
)
464 lineFirstNew
= m_lineFirst
+ 1;
466 else if ( evtType
== wxEVT_SCROLLWIN_PAGEUP
)
468 lineFirstNew
= FindFirstFromBottom(m_lineFirst
);
470 else if ( evtType
== wxEVT_SCROLLWIN_PAGEDOWN
)
472 lineFirstNew
= GetVisibleEnd();
476 else if ( evtType
== wxEVT_SCROLLWIN_THUMBRELEASE
)
478 lineFirstNew
= event
.GetPosition();
480 else if ( evtType
== wxEVT_SCROLLWIN_THUMBTRACK
)
482 lineFirstNew
= event
.GetPosition();
485 else // unknown scroll event?
487 wxFAIL_MSG( _T("unknown scroll event type?") );
491 ScrollToLine(lineFirstNew
);
500 void wxVScrolledWindow::OnMouseWheel(wxMouseEvent
& event
)
502 m_sumWheelRotation
+= event
.GetWheelRotation();
503 int delta
= event
.GetWheelDelta();
505 // how much to scroll this time
506 int units_to_scroll
= -(m_sumWheelRotation
/delta
);
507 if ( !units_to_scroll
)
510 m_sumWheelRotation
+= units_to_scroll
*delta
;
512 if ( !event
.IsPageScroll() )
513 ScrollLines( units_to_scroll
*event
.GetLinesPerAction() );
515 // scroll pages instead of lines
516 ScrollPages( units_to_scroll
);
519 #endif // wxUSE_MOUSEWHEEL