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