Applied wxVScrolledWindow 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: Brad Anderson
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 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/vscroll.h"
28 #include "wx/sizer.h"
29 #include "wx/dc.h"
30
31 // ----------------------------------------------------------------------------
32 // event tables
33 // ----------------------------------------------------------------------------
34
35 BEGIN_EVENT_TABLE(wxVScrolledWindow, wxPanel)
36 EVT_SIZE(wxVScrolledWindow::OnSize)
37 EVT_SCROLLWIN(wxVScrolledWindow::OnScroll)
38 #if wxUSE_MOUSEWHEEL
39 EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel)
40 #endif
41 END_EVENT_TABLE()
42
43
44 // ============================================================================
45 // implementation
46 // ============================================================================
47
48 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel)
49
50 // ----------------------------------------------------------------------------
51 // initialization
52 // ----------------------------------------------------------------------------
53
54 void wxVScrolledWindow::Init()
55 {
56 // we're initially empty
57 m_lineMax =
58 m_lineFirst = 0;
59
60 // this one should always be strictly positive
61 m_nVisible = 1;
62
63 m_heightTotal = 0;
64
65 #if wxUSE_MOUSEWHEEL
66 m_sumWheelRotation = 0;
67 #endif
68 }
69
70 // ----------------------------------------------------------------------------
71 // various helpers
72 // ----------------------------------------------------------------------------
73
74 wxCoord wxVScrolledWindow::EstimateTotalHeight() const
75 {
76 // estimate the total height: it is impossible to call
77 // OnGetLineHeight() for every line because there may be too many of
78 // them, so we just make a guess using some lines in the beginning,
79 // some in the end and some in the middle
80 static const size_t NUM_LINES_TO_SAMPLE = 10;
81
82 wxCoord heightTotal;
83 if ( m_lineMax < 3*NUM_LINES_TO_SAMPLE )
84 {
85 // in this case calculating exactly is faster and more correct than
86 // guessing
87 heightTotal = GetLinesHeight(0, m_lineMax);
88 }
89 else // too many lines to calculate exactly
90 {
91 // look at some lines in the beginning/middle/end
92 heightTotal =
93 GetLinesHeight(0, NUM_LINES_TO_SAMPLE) +
94 GetLinesHeight(m_lineMax - NUM_LINES_TO_SAMPLE, m_lineMax) +
95 GetLinesHeight(m_lineMax/2 - NUM_LINES_TO_SAMPLE/2,
96 m_lineMax/2 + NUM_LINES_TO_SAMPLE/2);
97
98 // use the height of the lines we looked as the average
99 heightTotal = (wxCoord)
100 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_lineMax);
101 }
102
103 return heightTotal;
104 }
105
106 wxCoord wxVScrolledWindow::GetLinesHeight(size_t lineMin, size_t lineMax) const
107 {
108 if ( lineMin == lineMax )
109 return 0;
110 else if ( lineMin > lineMax )
111 return -GetLinesHeight(lineMax, lineMin);
112 //else: lineMin < lineMax
113
114 // let the user code know that we're going to need all these lines
115 OnGetLinesHint(lineMin, lineMax);
116
117 // do sum up their heights
118 wxCoord height = 0;
119 for ( size_t line = lineMin; line < lineMax; line++ )
120 {
121 height += OnGetLineHeight(line);
122 }
123
124 return height;
125 }
126
127 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast, bool full)
128 {
129 const wxCoord hWindow = GetClientSize().y;
130
131 // go upwards until we arrive at a line such that lineLast is not visible
132 // any more when it is shown
133 size_t lineFirst = lineLast;
134 wxCoord h = 0;
135 for ( ;; )
136 {
137 h += OnGetLineHeight(lineFirst);
138
139 if ( h > hWindow )
140 {
141 // for this line to be fully visible we need to go one line
142 // down, but if it is enough for it to be only partly visible then
143 // this line will do as well
144 if ( full )
145 {
146 lineFirst++;
147 }
148
149 break;
150 }
151
152 if ( !lineFirst )
153 break;
154
155 lineFirst--;
156 }
157
158 return lineFirst;
159 }
160
161 void wxVScrolledWindow::UpdateScrollbar()
162 {
163 // see how many lines can we fit on screen
164 const wxCoord hWindow = GetClientSize().y;
165
166 wxCoord h = 0;
167 size_t line;
168 for ( line = m_lineFirst; line < m_lineMax; line++ )
169 {
170 if ( h > hWindow )
171 break;
172
173 h += OnGetLineHeight(line);
174 }
175
176 m_nVisible = line - m_lineFirst;
177
178 int pageSize = m_nVisible;
179 if ( h > hWindow )
180 {
181 // last line is only partially visible, we still need the scrollbar and
182 // so we have to "fix" pageSize because if it is equal to m_lineMax the
183 // scrollbar is not shown at all under MSW
184 pageSize--;
185 }
186
187 // set the scrollbar parameters to reflect this
188 SetScrollbar(wxVERTICAL, m_lineFirst, pageSize, m_lineMax);
189 }
190
191 // ----------------------------------------------------------------------------
192 // operations
193 // ----------------------------------------------------------------------------
194
195 void wxVScrolledWindow::SetLineCount(size_t count)
196 {
197 // save the number of lines
198 m_lineMax = count;
199
200 // and our estimate for their total height
201 m_heightTotal = EstimateTotalHeight();
202
203 // recalculate the scrollbars parameters
204 m_lineFirst = 1; // make sure it is != 0
205 ScrollToLine(0);
206 }
207
208 void wxVScrolledWindow::RefreshLine(size_t line)
209 {
210 // is this line visible?
211 if ( !IsVisible(line) )
212 {
213 // no, it is useless to do anything
214 return;
215 }
216
217 // calculate the rect occupied by this line on screen
218 wxRect rect;
219 rect.width = GetClientSize().x;
220 rect.height = OnGetLineHeight(line);
221 for ( size_t n = GetVisibleBegin(); n < line; n++ )
222 {
223 rect.y += OnGetLineHeight(n);
224 }
225
226 // do refresh it
227 RefreshRect(rect);
228 }
229
230 void wxVScrolledWindow::RefreshLines(size_t from, size_t to)
231 {
232 wxASSERT_MSG( from <= to, _T("RefreshLines(): empty range") );
233
234 // clump the range to just the visible lines -- it is useless to refresh
235 // the other ones
236 if ( from < GetVisibleBegin() )
237 from = GetVisibleBegin();
238
239 if ( to >= GetVisibleEnd() )
240 to = GetVisibleEnd();
241 else
242 to++;
243
244 // calculate the rect occupied by these lines on screen
245 wxRect rect;
246 rect.width = GetClientSize().x;
247 for ( size_t nBefore = GetVisibleBegin(); nBefore < from; nBefore++ )
248 {
249 rect.y += OnGetLineHeight(nBefore);
250 }
251
252 for ( size_t nBetween = from; nBetween < to; nBetween++ )
253 {
254 rect.height += OnGetLineHeight(nBetween);
255 }
256
257 // do refresh it
258 RefreshRect(rect);
259 }
260
261 void wxVScrolledWindow::RefreshAll()
262 {
263 UpdateScrollbar();
264
265 Refresh();
266 }
267
268 bool wxVScrolledWindow::Layout()
269 {
270 if(GetSizer())
271 {
272 // adjust the sizer dimensions/position taking into account the
273 // virtual size and scrolled position of the window.
274
275 int y, w, h; // x is always 0 so no variable needed
276
277 y = -GetLinesHeight(0, GetFirstVisibleLine());
278 GetVirtualSize(&w, &h);
279 GetSizer()->SetDimension(0, y, w, h);
280 return true;
281 }
282
283 // fall back to default for LayoutConstraints
284 return wxPanel::Layout();
285 }
286
287 int wxVScrolledWindow::HitTest(wxCoord WXUNUSED(x), wxCoord y) const
288 {
289 const size_t lineMax = GetVisibleEnd();
290 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
291 {
292 y -= OnGetLineHeight(line);
293 if ( y < 0 )
294 return line;
295 }
296
297 return wxNOT_FOUND;
298 }
299
300 // ----------------------------------------------------------------------------
301 // scrolling
302 // ----------------------------------------------------------------------------
303
304 bool wxVScrolledWindow::ScrollToLine(size_t line)
305 {
306 if ( !m_lineMax )
307 {
308 // we're empty, code below doesn't make sense in this case
309 return false;
310 }
311
312 // determine the real first line to scroll to: we shouldn't scroll beyond
313 // the end
314 size_t lineFirstLast = FindFirstFromBottom(m_lineMax - 1, true);
315 if ( line > lineFirstLast )
316 line = lineFirstLast;
317
318 // anything to do?
319 if ( line == m_lineFirst )
320 {
321 // no
322 return false;
323 }
324
325
326 // remember the currently shown lines for the refresh code below
327 size_t lineFirstOld = GetVisibleBegin();
328
329 m_lineFirst = line;
330
331
332 // the size of scrollbar thumb could have changed
333 UpdateScrollbar();
334
335 // finally, scroll the actual window
336 ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld));
337
338 return true;
339 }
340
341 bool wxVScrolledWindow::ScrollLines(int lines)
342 {
343 lines += m_lineFirst;
344 if ( lines < 0 )
345 lines = 0;
346
347 return ScrollToLine(lines);
348 }
349
350 bool wxVScrolledWindow::ScrollPages(int pages)
351 {
352 bool didSomething = false;
353
354 while ( pages )
355 {
356 int line;
357 if ( pages > 0 )
358 {
359 line = GetVisibleEnd();
360 if ( line )
361 line--;
362 pages--;
363 }
364 else // pages < 0
365 {
366 line = FindFirstFromBottom(GetVisibleBegin());
367 pages++;
368 }
369
370 didSomething = ScrollToLine(line);
371 }
372
373 return didSomething;
374 }
375
376 // ----------------------------------------------------------------------------
377 // event handling
378 // ----------------------------------------------------------------------------
379
380 void wxVScrolledWindow::OnSize(wxSizeEvent& event)
381 {
382 UpdateScrollbar();
383
384 event.Skip();
385 }
386
387 void wxVScrolledWindow::OnScroll(wxScrollWinEvent& event)
388 {
389 size_t lineFirstNew;
390
391 const wxEventType evtType = event.GetEventType();
392
393 if ( evtType == wxEVT_SCROLLWIN_TOP )
394 {
395 lineFirstNew = 0;
396 }
397 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
398 {
399 lineFirstNew = m_lineMax;
400 }
401 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
402 {
403 lineFirstNew = m_lineFirst ? m_lineFirst - 1 : 0;
404 }
405 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
406 {
407 lineFirstNew = m_lineFirst + 1;
408 }
409 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
410 {
411 lineFirstNew = FindFirstFromBottom(m_lineFirst);
412 }
413 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
414 {
415 lineFirstNew = GetVisibleEnd();
416 if ( lineFirstNew )
417 lineFirstNew--;
418 }
419 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
420 {
421 lineFirstNew = event.GetPosition();
422 }
423 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
424 {
425 lineFirstNew = event.GetPosition();
426 }
427
428 else // unknown scroll event?
429 {
430 wxFAIL_MSG( _T("unknown scroll event type?") );
431 return;
432 }
433
434 ScrollToLine(lineFirstNew);
435
436 #ifdef __WXMAC__
437 Update();
438 #endif // __WXMAC__
439 }
440
441 #if wxUSE_MOUSEWHEEL
442
443 void wxVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
444 {
445 m_sumWheelRotation += event.GetWheelRotation();
446 int delta = event.GetWheelDelta();
447
448 // how much to scroll this time
449 int units_to_scroll = -(m_sumWheelRotation/delta);
450 if ( !units_to_scroll )
451 return;
452
453 m_sumWheelRotation += units_to_scroll*delta;
454
455 if ( !event.IsPageScroll() )
456 ScrollLines( units_to_scroll*event.GetLinesPerAction() );
457 else
458 // scroll pages instead of lines
459 ScrollPages( units_to_scroll );
460 }
461
462 #endif
463
464
465
466
467 // ----------------------------------------------------------------------------
468 // wxVarScrolled Window event tables
469 // ----------------------------------------------------------------------------
470
471 BEGIN_EVENT_TABLE(wxHVScrolledWindow, wxPanel)
472 EVT_SIZE(wxHVScrolledWindow::OnSize)
473 EVT_SCROLLWIN(wxHVScrolledWindow::OnScroll)
474 #if wxUSE_MOUSEWHEEL
475 EVT_MOUSEWHEEL(wxHVScrolledWindow::OnMouseWheel)
476 #endif
477 END_EVENT_TABLE()
478
479
480 // ============================================================================
481 // wxVarScrolled implementation
482 // ============================================================================
483
484 IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel)
485
486 // ----------------------------------------------------------------------------
487 // initialization
488 // ----------------------------------------------------------------------------
489
490 void wxHVScrolledWindow::Init()
491 {
492 // we're initially empty
493 m_rowsMax =
494 m_columnsMax =
495 m_rowsFirst =
496 m_columnsFirst = 0;
497
498 // these should always be strictly positive
499 m_nRowsVisible =
500 m_nColumnsVisible = 1;
501
502 m_widthTotal =
503 m_heightTotal = 0;
504
505 m_physicalScrolling = true;
506
507 #if wxUSE_MOUSEWHEEL
508 m_sumWheelRotation = 0;
509 #endif
510 }
511
512 // ----------------------------------------------------------------------------
513 // various helpers
514 // ----------------------------------------------------------------------------
515
516 wxCoord wxHVScrolledWindow::EstimateTotalHeight() const
517 {
518 // estimate the total height: it is impossible to call
519 // OnGetLineHeight() for every line because there may be too many of
520 // them, so we just make a guess using some lines in the beginning,
521 // some in the end and some in the middle
522 static const size_t NUM_LINES_TO_SAMPLE = 10;
523
524 wxCoord heightTotal;
525 if ( m_rowsMax < 3*NUM_LINES_TO_SAMPLE )
526 {
527 // in this case calculating exactly is faster and more correct than
528 // guessing
529 heightTotal = GetRowsHeight(0, m_rowsMax);
530 }
531 else // too many lines to calculate exactly
532 {
533 // look at some lines in the beginning/middle/end
534 heightTotal =
535 GetRowsHeight(0, NUM_LINES_TO_SAMPLE) +
536 GetRowsHeight(m_rowsMax - NUM_LINES_TO_SAMPLE,
537 m_rowsMax) +
538 GetRowsHeight(m_rowsMax/2 - NUM_LINES_TO_SAMPLE/2,
539 m_rowsMax/2 + NUM_LINES_TO_SAMPLE/2);
540
541 // use the height of the lines we looked as the average
542 heightTotal = (wxCoord)
543 (((float)heightTotal / (3*NUM_LINES_TO_SAMPLE)) * m_rowsMax);
544 }
545
546 return heightTotal;
547 }
548
549 wxCoord wxHVScrolledWindow::EstimateTotalWidth() const
550 {
551 // estimate the total width: it is impossible to call
552 // OnGetLineWidth() for every line because there may be too many of
553 // them, so we just make a guess using some lines in the beginning,
554 // some in the end and some in the middle
555 static const size_t NUM_LINES_TO_SAMPLE = 10;
556
557 wxCoord widthTotal;
558 if ( m_columnsMax < 3*NUM_LINES_TO_SAMPLE )
559 {
560 // in this case calculating exactly is faster and more correct than
561 // guessing
562 widthTotal = GetColumnsWidth(0, m_columnsMax);
563 }
564 else // too many lines to calculate exactly
565 {
566 // look at some lines in the beginning/middle/end
567 widthTotal =
568 GetColumnsWidth(0, NUM_LINES_TO_SAMPLE) +
569 GetColumnsWidth(m_columnsMax - NUM_LINES_TO_SAMPLE,
570 m_columnsMax) +
571 GetColumnsWidth(m_columnsMax/2 - NUM_LINES_TO_SAMPLE/2,
572 m_columnsMax/2 + NUM_LINES_TO_SAMPLE/2);
573
574 // use the width of the lines we looked as the average
575 widthTotal = (wxCoord)
576 (((float)widthTotal / (3*NUM_LINES_TO_SAMPLE)) * m_columnsMax);
577 }
578
579 return widthTotal;
580 }
581
582 wxCoord wxHVScrolledWindow::GetRowsHeight(size_t rowMin, size_t rowMax) const
583 {
584 if ( rowMin == rowMax )
585 return 0;
586 else if ( rowMin > rowMax )
587 return -GetRowsHeight(rowMax, rowMin);
588 //else: lineMin < lineMax
589
590 // let the user code know that we're going to need all these lines
591 OnGetRowsHeightHint(rowMin, rowMax);
592
593 // do sum up their heights
594 wxCoord height = 0;
595 for ( size_t row = rowMin; row < rowMax; row++ )
596 {
597 height += OnGetRowHeight(row);
598 }
599
600 return height;
601 }
602
603 wxCoord wxHVScrolledWindow::GetColumnsWidth(size_t columnMin, size_t columnMax) const
604 {
605 if ( columnMin == columnMax )
606 return 0;
607 else if ( columnMin > columnMax )
608 return -GetColumnsWidth(columnMax, columnMin);
609 //else: lineMin < lineMax
610
611 // let the user code know that we're going to need all these lines
612 OnGetColumnsWidthHint(columnMin, columnMax);
613
614 // do sum up their widths
615 wxCoord width = 0;
616 for ( size_t column = columnMin; column < columnMax; column++ )
617 {
618 width += OnGetColumnWidth(column);
619 }
620
621 return width;
622 }
623
624 size_t wxHVScrolledWindow::FindFirstFromBottom(size_t rowLast, bool full)
625 {
626 const wxCoord hWindow = GetClientSize().y;
627
628 // go upwards until we arrive at a line such that lineLast is not visible
629 // any more when it is shown
630 size_t lineFirst = rowLast;
631 wxCoord h = 0;
632 for ( ;; )
633 {
634 h += OnGetRowHeight(lineFirst);
635
636 if ( h > hWindow )
637 {
638 // for this line to be fully visible we need to go one line
639 // down, but if it is enough for it to be only partly visible then
640 // this line will do as well
641 if ( full )
642 {
643 lineFirst++;
644 }
645
646 break;
647 }
648
649 if ( !lineFirst )
650 break;
651
652 lineFirst--;
653 }
654
655 return lineFirst;
656 }
657
658 size_t wxHVScrolledWindow::FindFirstFromRight(size_t columnLast, bool full)
659 {
660 const wxCoord wWindow = GetClientSize().x;
661
662 // go upwards until we arrive at a line such that lineLast is not visible
663 // any more when it is shown
664 size_t lineFirst = columnLast;
665 wxCoord w = 0;
666 for ( ;; )
667 {
668 w += OnGetColumnWidth(lineFirst);
669
670 if ( w > wWindow )
671 {
672 // for this line to be fully visible we need to go one line
673 // down, but if it is enough for it to be only partly visible then
674 // this line will do as well
675 if ( full )
676 {
677 lineFirst++;
678 }
679
680 break;
681 }
682
683 if ( !lineFirst )
684 break;
685
686 lineFirst--;
687 }
688
689 return lineFirst;
690 }
691
692 void wxHVScrolledWindow::UpdateScrollbars()
693 {
694 // see how many lines can we fit on screen (on both axes)
695 const wxCoord wWindow = GetClientSize().x;
696 const wxCoord hWindow = GetClientSize().y;
697
698 // first do the horizontal calculations
699 wxCoord w = 0;
700 size_t column;
701 for ( column = m_columnsFirst; column < m_columnsMax; column++ )
702 {
703 if ( w > wWindow )
704 break;
705
706 w += OnGetColumnWidth(column);
707 }
708
709 m_nColumnsVisible = column - m_columnsFirst;
710
711 int columnsPageSize = m_nColumnsVisible;
712 if ( w > wWindow )
713 {
714 // last line is only partially visible, we still need the scrollbar and
715 // so we have to "fix" pageSize because if it is equal to
716 // m_horizLineMax the scrollbar is not shown at all under MSW
717 columnsPageSize--;
718 }
719
720 // set the scrollbar parameters to reflect this
721 SetScrollbar(wxHORIZONTAL, m_columnsFirst, columnsPageSize, m_columnsMax);
722
723
724 // now do the vertical calculations
725 wxCoord h = 0;
726 size_t row;
727 for ( row = m_rowsFirst; row < m_rowsMax; row++ )
728 {
729 if ( h > hWindow )
730 break;
731
732 h += OnGetRowHeight(row);
733 }
734
735 m_nRowsVisible = row - m_rowsFirst;
736
737 int rowsPageSize = m_nRowsVisible;
738 if ( h > hWindow )
739 {
740 // last line is only partially visible, we still need the scrollbar and
741 // so we have to "fix" pageSize because if it is equal to m_vertLineMax
742 // the scrollbar is not shown at all under MSW
743 rowsPageSize--;
744 }
745
746 // set the scrollbar parameters to reflect this
747 SetScrollbar(wxVERTICAL, m_rowsFirst, rowsPageSize, m_rowsMax);
748 }
749
750 void wxHVScrolledWindow::PrepareDC(wxDC& dc)
751 {
752 if(m_physicalScrolling)
753 {
754 dc.SetDeviceOrigin(-GetColumnsWidth(0, GetVisibleColumnsBegin()),
755 -GetRowsHeight(0, GetVisibleRowsBegin()));
756 }
757 }
758
759 // ----------------------------------------------------------------------------
760 // operations
761 // ----------------------------------------------------------------------------
762
763 void wxHVScrolledWindow::SetRowColumnCounts(size_t rowCount, size_t columnCount)
764 {
765 // save the number of lines
766 m_rowsMax = rowCount;
767 m_columnsMax = columnCount;
768
769 // and our estimate for their total height and width
770 m_heightTotal = EstimateTotalHeight();
771 m_widthTotal = EstimateTotalWidth();
772
773 // recalculate the scrollbars parameters
774 if(m_rowsFirst >= rowCount)
775 m_rowsFirst = rowCount-1;
776
777 if(m_columnsFirst >= columnCount)
778 m_columnsFirst = columnCount-1;
779
780 if(m_rowsFirst < 0)
781 m_rowsFirst = 0;
782
783 if(m_columnsFirst < 0)
784 m_columnsFirst = 0;
785
786 ScrollToRowColumn(m_rowsFirst, m_columnsFirst);
787 }
788
789 void wxHVScrolledWindow::RefreshColumn(size_t column)
790 {
791 // is this line visible?
792 if ( !IsColumnVisible(column) )
793 {
794 // no, it is useless to do anything
795 return;
796 }
797
798 // calculate the rect occupied by this line on screen
799 wxRect rect;
800 rect.width = OnGetColumnWidth(column);
801 rect.height = GetClientSize().y;
802 for ( size_t n = GetVisibleColumnsBegin(); n < column; n++ )
803 {
804 rect.y += OnGetColumnWidth(n);
805 }
806
807 // do refresh it
808 RefreshRect(rect);
809 }
810
811 void wxHVScrolledWindow::RefreshRow(size_t row)
812 {
813 // is this line visible?
814 if ( !IsRowVisible(row) )
815 {
816 // no, it is useless to do anything
817 return;
818 }
819
820 // calculate the rect occupied by this line on screen
821 wxRect rect;
822 rect.width = GetClientSize().x;
823 rect.height = OnGetRowHeight(row);
824 for ( size_t n = GetVisibleRowsBegin(); n < row; n++ )
825 {
826 rect.y += OnGetRowHeight(n);
827 }
828
829 // do refresh it
830 RefreshRect(rect);
831 }
832
833 void wxHVScrolledWindow::RefreshRowColumn(size_t row, size_t column)
834 {
835 // is this line visible?
836 if ( !IsRowVisible(row) || !IsColumnVisible(column) )
837 {
838 // no, it is useless to do anything
839 return;
840 }
841
842 // calculate the rect occupied by this cell on screen
843 wxRect rect;
844 rect.height = OnGetRowHeight(row);
845 rect.width = OnGetColumnWidth(column);
846
847 for ( size_t n = GetVisibleRowsBegin(); n < row; n++ )
848 {
849 rect.y += OnGetRowHeight(n);
850 }
851
852 for ( size_t n = GetVisibleColumnsBegin(); n < column; n++ )
853 {
854 rect.x += OnGetColumnWidth(n);
855 }
856
857 // do refresh it
858 RefreshRect(rect);
859 }
860
861 void wxHVScrolledWindow::RefreshRows(size_t from, size_t to)
862 {
863 wxASSERT_MSG( from <= to, _T("RefreshRows(): empty range") );
864
865 // clump the range to just the visible lines -- it is useless to refresh
866 // the other ones
867 if ( from < GetVisibleRowsBegin() )
868 from = GetVisibleRowsBegin();
869
870 if ( to > GetVisibleRowsEnd() )
871 to = GetVisibleRowsEnd();
872
873 // calculate the rect occupied by these lines on screen
874 wxRect rect;
875 rect.width = GetClientSize().x;
876 for ( size_t nBefore = GetVisibleRowsBegin();
877 nBefore < from;
878 nBefore++ )
879 {
880 rect.y += OnGetRowHeight(nBefore);
881 }
882
883 for ( size_t nBetween = from; nBetween <= to; nBetween++ )
884 {
885 rect.height += OnGetRowHeight(nBetween);
886 }
887
888 // do refresh it
889 RefreshRect(rect);
890 }
891
892 void wxHVScrolledWindow::RefreshColumns(size_t from, size_t to)
893 {
894 wxASSERT_MSG( from <= to, _T("RefreshColumns(): empty range") );
895
896 // clump the range to just the visible lines -- it is useless to refresh
897 // the other ones
898 if ( from < GetVisibleColumnsBegin() )
899 from = GetVisibleColumnsBegin();
900
901 if ( to > GetVisibleColumnsEnd() )
902 to = GetVisibleColumnsEnd();
903
904 // calculate the rect occupied by these lines on screen
905 wxRect rect;
906 rect.height = GetClientSize().y;
907 for ( size_t nBefore = GetVisibleColumnsBegin();
908 nBefore < from;
909 nBefore++ )
910 {
911 rect.x += OnGetColumnWidth(nBefore);
912 }
913
914 for ( size_t nBetween = from; nBetween <= to; nBetween++ )
915 {
916 rect.width += OnGetColumnWidth(nBetween);
917 }
918
919 // do refresh it
920 RefreshRect(rect);
921 }
922
923 void wxHVScrolledWindow::RefreshRowsColumns(size_t fromRow, size_t toRow,
924 size_t fromColumn, size_t toColumn)
925 {
926 wxASSERT_MSG( fromRow <= toRow || fromColumn <= toColumn,
927 _T("RefreshRowsColumns(): empty range") );
928
929 // clump the range to just the visible lines -- it is useless to refresh
930 // the other ones
931 if ( fromRow < GetVisibleRowsBegin() )
932 fromRow = GetVisibleRowsBegin();
933
934 if ( toRow > GetVisibleRowsEnd() )
935 toRow = GetVisibleRowsEnd();
936
937 if ( fromColumn < GetVisibleColumnsBegin() )
938 fromColumn = GetVisibleColumnsBegin();
939
940 if ( toColumn > GetVisibleColumnsEnd() )
941 toColumn = GetVisibleColumnsEnd();
942
943 // calculate the rect occupied by these lines on screen
944 wxRect rect;
945 for ( size_t nBefore = GetVisibleRowsBegin();
946 nBefore < fromRow;
947 nBefore++ )
948 {
949 rect.y += OnGetRowHeight(nBefore);
950 }
951
952 for ( size_t nBetween = fromRow; nBetween <= toRow; nBetween++ )
953 {
954 rect.height += OnGetRowHeight(nBetween);
955 }
956
957 for ( size_t nBefore = GetVisibleColumnsBegin();
958 nBefore < fromColumn;
959 nBefore++ )
960 {
961 rect.x += OnGetColumnWidth(nBefore);
962 }
963
964 for ( size_t nBetween = fromColumn; nBetween <= toColumn; nBetween++ )
965 {
966 rect.width += OnGetColumnWidth(nBetween);
967 }
968
969 // do refresh it
970 RefreshRect(rect);
971 }
972
973 void wxHVScrolledWindow::RefreshAll()
974 {
975 UpdateScrollbars();
976
977 Refresh();
978 }
979
980 bool wxHVScrolledWindow::Layout()
981 {
982 if(GetSizer() && m_physicalScrolling)
983 {
984 // adjust the sizer dimensions/position taking into account the
985 // virtual size and scrolled position of the window.
986
987 int x, y, w, h;
988
989 y = -GetRowsHeight(0, GetVisibleRowsBegin());
990 x = -GetColumnsWidth(0, GetVisibleColumnsBegin());
991 GetVirtualSize(&w, &h);
992 GetSizer()->SetDimension(0, y, w, h);
993 return true;
994 }
995
996 // fall back to default for LayoutConstraints
997 return wxPanel::Layout();
998 }
999
1000 wxPoint wxHVScrolledWindow::HitTest(wxCoord x, wxCoord y) const
1001 {
1002 const size_t rowMax = GetVisibleRowsEnd();
1003 const size_t columnMax = GetVisibleColumnsEnd();
1004
1005 wxPoint hit(wxNOT_FOUND, wxNOT_FOUND);
1006 for ( size_t row = GetVisibleRowsBegin();
1007 row <= rowMax;
1008 row++ )
1009 {
1010 y -= OnGetRowHeight(row);
1011 if ( y < 0 )
1012 hit.y = row;
1013 }
1014
1015 for ( size_t column = GetVisibleColumnsBegin();
1016 column <= columnMax;
1017 column++ )
1018 {
1019 x -= OnGetColumnWidth(column);
1020 if ( x < 0 )
1021 hit.x = column;
1022 }
1023
1024 return hit;
1025 }
1026
1027 // ----------------------------------------------------------------------------
1028 // scrolling
1029 // ----------------------------------------------------------------------------
1030
1031 bool wxHVScrolledWindow::ScrollToRowColumn(size_t row, size_t column)
1032 {
1033 if ( !m_rowsMax && !m_columnsMax )
1034 {
1035 // we're empty, code below doesn't make sense in this case
1036 return false;
1037 }
1038
1039 bool scrolled = false;
1040 scrolled |= ScrollToRow(row);
1041 scrolled |= ScrollToColumn(column);
1042
1043 return scrolled;
1044 }
1045
1046 bool wxHVScrolledWindow::ScrollToRow(size_t row)
1047 {
1048 if ( !m_rowsMax )
1049 {
1050 // we're empty, code below doesn't make sense in this case
1051 return false;
1052 }
1053
1054 // determine the real first line to scroll to: we shouldn't scroll beyond
1055 // the end
1056 size_t lineFirstLast = FindFirstFromBottom(m_rowsMax - 1, true);
1057 if ( row > lineFirstLast )
1058 row = lineFirstLast;
1059
1060 // anything to do?
1061 if ( row == m_rowsFirst )
1062 {
1063 // no
1064 return false;
1065 }
1066
1067
1068 // remember the currently shown lines for the refresh code below
1069 size_t lineFirstOld = GetVisibleRowsBegin();
1070
1071 m_rowsFirst = row;
1072
1073
1074 // the size of scrollbar thumb could have changed
1075 UpdateScrollbars();
1076
1077
1078 // finally, scroll the actual window contents vertically
1079 if(m_physicalScrolling)
1080 ScrollWindow(0, GetRowsHeight(GetVisibleRowsBegin(), lineFirstOld));
1081
1082 return true;
1083 }
1084
1085 bool wxHVScrolledWindow::ScrollToColumn(size_t column)
1086 {
1087 if ( !m_columnsMax )
1088 {
1089 // we're empty, code below doesn't make sense in this case
1090 return false;
1091 }
1092
1093 // determine the real first line to scroll to: we shouldn't scroll beyond
1094 // the end
1095 size_t lineFirstLast = FindFirstFromRight(m_columnsMax - 1, true);
1096 if ( column > lineFirstLast )
1097 column = lineFirstLast;
1098
1099 // anything to do?
1100 if ( column == m_columnsFirst )
1101 {
1102 // no
1103 return false;
1104 }
1105
1106
1107 // remember the currently shown lines for the refresh code below
1108 size_t lineFirstOld = GetVisibleColumnsBegin();
1109
1110 m_columnsFirst = column;
1111
1112
1113 // the size of scrollbar thumb could have changed
1114 UpdateScrollbars();
1115
1116 // finally, scroll the actual window contents horizontally
1117 if(m_physicalScrolling)
1118 ScrollWindow(GetColumnsWidth(GetVisibleColumnsBegin(), lineFirstOld), 0);
1119
1120 return true;
1121 }
1122
1123 bool wxHVScrolledWindow::ScrollRows(int rows)
1124 {
1125 rows += m_rowsFirst;
1126 if ( rows < 0 )
1127 rows = 0;
1128
1129 return ScrollToRow(rows);
1130 }
1131
1132 bool wxHVScrolledWindow::ScrollColumns(int columns)
1133 {
1134 columns += m_columnsFirst;
1135 if ( columns < 0 )
1136 columns = 0;
1137
1138 return ScrollToColumn(columns);
1139 }
1140
1141 bool wxHVScrolledWindow::ScrollRowsColumns(int rows, int columns)
1142 {
1143 rows += m_rowsFirst;
1144 if ( rows < 0 )
1145 rows = 0;
1146
1147 columns += m_columnsFirst;
1148 if ( columns < 0 )
1149 columns = 0;
1150
1151 return ScrollToRowColumn(rows, columns);
1152 }
1153
1154 bool wxHVScrolledWindow::ScrollRowPages(int pages)
1155 {
1156 bool didSomething = false;
1157
1158 while ( pages )
1159 {
1160 int line;
1161 if ( pages > 0 )
1162 {
1163 line = GetVisibleRowsEnd();
1164 if ( line )
1165 line--;
1166 pages--;
1167 }
1168 else // pages < 0
1169 {
1170 line = FindFirstFromBottom(GetVisibleRowsEnd());
1171 pages++;
1172 }
1173
1174 didSomething = ScrollToRow(line);
1175 }
1176
1177 return didSomething;
1178 }
1179
1180 bool wxHVScrolledWindow::ScrollColumnPages(int pages)
1181 {
1182 bool didSomething = false;
1183
1184 while ( pages )
1185 {
1186 int line;
1187 if ( pages > 0 )
1188 {
1189 line = GetVisibleColumnsEnd();
1190 if ( line )
1191 line--;
1192 pages--;
1193 }
1194 else // pages < 0
1195 {
1196 line = FindFirstFromRight(GetVisibleColumnsEnd());
1197 pages++;
1198 }
1199
1200 didSomething = ScrollToColumn(line);
1201 }
1202
1203 return didSomething;
1204 }
1205
1206 bool wxHVScrolledWindow::ScrollPages(int rowPages, int columnPages)
1207 {
1208 bool didSomething = false;
1209
1210 while ( rowPages )
1211 {
1212 int line;
1213 if ( rowPages > 0 )
1214 {
1215 line = GetVisibleRowsEnd();
1216 if ( line )
1217 line--;
1218 rowPages--;
1219 }
1220 else // rowPages < 0
1221 {
1222 line = FindFirstFromBottom(GetVisibleRowsBegin());
1223 rowPages++;
1224 }
1225
1226 didSomething = ScrollToRow(line);
1227 }
1228
1229 while ( columnPages )
1230 {
1231 int line;
1232 if ( columnPages > 0 )
1233 {
1234 line = GetVisibleColumnsEnd();
1235 if ( line )
1236 line--;
1237 columnPages--;
1238 }
1239 else // columnPages < 0
1240 {
1241 line = FindFirstFromRight(GetVisibleColumnsBegin());
1242 columnPages++;
1243 }
1244
1245 didSomething |= ScrollToColumn(line);
1246 }
1247
1248 return didSomething;
1249 }
1250
1251 // ----------------------------------------------------------------------------
1252 // event handling
1253 // ----------------------------------------------------------------------------
1254
1255 void wxHVScrolledWindow::OnSize(wxSizeEvent& event)
1256 {
1257 UpdateScrollbars();
1258 Layout();
1259
1260 event.Skip();
1261 }
1262
1263 void wxHVScrolledWindow::OnScroll(wxScrollWinEvent& event)
1264 {
1265 if(event.GetOrientation() == wxHORIZONTAL)
1266 {
1267 size_t columnsFirstNew;
1268 const wxEventType evtType = event.GetEventType();
1269
1270 if ( evtType == wxEVT_SCROLLWIN_TOP )
1271 {
1272 columnsFirstNew = 0;
1273 }
1274 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
1275 {
1276 columnsFirstNew = m_columnsMax;
1277 }
1278 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
1279 {
1280 columnsFirstNew = m_columnsFirst ? m_columnsFirst - 1 : 0;
1281 }
1282 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
1283 {
1284 columnsFirstNew = m_columnsFirst + 1;
1285 }
1286 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
1287 {
1288 columnsFirstNew = FindFirstFromRight(m_columnsFirst);
1289 }
1290 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
1291 {
1292 columnsFirstNew = GetVisibleColumnsEnd();
1293 if ( columnsFirstNew )
1294 columnsFirstNew--;
1295 }
1296 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
1297 {
1298 columnsFirstNew = event.GetPosition();
1299 }
1300 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
1301 {
1302 columnsFirstNew = event.GetPosition();
1303 }
1304
1305 else // unknown scroll event?
1306 {
1307 wxFAIL_MSG( _T("unknown scroll event type?") );
1308 return;
1309 }
1310
1311 ScrollToColumn(columnsFirstNew);
1312 }
1313 else if(event.GetOrientation() == wxVERTICAL)
1314 {
1315 size_t rowsFirstNew;
1316 const wxEventType evtType = event.GetEventType();
1317
1318 if ( evtType == wxEVT_SCROLLWIN_TOP )
1319 {
1320 rowsFirstNew = 0;
1321 }
1322 else if ( evtType == wxEVT_SCROLLWIN_BOTTOM )
1323 {
1324 rowsFirstNew = m_rowsMax;
1325 }
1326 else if ( evtType == wxEVT_SCROLLWIN_LINEUP )
1327 {
1328 rowsFirstNew = m_rowsFirst ? m_rowsFirst - 1 : 0;
1329 }
1330 else if ( evtType == wxEVT_SCROLLWIN_LINEDOWN )
1331 {
1332 rowsFirstNew = m_rowsFirst + 1;
1333 }
1334 else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
1335 {
1336 rowsFirstNew = FindFirstFromBottom(m_rowsFirst);
1337 }
1338 else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
1339 {
1340 rowsFirstNew = GetVisibleRowsEnd();
1341 if ( rowsFirstNew )
1342 rowsFirstNew--;
1343 }
1344 else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
1345 {
1346 rowsFirstNew = event.GetPosition();
1347 }
1348 else if ( evtType == wxEVT_SCROLLWIN_THUMBTRACK )
1349 {
1350 rowsFirstNew = event.GetPosition();
1351 }
1352
1353 else // unknown scroll event?
1354 {
1355 wxFAIL_MSG( _T("unknown scroll event type?") );
1356 return;
1357 }
1358
1359 ScrollToRow(rowsFirstNew);
1360 }
1361
1362
1363 #ifdef __WXMAC__
1364 Update();
1365 #endif // __WXMAC__
1366 }
1367
1368 #if wxUSE_MOUSEWHEEL
1369
1370 void wxHVScrolledWindow::OnMouseWheel(wxMouseEvent& event)
1371 {
1372 m_sumWheelRotation += event.GetWheelRotation();
1373 int delta = event.GetWheelDelta();
1374
1375 // how much to scroll this time
1376 int units_to_scroll = -(m_sumWheelRotation/delta);
1377 if ( !units_to_scroll )
1378 return;
1379
1380 m_sumWheelRotation += units_to_scroll*delta;
1381
1382 if ( !event.IsPageScroll() )
1383 ScrollRows( units_to_scroll*event.GetLinesPerAction() );
1384 else
1385 // scroll pages instead of lines
1386 ScrollRowPages( units_to_scroll );
1387 }
1388
1389 #endif
1390