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