]> git.saurik.com Git - wxWidgets.git/blame - src/generic/vscroll.cpp
fixed typo in last commit
[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{
861a14e1
VZ
172 // if there is nothing to scroll, remove the scrollbar
173 if ( !m_lineMax )
174 {
175 RemoveScrollbar();
176 return;
177 }
178
cf7d6329
VZ
179 // see how many lines can we fit on screen
180 const wxCoord hWindow = GetClientSize().y;
181
182 wxCoord h = 0;
183 size_t line;
184 for ( line = m_lineFirst; line < m_lineMax; line++ )
185 {
186 if ( h > hWindow )
187 break;
188
189 h += OnGetLineHeight(line);
190 }
191
63916e44
VZ
192 // if we still have remaining space below, maybe we can fit everything?
193 if ( h < hWindow )
194 {
195 wxCoord hAll = h;
196 for ( size_t lineFirst = m_lineFirst; lineFirst > 0; lineFirst-- )
197 {
198 hAll += OnGetLineHeight(m_lineFirst - 1);
199 if ( hAll > hWindow )
200 break;
201 }
202
203 if ( hAll < hWindow )
204 {
205 // we don't need scrollbar at all
c1aa5517
VZ
206 RemoveScrollbar();
207 return;
63916e44
VZ
208 }
209 }
210
cf7d6329
VZ
211 m_nVisible = line - m_lineFirst;
212
213 int pageSize = m_nVisible;
214 if ( h > hWindow )
215 {
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
219 pageSize--;
220 }
221
222 // set the scrollbar parameters to reflect this
223 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
224}
225
226// ----------------------------------------------------------------------------
227// operations
228// ----------------------------------------------------------------------------
229
230void wxVScrolledWindow::SetLineCount(size_t count)
231{
232 // save the number of lines
233 m_lineMax = count;
234
1e0af0bc
VZ
235 // and our estimate for their total height
236 m_heightTotal = EstimateTotalHeight();
cf7d6329 237
861a14e1
VZ
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 )
c1aa5517 243 {
861a14e1
VZ
244 // but if it didn't do it, we still need to update the scrollbar to
245 // reflect the changed number of lines ourselves
246 UpdateScrollbar();
c1aa5517 247 }
cf7d6329
VZ
248}
249
e0c6027b
VZ
250void wxVScrolledWindow::RefreshLine(size_t line)
251{
252 // is this line visible?
253 if ( !IsVisible(line) )
254 {
255 // no, it is useless to do anything
256 return;
257 }
258
259 // calculate the rect occupied by this line on screen
260 wxRect rect;
261 rect.width = GetClientSize().x;
262 rect.height = OnGetLineHeight(line);
dd932cbe 263 for ( size_t n = GetVisibleBegin(); n < line; n++ )
e0c6027b
VZ
264 {
265 rect.y += OnGetLineHeight(n);
266 }
ae0f0223
VZ
267
268 // do refresh it
269 RefreshRect(rect);
270}
271
272void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
273{
274 wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
275
276 // clump the range to just the visible lines -- it is useless to refresh
277 // the other ones
dd932cbe
VZ
278 if ( from < GetVisibleBegin() )
279 from = GetVisibleBegin();
ae0f0223 280
dd932cbe
VZ
281 if ( to >= GetVisibleEnd() )
282 to = GetVisibleEnd();
283 else
284 to++;
ae0f0223
VZ
285
286 // calculate the rect occupied by these lines on screen
287 wxRect rect;
288 rect.width = GetClientSize().x;
dd932cbe 289 for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
ae0f0223
VZ
290 {
291 rect.y += OnGetLineHeight(nBefore);
292 }
293
dd932cbe 294 for ( size_t nBetween = from; nBetween < to; nBetween++ )
ae0f0223
VZ
295 {
296 rect.height += OnGetLineHeight(nBetween);
297 }
e0c6027b
VZ
298
299 // do refresh it
300 RefreshRect(rect);
301}
302
8b053348
VZ
303void wxVScrolledWindow::RefreshAll()
304{
305 UpdateScrollbar();
306
307 Refresh();
308}
309
61bb5a9c
VZ
310bool wxVScrolledWindow::Layout()
311{
312 if ( GetSizer() )
313 {
314 // adjust the sizer dimensions/position taking into account the
315 // virtual size and scrolled position of the window.
316
ac55e0a1 317 int w = 0, h = 0;
61bb5a9c
VZ
318 GetVirtualSize(&w, &h);
319
320 // x is always 0 so no variable needed
321 int y = -GetLinesHeight(0, GetFirstVisibleLine());
322
323 GetSizer()->SetDimension(0, y, w, h);
324 return true;
325 }
326
327 // fall back to default for LayoutConstraints
328 return wxPanel::Layout();
329}
330
e0c6027b
VZ
331int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
332{
dd932cbe
VZ
333 const size_t lineMax = GetVisibleEnd();
334 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
e0c6027b
VZ
335 {
336 y -= OnGetLineHeight(line);
337 if ( y < 0 )
338 return line;
339 }
340
341 return wxNOT_FOUND;
342}
343
cf7d6329
VZ
344// ----------------------------------------------------------------------------
345// scrolling
346// ----------------------------------------------------------------------------
347
348bool wxVScrolledWindow::ScrollToLine(size_t line)
349{
350 if ( !m_lineMax )
351 {
352 // we're empty, code below doesn't make sense in this case
353 return false;
354 }
355
356 // determine the real first line to scroll to: we shouldn't scroll beyond
357 // the end
0b49ccf8 358 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
cf7d6329
VZ
359 if ( line > lineFirstLast )
360 line = lineFirstLast;
361
362 // anything to do?
363 if ( line == m_lineFirst )
364 {
365 // no
366 return false;
367 }
368
369
370 // remember the currently shown lines for the refresh code below
234b1c01
VZ
371 size_t lineFirstOld = GetVisibleBegin(),
372 lineLastOld = GetVisibleEnd();
cf7d6329
VZ
373
374 m_lineFirst = line;
375
376
377 // the size of scrollbar thumb could have changed
378 UpdateScrollbar();
379
234b1c01
VZ
380
381 // finally refresh the display -- but only redraw as few lines as possible
382 // to avoid flicker
383 if ( GetVisibleBegin() >= lineLastOld ||
384 GetVisibleEnd() <= lineFirstOld )
385 {
386 // the simplest case: we don't have any old lines left, just redraw
387 // everything
388 Refresh();
389 }
390 else // overlap between the lines we showed before and should show now
391 {
392 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
393 }
cf7d6329
VZ
394
395 return true;
396}
397
398bool wxVScrolledWindow::ScrollLines(int lines)
399{
400 lines += m_lineFirst;
401 if ( lines < 0 )
402 lines = 0;
403
404 return ScrollToLine(lines);
405}
406
407bool wxVScrolledWindow::ScrollPages(int pages)
408{
409 bool didSomething = false;
410
411 while ( pages )
412 {
413 int line;
414 if ( pages > 0 )
415 {
dd932cbe
VZ
416 line = GetVisibleEnd();
417 if ( line )
418 line--;
cf7d6329
VZ
419 pages--;
420 }
421 else // pages < 0
422 {
dd932cbe 423 line = FindFirstFromBottom(GetVisibleBegin());
cf7d6329
VZ
424 pages++;
425 }
426
427 didSomething = ScrollToLine(line);
428 }
429
430 return didSomething;
431}
432
433// ----------------------------------------------------------------------------
434// event handling
435// ----------------------------------------------------------------------------
436
437void wxVScrolledWindow::OnSize(wxSizeEvent& event)
438{
439 UpdateScrollbar();
440
441 event.Skip();
442}
443
444void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
445{
446 size_t lineFirstNew;
447
448 const wxEventType evtType = event.GetEventType();
5d2ad055 449
cf7d6329
VZ
450 if ( evtType == wxEVT_SCROLLWIN_TOP )
451 {
452 lineFirstNew = 0;
453 }
454 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
455 {
456 lineFirstNew = m_lineMax;
457 }
458 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
459 {
460 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
461 }
462 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
463 {
464 lineFirstNew = m_lineFirst + 1;
465 }
466 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
467 {
468 lineFirstNew = FindFirstFromBottom(m_lineFirst);
469 }
470 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
471 {
dd932cbe
VZ
472 lineFirstNew = GetVisibleEnd();
473 if ( lineFirstNew )
474 lineFirstNew--;
cf7d6329 475 }
5d2ad055
RD
476 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
477 {
478 lineFirstNew = event.GetPosition();
479 }
480 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
481 {
482 lineFirstNew = event.GetPosition();
483 }
ca65c044 484
cf7d6329
VZ
485 else // unknown scroll event?
486 {
5d2ad055
RD
487 wxFAIL_MSG( _T("unknown scroll event type?") );
488 return;
cf7d6329
VZ
489 }
490
491 ScrollToLine(lineFirstNew);
b544a278
VZ
492
493#ifdef __WXMAC__
494 Update();
495#endif // __WXMAC__
cf7d6329
VZ
496}
497
4719e58d
RD
498#if wxUSE_MOUSEWHEEL
499
500void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
501{
502 m_sumWheelRotation += event.GetWheelRotation();
503 int delta = event.GetWheelDelta();
504
505 // how much to scroll this time
506 int units_to_scroll = -(m_sumWheelRotation/delta);
507 if ( !units_to_scroll )
508 return;
509
510 m_sumWheelRotation += units_to_scroll*delta;
511
512 if ( !event.IsPageScroll() )
513 ScrollLines( units_to_scroll*event.GetLinesPerAction() );
514 else
515 // scroll pages instead of lines
516 ScrollPages( units_to_scroll );
517}
518
234b1c01 519#endif // wxUSE_MOUSEWHEEL
d77836e4 520