]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/vscroll.cpp
minor cleanup
[wxWidgets.git] / src / generic / vscroll.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/generic/vscroll.cpp
3// Purpose: wxVScrolledWindow implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 30.05.03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24#pragma hdrstop
25#endif
26
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)
36#if wxUSE_MOUSEWHEEL
37 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
38#endif
39END_EVENT_TABLE()
40
41
42// ============================================================================
43// implementation
44// ============================================================================
45
46IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
47
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;
62
63#if wxUSE_MOUSEWHEEL
64 m_sumWheelRotation = 0;
65#endif
66}
67
68// ----------------------------------------------------------------------------
69// various helpers
70// ----------------------------------------------------------------------------
71
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)
98 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
99 }
100
101 return heightTotal;
102}
103
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
125size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
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 {
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 }
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
174 m_nVisible = line - m_lineFirst;
175
176 int pageSize = m_nVisible;
177 if ( h > hWindow )
178 {
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
182 pageSize--;
183 }
184
185 // set the scrollbar parameters to reflect this
186 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
187}
188
189// ----------------------------------------------------------------------------
190// operations
191// ----------------------------------------------------------------------------
192
193void wxVScrolledWindow::SetLineCount(size_t count)
194{
195 // save the number of lines
196 m_lineMax = count;
197
198 // and our estimate for their total height
199 m_heightTotal = EstimateTotalHeight();
200
201 // recalculate the scrollbars parameters
202 m_lineFirst = 1; // make sure it is != 0
203 ScrollToLine(0);
204}
205
206void wxVScrolledWindow::RefreshLine(size_t line)
207{
208 // is this line visible?
209 if ( !IsVisible(line) )
210 {
211 // no, it is useless to do anything
212 return;
213 }
214
215 // calculate the rect occupied by this line on screen
216 wxRect rect;
217 rect.width = GetClientSize().x;
218 rect.height = OnGetLineHeight(line);
219 for ( size_t n = GetVisibleBegin(); n < line; n++ )
220 {
221 rect.y += OnGetLineHeight(n);
222 }
223
224 // do refresh it
225 RefreshRect(rect);
226}
227
228void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
229{
230 wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
231
232 // clump the range to just the visible lines -- it is useless to refresh
233 // the other ones
234 if ( from < GetVisibleBegin() )
235 from = GetVisibleBegin();
236
237 if ( to >= GetVisibleEnd() )
238 to = GetVisibleEnd();
239 else
240 to++;
241
242 // calculate the rect occupied by these lines on screen
243 wxRect rect;
244 rect.width = GetClientSize().x;
245 for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
246 {
247 rect.y += OnGetLineHeight(nBefore);
248 }
249
250 for ( size_t nBetween = from; nBetween < to; nBetween++ )
251 {
252 rect.height += OnGetLineHeight(nBetween);
253 }
254
255 // do refresh it
256 RefreshRect(rect);
257}
258
259void wxVScrolledWindow::RefreshAll()
260{
261 UpdateScrollbar();
262
263 Refresh();
264}
265
266int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
267{
268 const size_t lineMax = GetVisibleEnd();
269 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
270 {
271 y -= OnGetLineHeight(line);
272 if ( y < 0 )
273 return line;
274 }
275
276 return wxNOT_FOUND;
277}
278
279// ----------------------------------------------------------------------------
280// scrolling
281// ----------------------------------------------------------------------------
282
283bool wxVScrolledWindow::ScrollToLine(size_t line)
284{
285 if ( !m_lineMax )
286 {
287 // we're empty, code below doesn't make sense in this case
288 return false;
289 }
290
291 // determine the real first line to scroll to: we shouldn't scroll beyond
292 // the end
293 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
294 if ( line > lineFirstLast )
295 line = lineFirstLast;
296
297 // anything to do?
298 if ( line == m_lineFirst )
299 {
300 // no
301 return false;
302 }
303
304
305 // remember the currently shown lines for the refresh code below
306 size_t lineFirstOld = GetVisibleBegin(),
307 lineLastOld = GetVisibleEnd();
308
309 m_lineFirst = line;
310
311
312 // the size of scrollbar thumb could have changed
313 UpdateScrollbar();
314
315
316 // finally refresh the display -- but only redraw as few lines as possible
317 // to avoid flicker
318 if ( GetVisibleBegin() >= lineLastOld ||
319 GetVisibleEnd() <= lineFirstOld )
320 {
321 // the simplest case: we don't have any old lines left, just redraw
322 // everything
323 Refresh();
324 }
325 else // overlap between the lines we showed before and should show now
326 {
327 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
328 }
329
330 return true;
331}
332
333bool wxVScrolledWindow::ScrollLines(int lines)
334{
335 lines += m_lineFirst;
336 if ( lines < 0 )
337 lines = 0;
338
339 return ScrollToLine(lines);
340}
341
342bool wxVScrolledWindow::ScrollPages(int pages)
343{
344 bool didSomething = false;
345
346 while ( pages )
347 {
348 int line;
349 if ( pages > 0 )
350 {
351 line = GetVisibleEnd();
352 if ( line )
353 line--;
354 pages--;
355 }
356 else // pages < 0
357 {
358 line = FindFirstFromBottom(GetVisibleBegin());
359 pages++;
360 }
361
362 didSomething = ScrollToLine(line);
363 }
364
365 return didSomething;
366}
367
368// ----------------------------------------------------------------------------
369// event handling
370// ----------------------------------------------------------------------------
371
372void wxVScrolledWindow::OnSize(wxSizeEvent& event)
373{
374 UpdateScrollbar();
375
376 event.Skip();
377}
378
379void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
380{
381 size_t lineFirstNew;
382
383 const wxEventType evtType = event.GetEventType();
384
385 if ( evtType == wxEVT_SCROLLWIN_TOP )
386 {
387 lineFirstNew = 0;
388 }
389 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
390 {
391 lineFirstNew = m_lineMax;
392 }
393 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
394 {
395 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
396 }
397 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
398 {
399 lineFirstNew = m_lineFirst + 1;
400 }
401 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
402 {
403 lineFirstNew = FindFirstFromBottom(m_lineFirst);
404 }
405 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
406 {
407 lineFirstNew = GetVisibleEnd();
408 if ( lineFirstNew )
409 lineFirstNew--;
410 }
411 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
412 {
413 lineFirstNew = event.GetPosition();
414 }
415 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
416 {
417 lineFirstNew = event.GetPosition();
418 }
419
420 else // unknown scroll event?
421 {
422 wxFAIL_MSG( _T("unknown scroll event type?") );
423 return;
424 }
425
426 ScrollToLine(lineFirstNew);
427
428#ifdef __WXMAC__
429 Update();
430#endif // __WXMAC__
431}
432
433#if wxUSE_MOUSEWHEEL
434
435void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
436{
437 m_sumWheelRotation += event.GetWheelRotation();
438 int delta = event.GetWheelDelta();
439
440 // how much to scroll this time
441 int units_to_scroll = -(m_sumWheelRotation/delta);
442 if ( !units_to_scroll )
443 return;
444
445 m_sumWheelRotation += units_to_scroll*delta;
446
447 if ( !event.IsPageScroll() )
448 ScrollLines( units_to_scroll*event.GetLinesPerAction() );
449 else
450 // scroll pages instead of lines
451 ScrollPages( units_to_scroll );
452}
453
454#endif