reverted wxHVScrolledWindow patch (rev 1.19/1.21 of vscroll.h/.cpp) because not only...
[wxWidgets.git] / src / generic / vscroll.cpp
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
33 BEGIN_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
39 END_EVENT_TABLE()
40
41
42 // ============================================================================
43 // implementation
44 // ============================================================================
45
46 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
47
48 // ----------------------------------------------------------------------------
49 // initialization
50 // ----------------------------------------------------------------------------
51
52 void 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
72 wxCoord 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
104 wxCoord 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
125 size_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
159 void 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
193 void 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
206 void 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
228 void 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
259 void wxVScrolledWindow::RefreshAll()
260 {
261 UpdateScrollbar();
262
263 Refresh();
264 }
265
266 int 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
283 bool 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
333 bool wxVScrolledWindow::ScrollLines(int lines)
334 {
335 lines += m_lineFirst;
336 if ( lines < 0 )
337 lines = 0;
338
339 return ScrollToLine(lines);
340 }
341
342 bool 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
372 void wxVScrolledWindow::OnSize(wxSizeEvent& event)
373 {
374 UpdateScrollbar();
375
376 event.Skip();
377 }
378
379 void 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
435 void 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 // wxUSE_MOUSEWHEEL
455