]> git.saurik.com Git - wxWidgets.git/blob - src/generic/vscroll.cpp
Applied patch [ 1116706 ] wxRendererGeneric::DrawComboBoxDropButton position fix
[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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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
31 #include "wx/vscroll.h"
32
33 // ----------------------------------------------------------------------------
34 // event tables
35 // ----------------------------------------------------------------------------
36
37 BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
38 EVT_SIZE(wxVScrolledWindow::OnSize)
39 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
40 #if wxUSE_MOUSEWHEEL
41 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
42 #endif
43 END_EVENT_TABLE()
44
45
46 // ============================================================================
47 // implementation
48 // ============================================================================
49
50 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
51
52 // ----------------------------------------------------------------------------
53 // initialization
54 // ----------------------------------------------------------------------------
55
56 void 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;
66
67 #if wxUSE_MOUSEWHEEL
68 m_sumWheelRotation = 0;
69 #endif
70 }
71
72 // ----------------------------------------------------------------------------
73 // various helpers
74 // ----------------------------------------------------------------------------
75
76 wxCoord 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)
102 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
103 }
104
105 return heightTotal;
106 }
107
108 wxCoord 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
129 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
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 {
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 }
150
151 break;
152 }
153
154 if ( !lineFirst )
155 break;
156
157 lineFirst--;
158 }
159
160 return lineFirst;
161 }
162
163 void 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
197 void wxVScrolledWindow::SetLineCount(size_t count)
198 {
199 // save the number of lines
200 m_lineMax = count;
201
202 // and our estimate for their total height
203 m_heightTotal = EstimateTotalHeight();
204
205 // recalculate the scrollbars parameters
206 m_lineFirst = 1; // make sure it is != 0
207 ScrollToLine(0);
208 }
209
210 void 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);
223 for ( size_t n = GetFirstVisibleLine(); n < line; n++ )
224 {
225 rect.y += OnGetLineHeight(n);
226 }
227
228 // do refresh it
229 RefreshRect(rect);
230 }
231
232 void 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
238 if ( from < GetFirstVisibleLine() )
239 from = GetFirstVisibleLine();
240
241 if ( to > GetLastVisibleLine() )
242 to = GetLastVisibleLine();
243
244 // calculate the rect occupied by these lines on screen
245 wxRect rect;
246 rect.width = GetClientSize().x;
247 for ( size_t nBefore = GetFirstVisibleLine(); nBefore < from; nBefore++ )
248 {
249 rect.y += OnGetLineHeight(nBefore);
250 }
251
252 for ( size_t nBetween = from; nBetween <= to; nBetween++ )
253 {
254 rect.height += OnGetLineHeight(nBetween);
255 }
256
257 // do refresh it
258 RefreshRect(rect);
259 }
260
261 void wxVScrolledWindow::RefreshAll()
262 {
263 UpdateScrollbar();
264
265 Refresh();
266 }
267
268 int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
269 {
270 const size_t lineMax = GetLastVisibleLine();
271 for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ )
272 {
273 y -= OnGetLineHeight(line);
274 if ( y < 0 )
275 return line;
276 }
277
278 return wxNOT_FOUND;
279 }
280
281 // ----------------------------------------------------------------------------
282 // scrolling
283 // ----------------------------------------------------------------------------
284
285 bool wxVScrolledWindow::ScrollToLine(size_t line)
286 {
287 if ( !m_lineMax )
288 {
289 // we're empty, code below doesn't make sense in this case
290 return false;
291 }
292
293 // determine the real first line to scroll to: we shouldn't scroll beyond
294 // the end
295 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
296 if ( line > lineFirstLast )
297 line = lineFirstLast;
298
299 // anything to do?
300 if ( line == m_lineFirst )
301 {
302 // no
303 return false;
304 }
305
306
307 // remember the currently shown lines for the refresh code below
308 size_t lineFirstOld = GetFirstVisibleLine(),
309 lineLastOld = GetLastVisibleLine();
310
311 m_lineFirst = line;
312
313
314 // the size of scrollbar thumb could have changed
315 UpdateScrollbar();
316
317
318 // finally refresh the display -- but only redraw as few lines as possible
319 // to avoid flicker
320 if ( GetFirstVisibleLine() > lineLastOld ||
321 GetLastVisibleLine() < lineFirstOld )
322 {
323 // the simplest case: we don't have any old lines left, just redraw
324 // everything
325 Refresh();
326 }
327 else // overlap between the lines we showed before and should show now
328 {
329 ScrollWindow(0, GetLinesHeight(GetFirstVisibleLine(), lineFirstOld));
330 }
331
332 return true;
333 }
334
335 bool wxVScrolledWindow::ScrollLines(int lines)
336 {
337 lines += m_lineFirst;
338 if ( lines < 0 )
339 lines = 0;
340
341 return ScrollToLine(lines);
342 }
343
344 bool wxVScrolledWindow::ScrollPages(int pages)
345 {
346 bool didSomething = false;
347
348 while ( pages )
349 {
350 int line;
351 if ( pages > 0 )
352 {
353 line = GetLastVisibleLine();
354 pages--;
355 }
356 else // pages < 0
357 {
358 line = FindFirstFromBottom(GetFirstVisibleLine());
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 = GetLastVisibleLine();
408 }
409 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
410 {
411 lineFirstNew = event.GetPosition();
412 }
413 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
414 {
415 lineFirstNew = event.GetPosition();
416 }
417
418 else // unknown scroll event?
419 {
420 wxFAIL_MSG( _T("unknown scroll event type?") );
421 return;
422 }
423
424 ScrollToLine(lineFirstNew);
425
426 #ifdef __WXMAC__
427 Update();
428 #endif // __WXMAC__
429 }
430
431 #if wxUSE_MOUSEWHEEL
432
433 void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
434 {
435 m_sumWheelRotation += event.GetWheelRotation();
436 int delta = event.GetWheelDelta();
437
438 // how much to scroll this time
439 int units_to_scroll = -(m_sumWheelRotation/delta);
440 if ( !units_to_scroll )
441 return;
442
443 m_sumWheelRotation += units_to_scroll*delta;
444
445 if ( !event.IsPageScroll() )
446 ScrollLines( units_to_scroll*event.GetLinesPerAction() );
447 else
448 // scroll pages instead of lines
449 ScrollPages( units_to_scroll );
450 }
451
452 #endif