]> git.saurik.com Git - wxWidgets.git/blob - src/generic/vscroll.cpp
added wxVScrolledWindow
[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 #include "wx/vscroll.h"
21
22 // ----------------------------------------------------------------------------
23 // event tables
24 // ----------------------------------------------------------------------------
25
26 BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
27 EVT_SIZE(wxVScrolledWindow::OnSize)
28 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
29 END_EVENT_TABLE()
30
31
32 // ============================================================================
33 // implementation
34 // ============================================================================
35
36 // ----------------------------------------------------------------------------
37 // initialization
38 // ----------------------------------------------------------------------------
39
40 void 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
56 wxCoord 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
77 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast)
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 {
91 lineFirst++;
92
93 break;
94 }
95
96 if ( !lineFirst )
97 break;
98
99 lineFirst--;
100 }
101
102 return lineFirst;
103 }
104
105 void wxVScrolledWindow::UpdateScrollbar()
106 {
107 // see how many lines can we fit on screen
108 const wxCoord hWindow = GetClientSize().y;
109
110 wxCoord h = 0;
111 size_t line;
112 for ( line = m_lineFirst; line < m_lineMax; line++ )
113 {
114 if ( h > hWindow )
115 break;
116
117 h += OnGetLineHeight(line);
118 }
119
120 m_nVisible = line - m_lineFirst;
121
122 int pageSize = m_nVisible;
123 if ( h > hWindow )
124 {
125 // last line is only partially visible, we still need the scrollbar and
126 // so we have to "fix" pageSize because if it is equal to m_lineMax the
127 // scrollbar is not shown at all under MSW
128 pageSize--;
129 }
130
131 // set the scrollbar parameters to reflect this
132 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
133 }
134
135 // ----------------------------------------------------------------------------
136 // operations
137 // ----------------------------------------------------------------------------
138
139 void wxVScrolledWindow::SetLineCount(size_t count)
140 {
141 // save the number of lines
142 m_lineMax = count;
143
144
145 // estimate the total height: it is impossible to call
146 // OnGetLineHeight() for every line because there may be too many of
147 // them, so we just make a guess using some lines in the beginning,
148 // some in the end and some in the middle
149 static const size_t NUM_LINES_TO_SAMPLE = 10;
150
151 if ( count < 3*NUM_LINES_TO_SAMPLE )
152 {
153 // in this case calculating exactly is faster and more correct than
154 // guessing
155 m_heightTotal = GetLinesHeight(0, m_lineMax);
156 }
157 else // too many lines to calculate exactly
158 {
159 // look at some lines in the beginning/middle/end
160 m_heightTotal =
161 GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
162 GetLinesHeight(count - NUM_LINES_TO_SAMPLE, count) +
163 GetLinesHeight(count/2 - NUM_LINES_TO_SAMPLE/2,
164 count/2 + NUM_LINES_TO_SAMPLE/2);
165
166 // use the height of the lines we looked as the average
167 m_heightTotal = ((float)m_heightTotal / (3*NUM_LINES_TO_SAMPLE)) *
168 m_lineMax;
169 }
170
171
172 // recalculate the scrollbars parameters
173 ScrollToLine(0);
174 }
175
176 // ----------------------------------------------------------------------------
177 // scrolling
178 // ----------------------------------------------------------------------------
179
180 bool wxVScrolledWindow::ScrollToLine(size_t line)
181 {
182 if ( !m_lineMax )
183 {
184 // we're empty, code below doesn't make sense in this case
185 return false;
186 }
187
188 // determine the real first line to scroll to: we shouldn't scroll beyond
189 // the end
190 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1);
191 if ( line > lineFirstLast )
192 line = lineFirstLast;
193
194 // anything to do?
195 if ( line == m_lineFirst )
196 {
197 // no
198 return false;
199 }
200
201
202 // remember the currently shown lines for the refresh code below
203 size_t lineFirstOld = GetFirstVisibleLine(),
204 lineLastOld = GetLastVisibleLine();
205
206 m_lineFirst = line;
207
208
209 // the size of scrollbar thumb could have changed
210 UpdateScrollbar();
211
212
213 // finally refresh the display -- but only redraw as few lines as possible
214 // to avoid flicker
215 if ( GetFirstVisibleLine() > lineLastOld ||
216 GetLastVisibleLine() < lineFirstOld )
217 {
218 // the simplest case: we don't have any old lines left, just redraw
219 // everything
220 Refresh();
221 }
222 else // overlap between the lines we showed before and should show now
223 {
224 ScrollWindow(0, GetLinesHeight(GetFirstVisibleLine(), lineFirstOld));
225 }
226
227 return true;
228 }
229
230 bool wxVScrolledWindow::ScrollLines(int lines)
231 {
232 lines += m_lineFirst;
233 if ( lines < 0 )
234 lines = 0;
235
236 return ScrollToLine(lines);
237 }
238
239 bool wxVScrolledWindow::ScrollPages(int pages)
240 {
241 bool didSomething = false;
242
243 while ( pages )
244 {
245 int line;
246 if ( pages > 0 )
247 {
248 line = GetLastVisibleLine();
249 pages--;
250 }
251 else // pages < 0
252 {
253 line = FindFirstFromBottom(GetFirstVisibleLine());
254 pages++;
255 }
256
257 didSomething = ScrollToLine(line);
258 }
259
260 return didSomething;
261 }
262
263 // ----------------------------------------------------------------------------
264 // event handling
265 // ----------------------------------------------------------------------------
266
267 void wxVScrolledWindow::OnSize(wxSizeEvent& event)
268 {
269 UpdateScrollbar();
270
271 event.Skip();
272 }
273
274 void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
275 {
276 size_t lineFirstNew;
277
278 const wxEventType evtType = event.GetEventType();
279 if ( evtType == wxEVT_SCROLLWIN_TOP )
280 {
281 lineFirstNew = 0;
282 }
283 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
284 {
285 lineFirstNew = m_lineMax;
286 }
287 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
288 {
289 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
290 }
291 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
292 {
293 lineFirstNew = m_lineFirst + 1;
294 }
295 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
296 {
297 lineFirstNew = FindFirstFromBottom(m_lineFirst);
298 }
299 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
300 {
301 lineFirstNew = GetLastVisibleLine();
302 }
303 else // unknown scroll event?
304 {
305 if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
306 {
307 lineFirstNew = event.GetPosition();
308 }
309 else
310 {
311 wxASSERT_MSG( evtType == wxEVT_SCROLLWIN_THUMBTRACK,
312 _T("unknown scroll event type?") );
313
314 // don't do anything, otherwise dragging the thumb around would
315 // be too slow
316 return;
317 }
318 }
319
320 ScrollToLine(lineFirstNew);
321 }
322