]> git.saurik.com Git - wxWidgets.git/blame - src/generic/vscroll.cpp
[ 1509599 ] 'Split pickers page in widgets sample' with more icons and rebaking.
[wxWidgets.git] / src / generic / vscroll.cpp
CommitLineData
cf7d6329
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/generic/vscroll.cpp
3// Purpose: wxVScrolledWindow implementation
4// Author: Vadim Zeitlin
234b1c01 5// Modified by:
cf7d6329
VZ
6// Created: 30.05.03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
65571936 9// Licence: wxWindows licence
cf7d6329
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
687dcff3
VS
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24#pragma hdrstop
25#endif
26
2c1e2499
VZ
27#ifndef WX_PRECOMP
28 #include "wx/sizer.h"
29#endif
30
cf7d6329
VZ
31#include "wx/vscroll.h"
32
33// ----------------------------------------------------------------------------
34// event tables
35// ----------------------------------------------------------------------------
36
37BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
38 EVT_SIZE(wxVScrolledWindow::OnSize)
39 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
4719e58d
RD
40#if wxUSE_MOUSEWHEEL
41 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
42#endif
cf7d6329
VZ
43END_EVENT_TABLE()
44
45
46// ============================================================================
47// implementation
48// ============================================================================
49
0c8392ca
RD
50IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
51
cf7d6329
VZ
52// ----------------------------------------------------------------------------
53// initialization
54// ----------------------------------------------------------------------------
55
56void wxVScrolledWindow::Init()
57{
58 // we're initially empty
59 m_lineMax =
60 m_lineFirst = 0;
61
62 // this one should always be strictly positive
63 m_nVisible = 1;
64
65 m_heightTotal = 0;
4719e58d
RD
66
67#if wxUSE_MOUSEWHEEL
68 m_sumWheelRotation = 0;
69#endif
cf7d6329
VZ
70}
71
72// ----------------------------------------------------------------------------
73// various helpers
74// ----------------------------------------------------------------------------
75
1e0af0bc
VZ
76wxCoord wxVScrolledWindow::EstimateTotalHeight() const
77{
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;
83
84 wxCoord heightTotal;
85 if ( m_lineMax < 3*NUM_LINES_TO_SAMPLE )
86 {
87 // in this case calculating exactly is faster and more correct than
88 // guessing
89 heightTotal = GetLinesHeight(0, m_lineMax);
90 }
91 else // too many lines to calculate exactly
92 {
93 // look at some lines in the beginning/middle/end
94 heightTotal =
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);
99
100 // use the height of the lines we looked as the average
101 heightTotal = (wxCoord)
999836aa 102 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
1e0af0bc
VZ
103 }
104
105 return heightTotal;
106}
107
cf7d6329
VZ
108wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
109{
110 if ( lineMin == lineMax )
111 return 0;
112 else if ( lineMin > lineMax )
113 return -GetLinesHeight(lineMax, lineMin);
114 //else: lineMin < lineMax
115
116 // let the user code know that we're going to need all these lines
117 OnGetLinesHint(lineMin, lineMax);
118
119 // do sum up their heights
120 wxCoord height = 0;
121 for ( size_t line = lineMin; line < lineMax; line++ )
122 {
123 height += OnGetLineHeight(line);
124 }
125
126 return height;
127}
128
0b49ccf8 129size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
cf7d6329
VZ
130{
131 const wxCoord hWindow = GetClientSize().y;
132
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;
136 wxCoord h = 0;
137 for ( ;; )
138 {
139 h += OnGetLineHeight(lineFirst);
140
141 if ( h > hWindow )
142 {
0b49ccf8
VZ
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
146 if ( full )
147 {
148 lineFirst++;
149 }
cf7d6329
VZ
150
151 break;
152 }
153
154 if ( !lineFirst )
155 break;
156
157 lineFirst--;
158 }
159
160 return lineFirst;
161}
162
c1aa5517
VZ
163void wxVScrolledWindow::RemoveScrollbar()
164{
165 m_lineFirst = 0;
166 m_nVisible = m_lineMax;
167 SetScrollbar(wxVERTICAL, 0, 0, 0);
168}
169
cf7d6329
VZ
170void wxVScrolledWindow::UpdateScrollbar()
171{
172 // see how many lines can we fit on screen
173 const wxCoord hWindow = GetClientSize().y;
174
175 wxCoord h = 0;
176 size_t line;
177 for ( line = m_lineFirst; line < m_lineMax; line++ )
178 {
179 if ( h > hWindow )
180 break;
181
182 h += OnGetLineHeight(line);
183 }
184
63916e44
VZ
185 // if we still have remaining space below, maybe we can fit everything?
186 if ( h < hWindow )
187 {
188 wxCoord hAll = h;
189 for ( size_t lineFirst = m_lineFirst; lineFirst > 0; lineFirst-- )
190 {
191 hAll += OnGetLineHeight(m_lineFirst - 1);
192 if ( hAll > hWindow )
193 break;
194 }
195
196 if ( hAll < hWindow )
197 {
198 // we don't need scrollbar at all
c1aa5517
VZ
199 RemoveScrollbar();
200 return;
63916e44
VZ
201 }
202 }
203
cf7d6329
VZ
204 m_nVisible = line - m_lineFirst;
205
206 int pageSize = m_nVisible;
207 if ( h > hWindow )
208 {
209 // last line is only partially visible, we still need the scrollbar and
210 // so we have to "fix" pageSize because if it is equal to m_lineMax the
211 // scrollbar is not shown at all under MSW
212 pageSize--;
213 }
214
215 // set the scrollbar parameters to reflect this
216 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
217}
218
219// ----------------------------------------------------------------------------
220// operations
221// ----------------------------------------------------------------------------
222
223void wxVScrolledWindow::SetLineCount(size_t count)
224{
225 // save the number of lines
226 m_lineMax = count;
227
1e0af0bc
VZ
228 // and our estimate for their total height
229 m_heightTotal = EstimateTotalHeight();
cf7d6329
VZ
230
231 // recalculate the scrollbars parameters
c1aa5517
VZ
232 if ( count )
233 {
234 m_lineFirst = 1; // make sure it is != 0
235 ScrollToLine(0);
236 }
237 else // no items
238 {
239 RemoveScrollbar();
240 }
cf7d6329
VZ
241}
242
e0c6027b
VZ
243void wxVScrolledWindow::RefreshLine(size_t line)
244{
245 // is this line visible?
246 if ( !IsVisible(line) )
247 {
248 // no, it is useless to do anything
249 return;
250 }
251
252 // calculate the rect occupied by this line on screen
253 wxRect rect;
254 rect.width = GetClientSize().x;
255 rect.height = OnGetLineHeight(line);
dd932cbe 256 for ( size_t n = GetVisibleBegin(); n < line; n++ )
e0c6027b
VZ
257 {
258 rect.y += OnGetLineHeight(n);
259 }
ae0f0223
VZ
260
261 // do refresh it
262 RefreshRect(rect);
263}
264
265void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
266{
267 wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
268
269 // clump the range to just the visible lines -- it is useless to refresh
270 // the other ones
dd932cbe
VZ
271 if ( from < GetVisibleBegin() )
272 from = GetVisibleBegin();
ae0f0223 273
dd932cbe
VZ
274 if ( to >= GetVisibleEnd() )
275 to = GetVisibleEnd();
276 else
277 to++;
ae0f0223
VZ
278
279 // calculate the rect occupied by these lines on screen
280 wxRect rect;
281 rect.width = GetClientSize().x;
dd932cbe 282 for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
ae0f0223
VZ
283 {
284 rect.y += OnGetLineHeight(nBefore);
285 }
286
dd932cbe 287 for ( size_t nBetween = from; nBetween < to; nBetween++ )
ae0f0223
VZ
288 {
289 rect.height += OnGetLineHeight(nBetween);
290 }
e0c6027b
VZ
291
292 // do refresh it
293 RefreshRect(rect);
294}
295
8b053348
VZ
296void wxVScrolledWindow::RefreshAll()
297{
298 UpdateScrollbar();
299
300 Refresh();
301}
302
61bb5a9c
VZ
303bool wxVScrolledWindow::Layout()
304{
305 if ( GetSizer() )
306 {
307 // adjust the sizer dimensions/position taking into account the
308 // virtual size and scrolled position of the window.
309
310 int w, h;
311 GetVirtualSize(&w, &h);
312
313 // x is always 0 so no variable needed
314 int y = -GetLinesHeight(0, GetFirstVisibleLine());
315
316 GetSizer()->SetDimension(0, y, w, h);
317 return true;
318 }
319
320 // fall back to default for LayoutConstraints
321 return wxPanel::Layout();
322}
323
e0c6027b
VZ
324int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
325{
dd932cbe
VZ
326 const size_t lineMax = GetVisibleEnd();
327 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
e0c6027b
VZ
328 {
329 y -= OnGetLineHeight(line);
330 if ( y < 0 )
331 return line;
332 }
333
334 return wxNOT_FOUND;
335}
336
cf7d6329
VZ
337// ----------------------------------------------------------------------------
338// scrolling
339// ----------------------------------------------------------------------------
340
341bool wxVScrolledWindow::ScrollToLine(size_t line)
342{
343 if ( !m_lineMax )
344 {
345 // we're empty, code below doesn't make sense in this case
346 return false;
347 }
348
349 // determine the real first line to scroll to: we shouldn't scroll beyond
350 // the end
0b49ccf8 351 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
cf7d6329
VZ
352 if ( line > lineFirstLast )
353 line = lineFirstLast;
354
355 // anything to do?
356 if ( line == m_lineFirst )
357 {
358 // no
359 return false;
360 }
361
362
363 // remember the currently shown lines for the refresh code below
234b1c01
VZ
364 size_t lineFirstOld = GetVisibleBegin(),
365 lineLastOld = GetVisibleEnd();
cf7d6329
VZ
366
367 m_lineFirst = line;
368
369
370 // the size of scrollbar thumb could have changed
371 UpdateScrollbar();
372
234b1c01
VZ
373
374 // finally refresh the display -- but only redraw as few lines as possible
375 // to avoid flicker
376 if ( GetVisibleBegin() >= lineLastOld ||
377 GetVisibleEnd() <= lineFirstOld )
378 {
379 // the simplest case: we don't have any old lines left, just redraw
380 // everything
381 Refresh();
382 }
383 else // overlap between the lines we showed before and should show now
384 {
385 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
386 }
cf7d6329
VZ
387
388 return true;
389}
390
391bool wxVScrolledWindow::ScrollLines(int lines)
392{
393 lines += m_lineFirst;
394 if ( lines < 0 )
395 lines = 0;
396
397 return ScrollToLine(lines);
398}
399
400bool wxVScrolledWindow::ScrollPages(int pages)
401{
402 bool didSomething = false;
403
404 while ( pages )
405 {
406 int line;
407 if ( pages > 0 )
408 {
dd932cbe
VZ
409 line = GetVisibleEnd();
410 if ( line )
411 line--;
cf7d6329
VZ
412 pages--;
413 }
414 else // pages < 0
415 {
dd932cbe 416 line = FindFirstFromBottom(GetVisibleBegin());
cf7d6329
VZ
417 pages++;
418 }
419
420 didSomething = ScrollToLine(line);
421 }
422
423 return didSomething;
424}
425
426// ----------------------------------------------------------------------------
427// event handling
428// ----------------------------------------------------------------------------
429
430void wxVScrolledWindow::OnSize(wxSizeEvent& event)
431{
432 UpdateScrollbar();
433
434 event.Skip();
435}
436
437void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
438{
439 size_t lineFirstNew;
440
441 const wxEventType evtType = event.GetEventType();
5d2ad055 442
cf7d6329
VZ
443 if ( evtType == wxEVT_SCROLLWIN_TOP )
444 {
445 lineFirstNew = 0;
446 }
447 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
448 {
449 lineFirstNew = m_lineMax;
450 }
451 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
452 {
453 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
454 }
455 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
456 {
457 lineFirstNew = m_lineFirst + 1;
458 }
459 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
460 {
461 lineFirstNew = FindFirstFromBottom(m_lineFirst);
462 }
463 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
464 {
dd932cbe
VZ
465 lineFirstNew = GetVisibleEnd();
466 if ( lineFirstNew )
467 lineFirstNew--;
cf7d6329 468 }
5d2ad055
RD
469 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
470 {
471 lineFirstNew = event.GetPosition();
472 }
473 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
474 {
475 lineFirstNew = event.GetPosition();
476 }
ca65c044 477
cf7d6329
VZ
478 else // unknown scroll event?
479 {
5d2ad055
RD
480 wxFAIL_MSG( _T("unknown scroll event type?") );
481 return;
cf7d6329
VZ
482 }
483
484 ScrollToLine(lineFirstNew);
b544a278
VZ
485
486#ifdef __WXMAC__
487 Update();
488#endif // __WXMAC__
cf7d6329
VZ
489}
490
4719e58d
RD
491#if wxUSE_MOUSEWHEEL
492
493void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
494{
495 m_sumWheelRotation += event.GetWheelRotation();
496 int delta = event.GetWheelDelta();
497
498 // how much to scroll this time
499 int units_to_scroll = -(m_sumWheelRotation/delta);
500 if ( !units_to_scroll )
501 return;
502
503 m_sumWheelRotation += units_to_scroll*delta;
504
505 if ( !event.IsPageScroll() )
506 ScrollLines( units_to_scroll*event.GetLinesPerAction() );
507 else
508 // scroll pages instead of lines
509 ScrollPages( units_to_scroll );
510}
511
234b1c01 512#endif // wxUSE_MOUSEWHEEL
d77836e4 513