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