1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/generic/vscroll.cpp 
   3 // Purpose:     wxVScrolledWindow implementation 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org> 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #include "wx/vscroll.h" 
  29 // ---------------------------------------------------------------------------- 
  31 // ---------------------------------------------------------------------------- 
  33 BEGIN_EVENT_TABLE(wxVScrolledWindow
, wxPanel
) 
  34     EVT_SIZE(wxVScrolledWindow::OnSize
) 
  35     EVT_SCROLLWIN(wxVScrolledWindow::OnScroll
) 
  37     EVT_MOUSEWHEEL(wxVScrolledWindow::OnMouseWheel
) 
  42 // ============================================================================ 
  44 // ============================================================================ 
  46 IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow
, wxPanel
) 
  48 // ---------------------------------------------------------------------------- 
  50 // ---------------------------------------------------------------------------- 
  52 void wxVScrolledWindow::Init() 
  54     // we're initially empty 
  58     // this one should always be strictly positive 
  64     m_sumWheelRotation 
= 0; 
  68 // ---------------------------------------------------------------------------- 
  70 // ---------------------------------------------------------------------------- 
  72 wxCoord 
wxVScrolledWindow::EstimateTotalHeight() const 
  74     // estimate the total height: it is impossible to call 
  75     // OnGetLineHeight() for every line because there may be too many of 
  76     // them, so we just make a guess using some lines in the beginning, 
  77     // some in the end and some in the middle 
  78     static const size_t NUM_LINES_TO_SAMPLE 
= 10; 
  81     if ( m_lineMax 
< 3*NUM_LINES_TO_SAMPLE 
) 
  83         // in this case calculating exactly is faster and more correct than 
  85         heightTotal 
= GetLinesHeight(0, m_lineMax
); 
  87     else // too many lines to calculate exactly 
  89         // look at some lines in the beginning/middle/end 
  91             GetLinesHeight(0, NUM_LINES_TO_SAMPLE
) + 
  92                 GetLinesHeight(m_lineMax 
- NUM_LINES_TO_SAMPLE
, m_lineMax
) + 
  93                     GetLinesHeight(m_lineMax
/2 - NUM_LINES_TO_SAMPLE
/2, 
  94                                    m_lineMax
/2 + NUM_LINES_TO_SAMPLE
/2); 
  96         // use the height of the lines we looked as the average 
  97         heightTotal 
= (wxCoord
) 
  98                 (((float)heightTotal 
/ (3*NUM_LINES_TO_SAMPLE
)) * m_lineMax
); 
 104 wxCoord 
wxVScrolledWindow::GetLinesHeight(size_t lineMin
, size_t lineMax
) const 
 106     if ( lineMin 
== lineMax 
) 
 108     else if ( lineMin 
> lineMax 
) 
 109         return -GetLinesHeight(lineMax
, lineMin
); 
 110     //else: lineMin < lineMax 
 112     // let the user code know that we're going to need all these lines 
 113     OnGetLinesHint(lineMin
, lineMax
); 
 115     // do sum up their heights 
 117     for ( size_t line 
= lineMin
; line 
< lineMax
; line
++ ) 
 119         height 
+= OnGetLineHeight(line
); 
 125 size_t wxVScrolledWindow::FindFirstFromBottom(size_t lineLast
, bool full
) 
 127     const wxCoord hWindow 
= GetClientSize().y
; 
 129     // go upwards until we arrive at a line such that lineLast is not visible 
 130     // any more when it is shown 
 131     size_t lineFirst 
= lineLast
; 
 135         h 
+= OnGetLineHeight(lineFirst
); 
 139             // for this line to be fully visible we need to go one line 
 140             // down, but if it is enough for it to be only partly visible then 
 141             // this line will do as well 
 159 void wxVScrolledWindow::UpdateScrollbar() 
 161     // see how many lines can we fit on screen 
 162     const wxCoord hWindow 
= GetClientSize().y
; 
 166     for ( line 
= m_lineFirst
; line 
< m_lineMax
; line
++ ) 
 171         h 
+= OnGetLineHeight(line
); 
 174     // if we still have remaining space below, maybe we can fit everything? 
 178         for ( size_t lineFirst 
= m_lineFirst
; lineFirst 
> 0; lineFirst
-- ) 
 180             hAll 
+= OnGetLineHeight(m_lineFirst 
- 1); 
 181             if ( hAll 
> hWindow 
) 
 185         if ( hAll 
< hWindow 
) 
 187             // we don't need scrollbar at all 
 189             SetScrollbar(wxVERTICAL
, 0, 0, 0); 
 193     m_nVisible 
= line 
- m_lineFirst
; 
 195     int pageSize 
= m_nVisible
; 
 198         // last line is only partially visible, we still need the scrollbar and 
 199         // so we have to "fix" pageSize because if it is equal to m_lineMax the 
 200         // scrollbar is not shown at all under MSW 
 204     // set the scrollbar parameters to reflect this 
 205     SetScrollbar(wxVERTICAL
, m_lineFirst
, pageSize
, m_lineMax
); 
 208 // ---------------------------------------------------------------------------- 
 210 // ---------------------------------------------------------------------------- 
 212 void wxVScrolledWindow::SetLineCount(size_t count
) 
 214     // save the number of lines 
 217     // and our estimate for their total height 
 218     m_heightTotal 
= EstimateTotalHeight(); 
 220     // recalculate the scrollbars parameters 
 221     m_lineFirst 
= 1;    // make sure it is != 0 
 225 void wxVScrolledWindow::RefreshLine(size_t line
) 
 227     // is this line visible? 
 228     if ( !IsVisible(line
) ) 
 230         // no, it is useless to do anything 
 234     // calculate the rect occupied by this line on screen 
 236     rect
.width 
= GetClientSize().x
; 
 237     rect
.height 
= OnGetLineHeight(line
); 
 238     for ( size_t n 
= GetVisibleBegin(); n 
< line
; n
++ ) 
 240         rect
.y 
+= OnGetLineHeight(n
); 
 247 void wxVScrolledWindow::RefreshLines(size_t from
, size_t to
) 
 249     wxASSERT_MSG( from 
<= to
, _T("RefreshLines(): empty range") ); 
 251     // clump the range to just the visible lines -- it is useless to refresh 
 253     if ( from 
< GetVisibleBegin() ) 
 254         from 
= GetVisibleBegin(); 
 256     if ( to 
>= GetVisibleEnd() ) 
 257         to 
= GetVisibleEnd(); 
 261     // calculate the rect occupied by these lines on screen 
 263     rect
.width 
= GetClientSize().x
; 
 264     for ( size_t nBefore 
= GetVisibleBegin(); nBefore 
< from
; nBefore
++ ) 
 266         rect
.y 
+= OnGetLineHeight(nBefore
); 
 269     for ( size_t nBetween 
= from
; nBetween 
< to
; nBetween
++ ) 
 271         rect
.height 
+= OnGetLineHeight(nBetween
); 
 278 void wxVScrolledWindow::RefreshAll() 
 285 int wxVScrolledWindow::HitTest(wxCoord 
WXUNUSED(x
), wxCoord y
) const 
 287     const size_t lineMax 
= GetVisibleEnd(); 
 288     for ( size_t line 
= GetVisibleBegin(); line 
< lineMax
; line
++ ) 
 290         y 
-= OnGetLineHeight(line
); 
 298 // ---------------------------------------------------------------------------- 
 300 // ---------------------------------------------------------------------------- 
 302 bool wxVScrolledWindow::ScrollToLine(size_t line
) 
 306         // we're empty, code below doesn't make sense in this case 
 310     // determine the real first line to scroll to: we shouldn't scroll beyond 
 312     size_t lineFirstLast 
= FindFirstFromBottom(m_lineMax 
- 1, true); 
 313     if ( line 
> lineFirstLast 
) 
 314         line 
= lineFirstLast
; 
 317     if ( line 
== m_lineFirst 
) 
 324     // remember the currently shown lines for the refresh code below 
 325     size_t lineFirstOld 
= GetVisibleBegin(), 
 326            lineLastOld 
= GetVisibleEnd(); 
 331     // the size of scrollbar thumb could have changed 
 335     // finally refresh the display -- but only redraw as few lines as possible 
 337     if ( GetVisibleBegin() >= lineLastOld 
|| 
 338             GetVisibleEnd() <= lineFirstOld 
) 
 340         // the simplest case: we don't have any old lines left, just redraw 
 344     else // overlap between the lines we showed before and should show now 
 346         ScrollWindow(0, GetLinesHeight(GetVisibleBegin(), lineFirstOld
)); 
 352 bool wxVScrolledWindow::ScrollLines(int lines
) 
 354     lines 
+= m_lineFirst
; 
 358     return ScrollToLine(lines
); 
 361 bool wxVScrolledWindow::ScrollPages(int pages
) 
 363     bool didSomething 
= false; 
 370             line 
= GetVisibleEnd(); 
 377             line 
= FindFirstFromBottom(GetVisibleBegin()); 
 381         didSomething 
= ScrollToLine(line
); 
 387 // ---------------------------------------------------------------------------- 
 389 // ---------------------------------------------------------------------------- 
 391 void wxVScrolledWindow::OnSize(wxSizeEvent
& event
) 
 398 void wxVScrolledWindow::OnScroll(wxScrollWinEvent
& event
) 
 402     const wxEventType evtType 
= event
.GetEventType(); 
 404     if ( evtType 
== wxEVT_SCROLLWIN_TOP 
) 
 408     else if ( evtType 
== wxEVT_SCROLLWIN_BOTTOM 
) 
 410         lineFirstNew 
= m_lineMax
; 
 412     else if ( evtType 
== wxEVT_SCROLLWIN_LINEUP 
) 
 414         lineFirstNew 
= m_lineFirst 
? m_lineFirst 
- 1 : 0; 
 416     else if ( evtType 
== wxEVT_SCROLLWIN_LINEDOWN 
) 
 418         lineFirstNew 
= m_lineFirst 
+ 1; 
 420     else if ( evtType 
== wxEVT_SCROLLWIN_PAGEUP 
) 
 422         lineFirstNew 
= FindFirstFromBottom(m_lineFirst
); 
 424     else if ( evtType 
== wxEVT_SCROLLWIN_PAGEDOWN 
) 
 426         lineFirstNew 
= GetVisibleEnd(); 
 430     else if ( evtType 
== wxEVT_SCROLLWIN_THUMBRELEASE 
) 
 432         lineFirstNew 
= event
.GetPosition(); 
 434     else if ( evtType 
== wxEVT_SCROLLWIN_THUMBTRACK 
) 
 436         lineFirstNew 
= event
.GetPosition(); 
 439     else // unknown scroll event? 
 441         wxFAIL_MSG( _T("unknown scroll event type?") ); 
 445     ScrollToLine(lineFirstNew
); 
 454 void wxVScrolledWindow::OnMouseWheel(wxMouseEvent
& event
) 
 456     m_sumWheelRotation 
+= event
.GetWheelRotation(); 
 457     int delta 
= event
.GetWheelDelta(); 
 459     // how much to scroll this time 
 460     int units_to_scroll 
= -(m_sumWheelRotation
/delta
); 
 461     if ( !units_to_scroll 
) 
 464     m_sumWheelRotation 
+= units_to_scroll
*delta
; 
 466     if ( !event
.IsPageScroll() ) 
 467         ScrollLines( units_to_scroll
*event
.GetLinesPerAction() ); 
 469         // scroll pages instead of lines 
 470         ScrollPages( units_to_scroll 
); 
 473 #endif // wxUSE_MOUSEWHEEL