]> git.saurik.com Git - wxWidgets.git/blame - src/generic/vscroll.cpp
concentrating content and structure region calculations
[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
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
687dcff3
VS
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
cf7d6329
VZ
31#include "wx/vscroll.h"
32
33// ----------------------------------------------------------------------------
34// event tables
35// ----------------------------------------------------------------------------
36
37BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
38 EVT_SIZE(wxVScrolledWindow::OnSize)
39 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
40END_EVENT_TABLE()
41
42
43// ============================================================================
44// implementation
45// ============================================================================
46
0c8392ca
RD
47IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
48
cf7d6329
VZ
49// ----------------------------------------------------------------------------
50// initialization
51// ----------------------------------------------------------------------------
52
53void 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
1e0af0bc
VZ
69wxCoord 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)
999836aa 95 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
1e0af0bc
VZ
96 }
97
98 return heightTotal;
99}
100
cf7d6329
VZ
101wxCoord 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
0b49ccf8 122size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
cf7d6329
VZ
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 {
0b49ccf8
VZ
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 }
cf7d6329
VZ
143
144 break;
145 }
146
147 if ( !lineFirst )
148 break;
149
150 lineFirst--;
151 }
152
153 return lineFirst;
154}
155
156void 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
190void wxVScrolledWindow::SetLineCount(size_t count)
191{
192 // save the number of lines
193 m_lineMax = count;
194
1e0af0bc
VZ
195 // and our estimate for their total height
196 m_heightTotal = EstimateTotalHeight();
cf7d6329
VZ
197
198 // recalculate the scrollbars parameters
e0c6027b 199 m_lineFirst = 1; // make sure it is != 0
cf7d6329
VZ
200 ScrollToLine(0);
201}
202
e0c6027b
VZ
203void 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 }
ae0f0223
VZ
220
221 // do refresh it
222 RefreshRect(rect);
223}
224
225void 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
33a03749 245 for ( size_t nBetween = from; nBetween <= to; nBetween++ )
ae0f0223
VZ
246 {
247 rect.height += OnGetLineHeight(nBetween);
248 }
e0c6027b
VZ
249
250 // do refresh it
251 RefreshRect(rect);
252}
253
8b053348
VZ
254void wxVScrolledWindow::RefreshAll()
255{
256 UpdateScrollbar();
257
258 Refresh();
259}
260
e0c6027b
VZ
261int 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
cf7d6329
VZ
274// ----------------------------------------------------------------------------
275// scrolling
276// ----------------------------------------------------------------------------
277
278bool 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
0b49ccf8 288 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
cf7d6329
VZ
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
328bool wxVScrolledWindow::ScrollLines(int lines)
329{
330 lines += m_lineFirst;
331 if ( lines < 0 )
332 lines = 0;
333
334 return ScrollToLine(lines);
335}
336
337bool 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
365void wxVScrolledWindow::OnSize(wxSizeEvent& event)
366{
367 UpdateScrollbar();
368
369 event.Skip();
370}
371
372void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
373{
374 size_t lineFirstNew;
375
376 const wxEventType evtType = event.GetEventType();
5d2ad055 377
cf7d6329
VZ
378 if ( evtType == wxEVT_SCROLLWIN_TOP )
379 {
380 lineFirstNew = 0;
381 }
382 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
383 {
384 lineFirstNew = m_lineMax;
385 }
386 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
387 {
388 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
389 }
390 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
391 {
392 lineFirstNew = m_lineFirst + 1;
393 }
394 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
395 {
396 lineFirstNew = FindFirstFromBottom(m_lineFirst);
397 }
398 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
399 {
400 lineFirstNew = GetLastVisibleLine();
401 }
5d2ad055
RD
402 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
403 {
404 lineFirstNew = event.GetPosition();
405 }
406 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
407 {
408 lineFirstNew = event.GetPosition();
409 }
410
cf7d6329
VZ
411 else // unknown scroll event?
412 {
5d2ad055
RD
413 wxFAIL_MSG( _T("unknown scroll event type?") );
414 return;
cf7d6329
VZ
415 }
416
417 ScrollToLine(lineFirstNew);
b544a278
VZ
418
419#ifdef __WXMAC__
420 Update();
421#endif // __WXMAC__
cf7d6329
VZ
422}
423