added support for gcc precompiled headers
[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 END_EVENT_TABLE()
41
42
43 // ============================================================================
44 // implementation
45 // ============================================================================
46
47 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
48
49 // ----------------------------------------------------------------------------
50 // initialization
51 // ----------------------------------------------------------------------------
52
53 void wxVScrolledWindow::Init()
54 {
55 // we're initially empty
56 m_lineMax =
57 m_lineFirst = 0;
58
59 // this one should always be strictly positive
60 m_nVisible = 1;
61
62 m_heightTotal = 0;
63 }
64
65 // ----------------------------------------------------------------------------
66 // various helpers
67 // ----------------------------------------------------------------------------
68
69 wxCoord wxVScrolledWindow::EstimateTotalHeight() const
70 {
71 // estimate the total height: it is impossible to call
72 // OnGetLineHeight() for every line because there may be too many of
73 // them, so we just make a guess using some lines in the beginning,
74 // some in the end and some in the middle
75 static const size_t NUM_LINES_TO_SAMPLE = 10;
76
77 wxCoord heightTotal;
78 if ( m_lineMax < 3*NUM_LINES_TO_SAMPLE )
79 {
80 // in this case calculating exactly is faster and more correct than
81 // guessing
82 heightTotal = GetLinesHeight(0, m_lineMax);
83 }
84 else // too many lines to calculate exactly
85 {
86 // look at some lines in the beginning/middle/end
87 heightTotal =
88 GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
89 GetLinesHeight(m_lineMax - NUM_LINES_TO_SAMPLE, m_lineMax) +
90 GetLinesHeight(m_lineMax/2 - NUM_LINES_TO_SAMPLE/2,
91 m_lineMax/2 + NUM_LINES_TO_SAMPLE/2);
92
93 // use the height of the lines we looked as the average
94 heightTotal = (wxCoord)
95 (((float)m_heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
96 }
97
98 return heightTotal;
99 }
100
101 wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
102 {
103 if ( lineMin == lineMax )
104 return 0;
105 else if ( lineMin > lineMax )
106 return -GetLinesHeight(lineMax, lineMin);
107 //else: lineMin < lineMax
108
109 // let the user code know that we're going to need all these lines
110 OnGetLinesHint(lineMin, lineMax);
111
112 // do sum up their heights
113 wxCoord height = 0;
114 for ( size_t line = lineMin; line < lineMax; line++ )
115 {
116 height += OnGetLineHeight(line);
117 }
118
119 return height;
120 }
121
122 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
123 {
124 const wxCoord hWindow = GetClientSize().y;
125
126 // go upwards until we arrive at a line such that lineLast is not visible
127 // any more when it is shown
128 size_t lineFirst = lineLast;
129 wxCoord h = 0;
130 for ( ;; )
131 {
132 h += OnGetLineHeight(lineFirst);
133
134 if ( h > hWindow )
135 {
136 // for this line to be fully visible we need to go one line
137 // down, but if it is enough for it to be only partly visible then
138 // this line will do as well
139 if ( full )
140 {
141 lineFirst++;
142 }
143
144 break;
145 }
146
147 if ( !lineFirst )
148 break;
149
150 lineFirst--;
151 }
152
153 return lineFirst;
154 }
155
156 void wxVScrolledWindow::UpdateScrollbar()
157 {
158 // see how many lines can we fit on screen
159 const wxCoord hWindow = GetClientSize().y;
160
161 wxCoord h = 0;
162 size_t line;
163 for ( line = m_lineFirst; line < m_lineMax; line++ )
164 {
165 if ( h > hWindow )
166 break;
167
168 h += OnGetLineHeight(line);
169 }
170
171 m_nVisible = line - m_lineFirst;
172
173 int pageSize = m_nVisible;
174 if ( h > hWindow )
175 {
176 // last line is only partially visible, we still need the scrollbar and
177 // so we have to "fix" pageSize because if it is equal to m_lineMax the
178 // scrollbar is not shown at all under MSW
179 pageSize--;
180 }
181
182 // set the scrollbar parameters to reflect this
183 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
184 }
185
186 // ----------------------------------------------------------------------------
187 // operations
188 // ----------------------------------------------------------------------------
189
190 void wxVScrolledWindow::SetLineCount(size_t count)
191 {
192 // save the number of lines
193 m_lineMax = count;
194
195 // and our estimate for their total height
196 m_heightTotal = EstimateTotalHeight();
197
198 // recalculate the scrollbars parameters
199 m_lineFirst = 1; // make sure it is != 0
200 ScrollToLine(0);
201 }
202
203 void wxVScrolledWindow::RefreshLine(size_t line)
204 {
205 // is this line visible?
206 if ( !IsVisible(line) )
207 {
208 // no, it is useless to do anything
209 return;
210 }
211
212 // calculate the rect occupied by this line on screen
213 wxRect rect;
214 rect.width = GetClientSize().x;
215 rect.height = OnGetLineHeight(line);
216 for ( size_t n = GetFirstVisibleLine(); n < line; n++ )
217 {
218 rect.y += OnGetLineHeight(n);
219 }
220
221 // do refresh it
222 RefreshRect(rect);
223 }
224
225 void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
226 {
227 wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
228
229 // clump the range to just the visible lines -- it is useless to refresh
230 // the other ones
231 if ( from < GetFirstVisibleLine() )
232 from = GetFirstVisibleLine();
233
234 if ( to > GetLastVisibleLine() )
235 to = GetLastVisibleLine();
236
237 // calculate the rect occupied by these lines on screen
238 wxRect rect;
239 rect.width = GetClientSize().x;
240 for ( size_t nBefore = GetFirstVisibleLine(); nBefore < from; nBefore++ )
241 {
242 rect.y += OnGetLineHeight(nBefore);
243 }
244
245 for ( size_t nBetween = from; nBetween <= to; nBetween++ )
246 {
247 rect.height += OnGetLineHeight(nBetween);
248 }
249
250 // do refresh it
251 RefreshRect(rect);
252 }
253
254 void wxVScrolledWindow::RefreshAll()
255 {
256 UpdateScrollbar();
257
258 Refresh();
259 }
260
261 int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
262 {
263 const size_t lineMax = GetLastVisibleLine();
264 for ( size_t line = GetFirstVisibleLine(); line <= lineMax; line++ )
265 {
266 y -= OnGetLineHeight(line);
267 if ( y < 0 )
268 return line;
269 }
270
271 return wxNOT_FOUND;
272 }
273
274 // ----------------------------------------------------------------------------
275 // scrolling
276 // ----------------------------------------------------------------------------
277
278 bool wxVScrolledWindow::ScrollToLine(size_t line)
279 {
280 if ( !m_lineMax )
281 {
282 // we're empty, code below doesn't make sense in this case
283 return false;
284 }
285
286 // determine the real first line to scroll to: we shouldn't scroll beyond
287 // the end
288 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
289 if ( line > lineFirstLast )
290 line = lineFirstLast;
291
292 // anything to do?
293 if ( line == m_lineFirst )
294 {
295 // no
296 return false;
297 }
298
299
300 // remember the currently shown lines for the refresh code below
301 size_t lineFirstOld = GetFirstVisibleLine(),
302 lineLastOld = GetLastVisibleLine();
303
304 m_lineFirst = line;
305
306
307 // the size of scrollbar thumb could have changed
308 UpdateScrollbar();
309
310
311 // finally refresh the display -- but only redraw as few lines as possible
312 // to avoid flicker
313 if ( GetFirstVisibleLine() > lineLastOld ||
314 GetLastVisibleLine() < lineFirstOld )
315 {
316 // the simplest case: we don't have any old lines left, just redraw
317 // everything
318 Refresh();
319 }
320 else // overlap between the lines we showed before and should show now
321 {
322 ScrollWindow(0, GetLinesHeight(GetFirstVisibleLine(), lineFirstOld));
323 }
324
325 return true;
326 }
327
328 bool wxVScrolledWindow::ScrollLines(int lines)
329 {
330 lines += m_lineFirst;
331 if ( lines < 0 )
332 lines = 0;
333
334 return ScrollToLine(lines);
335 }
336
337 bool wxVScrolledWindow::ScrollPages(int pages)
338 {
339 bool didSomething = false;
340
341 while ( pages )
342 {
343 int line;
344 if ( pages > 0 )
345 {
346 line = GetLastVisibleLine();
347 pages--;
348 }
349 else // pages < 0
350 {
351 line = FindFirstFromBottom(GetFirstVisibleLine());
352 pages++;
353 }
354
355 didSomething = ScrollToLine(line);
356 }
357
358 return didSomething;
359 }
360
361 // ----------------------------------------------------------------------------
362 // event handling
363 // ----------------------------------------------------------------------------
364
365 void wxVScrolledWindow::OnSize(wxSizeEvent& event)
366 {
367 UpdateScrollbar();
368
369 event.Skip();
370 }
371
372 void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
373 {
374 size_t lineFirstNew;
375
376 const wxEventType evtType = event.GetEventType();
377 if ( evtType == wxEVT_SCROLLWIN_TOP )
378 {
379 lineFirstNew = 0;
380 }
381 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
382 {
383 lineFirstNew = m_lineMax;
384 }
385 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
386 {
387 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
388 }
389 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
390 {
391 lineFirstNew = m_lineFirst + 1;
392 }
393 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
394 {
395 lineFirstNew = FindFirstFromBottom(m_lineFirst);
396 }
397 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
398 {
399 lineFirstNew = GetLastVisibleLine();
400 }
401 else // unknown scroll event?
402 {
403 if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
404 {
405 lineFirstNew = event.GetPosition();
406 }
407 else
408 {
409 wxASSERT_MSG( evtType == wxEVT_SCROLLWIN_THUMBTRACK,
410 _T("unknown scroll event type?") );
411
412 // don't do anything, otherwise dragging the thumb around would
413 // be too slow
414 return;
415 }
416 }
417
418 ScrollToLine(lineFirstNew);
419
420 #ifdef __WXMAC__
421 Update();
422 #endif // __WXMAC__
423 }
424