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