1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/generic/scrlwing.cpp 
   3 // Purpose:     wxScrolledWindow implementation 
   4 // Author:      Julian Smart 
   5 // Modified by: Vadim Zeitlin on 31.08.00: wxScrollHelper allows to implement. 
   6 //              Ron Lee on 10.4.02:  virtual size / auto scrollbars et al. 
   9 // Copyright:   (c) wxWidgets team 
  10 // Licence:     wxWindows licence 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  22 #define XtDisplay XTDISPLAY 
  25 // For compilers that support precompilation, includes "wx.h". 
  26 #include "wx/wxprec.h" 
  32 #include "wx/scrolwin.h" 
  37     #include "wx/dcclient.h" 
  44 #include "wx/recguard.h" 
  47     #include <windows.h> // for DLGC_WANTARROWS 
  48     #include "wx/msw/winundef.h" 
  52 // For wxRETAINED implementation 
  53 #ifdef __VMS__ //VMS's Xm.h is not (yet) compatible with C++ 
  54                //This code switches off the compiler warnings 
  55 # pragma message disable nosimpint 
  59 # pragma message enable nosimpint 
  65         style wxHSCROLL | wxVSCROLL 
  68 // ---------------------------------------------------------------------------- 
  69 // wxScrollHelperEvtHandler: intercept the events from the window and forward 
  70 // them to wxScrollHelper 
  71 // ---------------------------------------------------------------------------- 
  73 class WXDLLEXPORT wxScrollHelperEvtHandler 
: public wxEvtHandler
 
  76     wxScrollHelperEvtHandler(wxScrollHelper 
*scrollHelper
) 
  78         m_scrollHelper 
= scrollHelper
; 
  81     virtual bool ProcessEvent(wxEvent
& event
); 
  83     void ResetDrawnFlag() { m_hasDrawnWindow 
= false; } 
  86     wxScrollHelper 
*m_scrollHelper
; 
  88     bool m_hasDrawnWindow
; 
  90     DECLARE_NO_COPY_CLASS(wxScrollHelperEvtHandler
) 
  94 // ---------------------------------------------------------------------------- 
  95 // wxAutoScrollTimer: the timer used to generate a stream of scroll events when 
  96 // a captured mouse is held outside the window 
  97 // ---------------------------------------------------------------------------- 
  99 class wxAutoScrollTimer 
: public wxTimer
 
 102     wxAutoScrollTimer(wxWindow 
*winToScroll
, wxScrollHelper 
*scroll
, 
 103                       wxEventType eventTypeToSend
, 
 104                       int pos
, int orient
); 
 106     virtual void Notify(); 
 110     wxScrollHelper 
*m_scrollHelper
; 
 111     wxEventType m_eventType
; 
 115     DECLARE_NO_COPY_CLASS(wxAutoScrollTimer
) 
 118 // ============================================================================ 
 120 // ============================================================================ 
 122 // ---------------------------------------------------------------------------- 
 124 // ---------------------------------------------------------------------------- 
 126 wxAutoScrollTimer::wxAutoScrollTimer(wxWindow 
*winToScroll
, 
 127                                      wxScrollHelper 
*scroll
, 
 128                                      wxEventType eventTypeToSend
, 
 132     m_scrollHelper 
= scroll
; 
 133     m_eventType 
= eventTypeToSend
; 
 138 void wxAutoScrollTimer::Notify() 
 140     // only do all this as long as the window is capturing the mouse 
 141     if ( wxWindow::GetCapture() != m_win 
) 
 145     else // we still capture the mouse, continue generating events 
 147         // first scroll the window if we are allowed to do it 
 148         wxScrollWinEvent 
event1(m_eventType
, m_pos
, m_orient
); 
 149         event1
.SetEventObject(m_win
); 
 150         if ( m_scrollHelper
->SendAutoScrollEvents(event1
) && 
 151                 m_win
->GetEventHandler()->ProcessEvent(event1
) ) 
 153             // and then send a pseudo mouse-move event to refresh the selection 
 154             wxMouseEvent 
event2(wxEVT_MOTION
); 
 155             wxGetMousePosition(&event2
.m_x
, &event2
.m_y
); 
 157             // the mouse event coordinates should be client, not screen as 
 158             // returned by wxGetMousePosition 
 159             wxWindow 
*parentTop 
= m_win
; 
 160             while ( parentTop
->GetParent() ) 
 161                 parentTop 
= parentTop
->GetParent(); 
 162             wxPoint ptOrig 
= parentTop
->GetPosition(); 
 163             event2
.m_x 
-= ptOrig
.x
; 
 164             event2
.m_y 
-= ptOrig
.y
; 
 166             event2
.SetEventObject(m_win
); 
 168             // FIXME: we don't fill in the other members - ok? 
 170             m_win
->GetEventHandler()->ProcessEvent(event2
); 
 172         else // can't scroll further, stop 
 180 // ---------------------------------------------------------------------------- 
 181 // wxScrollHelperEvtHandler 
 182 // ---------------------------------------------------------------------------- 
 184 bool wxScrollHelperEvtHandler::ProcessEvent(wxEvent
& event
) 
 186     wxEventType evType 
= event
.GetEventType(); 
 188     // the explanation of wxEVT_PAINT processing hack: for historic reasons 
 189     // there are 2 ways to process this event in classes deriving from 
 190     // wxScrolledWindow. The user code may 
 192     //  1. override wxScrolledWindow::OnDraw(dc) 
 193     //  2. define its own OnPaint() handler 
 195     // In addition, in wxUniversal wxWindow defines OnPaint() itself and 
 196     // always processes the draw event, so we can't just try the window 
 197     // OnPaint() first and call our HandleOnPaint() if it doesn't process it 
 198     // (the latter would never be called in wxUniversal). 
 200     // So the solution is to have a flag telling us whether the user code drew 
 201     // anything in the window. We set it to true here but reset it to false in 
 202     // wxScrolledWindow::OnPaint() handler (which wouldn't be called if the 
 203     // user code defined OnPaint() in the derived class) 
 204     m_hasDrawnWindow 
= true; 
 206     // pass it on to the real handler 
 207     bool processed 
= wxEvtHandler::ProcessEvent(event
); 
 209     // always process the size events ourselves, even if the user code handles 
 210     // them as well, as we need to AdjustScrollbars() 
 212     // NB: it is important to do it after processing the event in the normal 
 213     //     way as HandleOnSize() may generate a wxEVT_SIZE itself if the 
 214     //     scrollbar[s] (dis)appear and it should be seen by the user code 
 216     if ( evType 
== wxEVT_SIZE 
) 
 218         m_scrollHelper
->HandleOnSize((wxSizeEvent 
&)event
); 
 225         // normally, nothing more to do here - except if it was a paint event 
 226         // which wasn't really processed, then we'll try to call our 
 227         // OnDraw() below (from HandleOnPaint) 
 228         if ( m_hasDrawnWindow 
|| event
.IsCommandEvent() ) 
 234     // reset the skipped flag to false as it might have been set to true in 
 235     // ProcessEvent() above 
 238     if ( evType 
== wxEVT_PAINT 
) 
 240         m_scrollHelper
->HandleOnPaint((wxPaintEvent 
&)event
); 
 244     if ( evType 
== wxEVT_SCROLLWIN_TOP 
|| 
 245          evType 
== wxEVT_SCROLLWIN_BOTTOM 
|| 
 246          evType 
== wxEVT_SCROLLWIN_LINEUP 
|| 
 247          evType 
== wxEVT_SCROLLWIN_LINEDOWN 
|| 
 248          evType 
== wxEVT_SCROLLWIN_PAGEUP 
|| 
 249          evType 
== wxEVT_SCROLLWIN_PAGEDOWN 
|| 
 250          evType 
== wxEVT_SCROLLWIN_THUMBTRACK 
|| 
 251          evType 
== wxEVT_SCROLLWIN_THUMBRELEASE 
) 
 253             m_scrollHelper
->HandleOnScroll((wxScrollWinEvent 
&)event
); 
 254             return !event
.GetSkipped(); 
 257     if ( evType 
== wxEVT_ENTER_WINDOW 
) 
 259         m_scrollHelper
->HandleOnMouseEnter((wxMouseEvent 
&)event
); 
 261     else if ( evType 
== wxEVT_LEAVE_WINDOW 
) 
 263         m_scrollHelper
->HandleOnMouseLeave((wxMouseEvent 
&)event
); 
 266     else if ( evType 
== wxEVT_MOUSEWHEEL 
) 
 268         m_scrollHelper
->HandleOnMouseWheel((wxMouseEvent 
&)event
); 
 270 #endif // wxUSE_MOUSEWHEEL 
 271     else if ( evType 
== wxEVT_CHAR 
) 
 273         m_scrollHelper
->HandleOnChar((wxKeyEvent 
&)event
); 
 274         return !event
.GetSkipped(); 
 280 // ---------------------------------------------------------------------------- 
 281 // wxScrollHelper construction 
 282 // ---------------------------------------------------------------------------- 
 284 wxScrollHelper::wxScrollHelper(wxWindow 
*win
) 
 286     wxASSERT_MSG( win
, _T("associated window can't be NULL in wxScrollHelper") ); 
 288     m_xScrollPixelsPerLine 
= 
 289     m_yScrollPixelsPerLine 
= 
 294     m_xScrollLinesPerPage 
= 
 295     m_yScrollLinesPerPage 
= 0; 
 297     m_xScrollingEnabled 
= 
 298     m_yScrollingEnabled 
= true; 
 307     m_targetWindow 
= (wxWindow 
*)NULL
; 
 309     m_timerAutoScroll 
= (wxTimer 
*)NULL
; 
 315     m_win
->SetScrollHelper( this ); 
 317     // by default, the associated window is also the target window 
 318     DoSetTargetWindow(win
); 
 321 wxScrollHelper::~wxScrollHelper() 
 328 // ---------------------------------------------------------------------------- 
 329 // setting scrolling parameters 
 330 // ---------------------------------------------------------------------------- 
 332 void wxScrollHelper::SetScrollbars(int pixelsPerUnitX
, 
 342     CalcUnscrolledPosition(xPos
, yPos
, &xpos
, &ypos
); 
 345       (noUnitsX 
!= 0 && m_xScrollLines 
== 0) || 
 346       (noUnitsX 
< m_xScrollLines 
&& xpos 
> pixelsPerUnitX 
* noUnitsX
) || 
 348       (noUnitsY 
!= 0 && m_yScrollLines 
== 0) || 
 349       (noUnitsY 
< m_yScrollLines 
&& ypos 
> pixelsPerUnitY 
* noUnitsY
) || 
 350       (xPos 
!= m_xScrollPosition
) || 
 351       (yPos 
!= m_yScrollPosition
) 
 354     m_xScrollPixelsPerLine 
= pixelsPerUnitX
; 
 355     m_yScrollPixelsPerLine 
= pixelsPerUnitY
; 
 356     m_xScrollPosition 
= xPos
; 
 357     m_yScrollPosition 
= yPos
; 
 359     int w 
= noUnitsX 
* pixelsPerUnitX
; 
 360     int h 
= noUnitsY 
* pixelsPerUnitY
; 
 362     // For better backward compatibility we set persisting limits 
 363     // here not just the size.  It makes SetScrollbars 'sticky' 
 364     // emulating the old non-autoscroll behaviour. 
 365     //   m_targetWindow->SetVirtualSizeHints( w, h ); 
 367     // The above should arguably be deprecated, this however we still need. 
 369     // take care not to set 0 virtual size, 0 means that we don't have any 
 370     // scrollbars and hence we should use the real size instead of the virtual 
 371     // one which is indicated by using wxDefaultCoord 
 372     m_targetWindow
->SetVirtualSize( w 
? w 
: wxDefaultCoord
, 
 373                                     h 
? h 
: wxDefaultCoord
); 
 375     if (do_refresh 
&& !noRefresh
) 
 376         m_targetWindow
->Refresh(true, GetScrollRect()); 
 378 #ifndef __WXUNIVERSAL__ 
 379     // If the target is not the same as the window with the scrollbars, 
 380     // then we need to update the scrollbars here, since they won't have 
 381     // been updated by SetVirtualSize(). 
 382     if ( m_targetWindow 
!= m_win 
) 
 383 #endif // !__WXUNIVERSAL__ 
 387 #ifndef __WXUNIVERSAL__ 
 390         // otherwise this has been done by AdjustScrollbars, above 
 392 #endif // !__WXUNIVERSAL__ 
 395 // ---------------------------------------------------------------------------- 
 396 // [target] window handling 
 397 // ---------------------------------------------------------------------------- 
 399 void wxScrollHelper::DeleteEvtHandler() 
 401     // search for m_handler in the handler list 
 402     if ( m_win 
&& m_handler 
) 
 404         if ( m_win
->RemoveEventHandler(m_handler
) ) 
 408         //else: something is very wrong, so better [maybe] leak memory than 
 409         //      risk a crash because of double deletion 
 415 void wxScrollHelper::DoSetTargetWindow(wxWindow 
*target
) 
 417     m_targetWindow 
= target
; 
 419     target
->MacSetClipChildren( true ) ; 
 422     // install the event handler which will intercept the events we're 
 423     // interested in (but only do it for our real window, not the target window 
 424     // which we scroll - we don't need to hijack its events) 
 425     if ( m_targetWindow 
== m_win 
) 
 427         // if we already have a handler, delete it first 
 430         m_handler 
= new wxScrollHelperEvtHandler(this); 
 431         m_targetWindow
->PushEventHandler(m_handler
); 
 435 void wxScrollHelper::SetTargetWindow(wxWindow 
*target
) 
 437     wxCHECK_RET( target
, wxT("target window must not be NULL") ); 
 439     if ( target 
== m_targetWindow 
) 
 442     DoSetTargetWindow(target
); 
 445 wxWindow 
*wxScrollHelper::GetTargetWindow() const 
 447     return m_targetWindow
; 
 450 // ---------------------------------------------------------------------------- 
 451 // scrolling implementation itself 
 452 // ---------------------------------------------------------------------------- 
 454 void wxScrollHelper::HandleOnScroll(wxScrollWinEvent
& event
) 
 456     int nScrollInc 
= CalcScrollInc(event
); 
 457     if ( nScrollInc 
== 0 ) 
 459         // can't scroll further 
 465     bool needsRefresh 
= false; 
 467     // OS X blocks on immediate redraws, so make this a refresh 
 472     int orient 
= event
.GetOrientation(); 
 473     if (orient 
== wxHORIZONTAL
) 
 475        if ( m_xScrollingEnabled 
) 
 477            dx 
= -m_xScrollPixelsPerLine 
* nScrollInc
; 
 486         if ( m_yScrollingEnabled 
) 
 488             dy 
= -m_yScrollPixelsPerLine 
* nScrollInc
; 
 498         // flush all pending repaints before we change m_{x,y}ScrollPosition, as 
 499         // otherwise invalidated area could be updated incorrectly later when 
 500         // ScrollWindow() makes sure they're repainted before scrolling them 
 501         m_targetWindow
->Update(); 
 504     if (orient 
== wxHORIZONTAL
) 
 506         m_xScrollPosition 
+= nScrollInc
; 
 507         m_win
->SetScrollPos(wxHORIZONTAL
, m_xScrollPosition
); 
 511         m_yScrollPosition 
+= nScrollInc
; 
 512         m_win
->SetScrollPos(wxVERTICAL
, m_yScrollPosition
); 
 517         m_targetWindow
->Refresh(true, GetScrollRect()); 
 521         m_targetWindow
->ScrollWindow(dx
, dy
, GetScrollRect()); 
 525 int wxScrollHelper::CalcScrollInc(wxScrollWinEvent
& event
) 
 527     int pos 
= event
.GetPosition(); 
 528     int orient 
= event
.GetOrientation(); 
 531     if (event
.GetEventType() == wxEVT_SCROLLWIN_TOP
) 
 533             if (orient 
== wxHORIZONTAL
) 
 534                 nScrollInc 
= - m_xScrollPosition
; 
 536                 nScrollInc 
= - m_yScrollPosition
; 
 538     if (event
.GetEventType() == wxEVT_SCROLLWIN_BOTTOM
) 
 540             if (orient 
== wxHORIZONTAL
) 
 541                 nScrollInc 
= m_xScrollLines 
- m_xScrollPosition
; 
 543                 nScrollInc 
= m_yScrollLines 
- m_yScrollPosition
; 
 545     if (event
.GetEventType() == wxEVT_SCROLLWIN_LINEUP
) 
 549     if (event
.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN
) 
 553     if (event
.GetEventType() == wxEVT_SCROLLWIN_PAGEUP
) 
 555             if (orient 
== wxHORIZONTAL
) 
 556                 nScrollInc 
= -GetScrollPageSize(wxHORIZONTAL
); 
 558                 nScrollInc 
= -GetScrollPageSize(wxVERTICAL
); 
 560     if (event
.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN
) 
 562             if (orient 
== wxHORIZONTAL
) 
 563                 nScrollInc 
= GetScrollPageSize(wxHORIZONTAL
); 
 565                 nScrollInc 
= GetScrollPageSize(wxVERTICAL
); 
 567     if ((event
.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK
) || 
 568         (event
.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE
)) 
 570             if (orient 
== wxHORIZONTAL
) 
 571                 nScrollInc 
= pos 
- m_xScrollPosition
; 
 573                 nScrollInc 
= pos 
- m_yScrollPosition
; 
 576     if (orient 
== wxHORIZONTAL
) 
 578         if (m_xScrollPixelsPerLine 
> 0) 
 580             if ( m_xScrollPosition 
+ nScrollInc 
< 0 ) 
 582                 // As -ve as we can go 
 583                 nScrollInc 
= -m_xScrollPosition
; 
 585             else // check for the other bound 
 587                 const int posMax 
= m_xScrollLines 
- m_xScrollLinesPerPage
; 
 588                 if ( m_xScrollPosition 
+ nScrollInc 
> posMax 
) 
 590                     // As +ve as we can go 
 591                     nScrollInc 
= posMax 
- m_xScrollPosition
; 
 596             m_targetWindow
->Refresh(true, GetScrollRect()); 
 600         if ( m_yScrollPixelsPerLine 
> 0 ) 
 602             if ( m_yScrollPosition 
+ nScrollInc 
< 0 ) 
 604                 // As -ve as we can go 
 605                 nScrollInc 
= -m_yScrollPosition
; 
 607             else // check for the other bound 
 609                 const int posMax 
= m_yScrollLines 
- m_yScrollLinesPerPage
; 
 610                 if ( m_yScrollPosition 
+ nScrollInc 
> posMax 
) 
 612                     // As +ve as we can go 
 613                     nScrollInc 
= posMax 
- m_yScrollPosition
; 
 619             // VZ: why do we do this? (FIXME) 
 620             m_targetWindow
->Refresh(true, GetScrollRect()); 
 627 // Adjust the scrollbars - new version. 
 628 void wxScrollHelper::AdjustScrollbars() 
 630     static wxRecursionGuardFlag s_flagReentrancy
; 
 631     wxRecursionGuard 
guard(s_flagReentrancy
); 
 632     if ( guard
.IsInside() ) 
 634         // don't reenter AdjustScrollbars() while another call to 
 635         // AdjustScrollbars() is in progress because this may lead to calling 
 636         // ScrollWindow() twice and this can really happen under MSW if 
 637         // SetScrollbar() call below adds or removes the scrollbar which 
 638         // changes the window size and hence results in another 
 639         // AdjustScrollbars() call 
 646     int oldXScroll 
= m_xScrollPosition
; 
 647     int oldYScroll 
= m_yScrollPosition
; 
 649     // VZ: at least under Windows this loop is useless because when scrollbars 
 650     //     [dis]appear we get a WM_SIZE resulting in another call to 
 651     //     AdjustScrollbars() anyhow. As it doesn't seem to do any harm I leave 
 652     //     it here for now but it would be better to ensure that all ports 
 653     //     generate EVT_SIZE when scrollbars [dis]appear, emulating it if 
 654     //     necessary, and remove it later 
 655     // JACS: Stop potential infinite loop by limiting number of iterations 
 656     int iterationCount 
= 0; 
 657     const int iterationMax 
= 5; 
 662         GetTargetSize(&w
, 0); 
 664         // scroll lines per page: if 0, no scrolling is needed 
 667         if ( m_xScrollPixelsPerLine 
== 0 ) 
 669             // scrolling is disabled 
 671             m_xScrollPosition 
= 0; 
 674         else // might need scrolling 
 676             // Round up integer division to catch any "leftover" client space. 
 677             const int wVirt 
= m_targetWindow
->GetVirtualSize().GetWidth(); 
 678             m_xScrollLines 
= (wVirt 
+ m_xScrollPixelsPerLine 
- 1) / m_xScrollPixelsPerLine
; 
 680             // Calculate page size i.e. number of scroll units you get on the 
 681             // current client window. 
 682             linesPerPage 
= w 
/ m_xScrollPixelsPerLine
; 
 684             // Special case. When client and virtual size are very close but 
 685             // the client is big enough, kill scrollbar. 
 686             if ((linesPerPage 
< m_xScrollLines
) && (w 
>= wVirt
)) ++linesPerPage
; 
 688             if (linesPerPage 
>= m_xScrollLines
) 
 690                 // we're big enough to not need scrolling 
 693                 m_xScrollPosition 
= 0; 
 695             else // we do need a scrollbar 
 697                 if ( linesPerPage 
< 1 ) 
 700                 // Correct position if greater than extent of canvas minus 
 701                 // the visible portion of it or if below zero 
 702                 const int posMax 
= m_xScrollLines 
- linesPerPage
; 
 703                 if ( m_xScrollPosition 
> posMax 
) 
 704                     m_xScrollPosition 
= posMax
; 
 705                 else if ( m_xScrollPosition 
< 0 ) 
 706                     m_xScrollPosition 
= 0; 
 710         m_win
->SetScrollbar(wxHORIZONTAL
, m_xScrollPosition
, 
 711                             linesPerPage
, m_xScrollLines
); 
 713         // The amount by which we scroll when paging 
 714         SetScrollPageSize(wxHORIZONTAL
, linesPerPage
); 
 716         GetTargetSize(0, &h
); 
 718         if ( m_yScrollPixelsPerLine 
== 0 ) 
 720             // scrolling is disabled 
 722             m_yScrollPosition 
= 0; 
 725         else // might need scrolling 
 727             // Round up integer division to catch any "leftover" client space. 
 728             const int hVirt 
= m_targetWindow
->GetVirtualSize().GetHeight(); 
 729             m_yScrollLines 
= ( hVirt 
+ m_yScrollPixelsPerLine 
- 1 ) / m_yScrollPixelsPerLine
; 
 731             // Calculate page size i.e. number of scroll units you get on the 
 732             // current client window. 
 733             linesPerPage 
= h 
/ m_yScrollPixelsPerLine
; 
 735             // Special case. When client and virtual size are very close but 
 736             // the client is big enough, kill scrollbar. 
 737             if ((linesPerPage 
< m_yScrollLines
) && (h 
>= hVirt
)) ++linesPerPage
; 
 739             if (linesPerPage 
>= m_yScrollLines
) 
 741                 // we're big enough to not need scrolling 
 744                 m_yScrollPosition 
= 0; 
 746             else // we do need a scrollbar 
 748                 if ( linesPerPage 
< 1 ) 
 751                 // Correct position if greater than extent of canvas minus 
 752                 // the visible portion of it or if below zero 
 753                 const int posMax 
= m_yScrollLines 
- linesPerPage
; 
 754                 if ( m_yScrollPosition 
> posMax 
) 
 755                     m_yScrollPosition 
= posMax
; 
 756                 else if ( m_yScrollPosition 
< 0 ) 
 757                     m_yScrollPosition 
= 0; 
 761         m_win
->SetScrollbar(wxVERTICAL
, m_yScrollPosition
, 
 762                             linesPerPage
, m_yScrollLines
); 
 764         // The amount by which we scroll when paging 
 765         SetScrollPageSize(wxVERTICAL
, linesPerPage
); 
 768         // If a scrollbar (dis)appeared as a result of this, adjust them again. 
 772         GetTargetSize( &w
, &h 
); 
 773     } while ( (w 
!= oldw 
|| h 
!= oldh
) && (iterationCount 
< iterationMax
) ); 
 776     // Sorry, some Motif-specific code to implement a backing pixmap 
 777     // for the wxRETAINED style. Implementing a backing store can't 
 778     // be entirely generic because it relies on the wxWindowDC implementation 
 779     // to duplicate X drawing calls for the backing pixmap. 
 781     if ( m_targetWindow
->GetWindowStyle() & wxRETAINED 
) 
 783         Display
* dpy 
= XtDisplay((Widget
)m_targetWindow
->GetMainWidget()); 
 785         int totalPixelWidth 
= m_xScrollLines 
* m_xScrollPixelsPerLine
; 
 786         int totalPixelHeight 
= m_yScrollLines 
* m_yScrollPixelsPerLine
; 
 787         if (m_targetWindow
->GetBackingPixmap() && 
 788            !((m_targetWindow
->GetPixmapWidth() == totalPixelWidth
) && 
 789              (m_targetWindow
->GetPixmapHeight() == totalPixelHeight
))) 
 791             XFreePixmap (dpy
, (Pixmap
) m_targetWindow
->GetBackingPixmap()); 
 792             m_targetWindow
->SetBackingPixmap((WXPixmap
) 0); 
 795         if (!m_targetWindow
->GetBackingPixmap() && 
 796            (m_xScrollLines 
!= 0) && (m_yScrollLines 
!= 0)) 
 798             int depth 
= wxDisplayDepth(); 
 799             m_targetWindow
->SetPixmapWidth(totalPixelWidth
); 
 800             m_targetWindow
->SetPixmapHeight(totalPixelHeight
); 
 801             m_targetWindow
->SetBackingPixmap((WXPixmap
) XCreatePixmap (dpy
, RootWindow (dpy
, DefaultScreen (dpy
)), 
 802               m_targetWindow
->GetPixmapWidth(), m_targetWindow
->GetPixmapHeight(), depth
)); 
 808     if (oldXScroll 
!= m_xScrollPosition
) 
 810        if (m_xScrollingEnabled
) 
 811             m_targetWindow
->ScrollWindow( m_xScrollPixelsPerLine 
* (oldXScroll 
- m_xScrollPosition
), 0, 
 814             m_targetWindow
->Refresh(true, GetScrollRect()); 
 817     if (oldYScroll 
!= m_yScrollPosition
) 
 819         if (m_yScrollingEnabled
) 
 820             m_targetWindow
->ScrollWindow( 0, m_yScrollPixelsPerLine 
* (oldYScroll
-m_yScrollPosition
), 
 823             m_targetWindow
->Refresh(true, GetScrollRect()); 
 827 void wxScrollHelper::DoPrepareDC(wxDC
& dc
) 
 829     wxPoint pt 
= dc
.GetDeviceOrigin(); 
 831     // It may actually be correct to always query 
 832     // the m_sign from the DC here, but I leve the 
 833     // #ifdef GTK for now. 
 834     if (m_win
->GetLayoutDirection() == wxLayout_RightToLeft
) 
 835         dc
.SetDeviceOrigin( pt
.x 
+ m_xScrollPosition 
* m_xScrollPixelsPerLine
, 
 836                             pt
.y 
- m_yScrollPosition 
* m_yScrollPixelsPerLine 
); 
 839         dc
.SetDeviceOrigin( pt
.x 
- m_xScrollPosition 
* m_xScrollPixelsPerLine
, 
 840                             pt
.y 
- m_yScrollPosition 
* m_yScrollPixelsPerLine 
); 
 841     dc
.SetUserScale( m_scaleX
, m_scaleY 
); 
 844 void wxScrollHelper::SetScrollRate( int xstep
, int ystep 
) 
 846     int old_x 
= m_xScrollPixelsPerLine 
* m_xScrollPosition
; 
 847     int old_y 
= m_yScrollPixelsPerLine 
* m_yScrollPosition
; 
 849     m_xScrollPixelsPerLine 
= xstep
; 
 850     m_yScrollPixelsPerLine 
= ystep
; 
 852     int new_x 
= m_xScrollPixelsPerLine 
* m_xScrollPosition
; 
 853     int new_y 
= m_yScrollPixelsPerLine 
* m_yScrollPosition
; 
 855     m_win
->SetScrollPos( wxHORIZONTAL
, m_xScrollPosition 
); 
 856     m_win
->SetScrollPos( wxVERTICAL
, m_yScrollPosition 
); 
 857     m_targetWindow
->ScrollWindow( old_x 
- new_x
, old_y 
- new_y 
); 
 862 void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit
, int *y_unit
) const 
 865         *x_unit 
= m_xScrollPixelsPerLine
; 
 867         *y_unit 
= m_yScrollPixelsPerLine
; 
 871 int wxScrollHelper::GetScrollLines( int orient 
) const 
 873     if ( orient 
== wxHORIZONTAL 
) 
 874         return m_xScrollLines
; 
 876         return m_yScrollLines
; 
 879 int wxScrollHelper::GetScrollPageSize(int orient
) const 
 881     if ( orient 
== wxHORIZONTAL 
) 
 882         return m_xScrollLinesPerPage
; 
 884         return m_yScrollLinesPerPage
; 
 887 void wxScrollHelper::SetScrollPageSize(int orient
, int pageSize
) 
 889     if ( orient 
== wxHORIZONTAL 
) 
 890         m_xScrollLinesPerPage 
= pageSize
; 
 892         m_yScrollLinesPerPage 
= pageSize
; 
 896  * Scroll to given position (scroll position, not pixel position) 
 898 void wxScrollHelper::Scroll( int x_pos
, int y_pos 
) 
 903     if (((x_pos 
== -1) || (x_pos 
== m_xScrollPosition
)) && 
 904         ((y_pos 
== -1) || (y_pos 
== m_yScrollPosition
))) return; 
 907     GetTargetSize(&w
, &h
); 
 909     // compute new position: 
 910     int new_x 
= m_xScrollPosition
; 
 911     int new_y 
= m_yScrollPosition
; 
 913     if ((x_pos 
!= -1) && (m_xScrollPixelsPerLine
)) 
 917         // Calculate page size i.e. number of scroll units you get on the 
 918         // current client window 
 919         int noPagePositions 
= w
/m_xScrollPixelsPerLine
; 
 920         if (noPagePositions 
< 1) noPagePositions 
= 1; 
 922         // Correct position if greater than extent of canvas minus 
 923         // the visible portion of it or if below zero 
 924         new_x 
= wxMin( m_xScrollLines
-noPagePositions
, new_x 
); 
 925         new_x 
= wxMax( 0, new_x 
); 
 927     if ((y_pos 
!= -1) && (m_yScrollPixelsPerLine
)) 
 931         // Calculate page size i.e. number of scroll units you get on the 
 932         // current client window 
 933         int noPagePositions 
= h
/m_yScrollPixelsPerLine
; 
 934         if (noPagePositions 
< 1) noPagePositions 
= 1; 
 936         // Correct position if greater than extent of canvas minus 
 937         // the visible portion of it or if below zero 
 938         new_y 
= wxMin( m_yScrollLines
-noPagePositions
, new_y 
); 
 939         new_y 
= wxMax( 0, new_y 
); 
 942     if ( new_x 
== m_xScrollPosition 
&& new_y 
== m_yScrollPosition 
) 
 943         return; // nothing to do, the position didn't change 
 945     // flush all pending repaints before we change m_{x,y}ScrollPosition, as 
 946     // otherwise invalidated area could be updated incorrectly later when 
 947     // ScrollWindow() makes sure they're repainted before scrolling them 
 948     m_targetWindow
->Update(); 
 950     // update the position and scroll the window now: 
 951     if (m_xScrollPosition 
!= new_x
) 
 953         int old_x 
= m_xScrollPosition
; 
 954         m_xScrollPosition 
= new_x
; 
 955         m_win
->SetScrollPos( wxHORIZONTAL
, new_x 
); 
 956         m_targetWindow
->ScrollWindow( (old_x
-new_x
)*m_xScrollPixelsPerLine
, 0, 
 960     if (m_yScrollPosition 
!= new_y
) 
 962         int old_y 
= m_yScrollPosition
; 
 963         m_yScrollPosition 
= new_y
; 
 964         m_win
->SetScrollPos( wxVERTICAL
, new_y 
); 
 965         m_targetWindow
->ScrollWindow( 0, (old_y
-new_y
)*m_yScrollPixelsPerLine
, 
 970 void wxScrollHelper::EnableScrolling (bool x_scroll
, bool y_scroll
) 
 972     m_xScrollingEnabled 
= x_scroll
; 
 973     m_yScrollingEnabled 
= y_scroll
; 
 976 // Where the current view starts from 
 977 void wxScrollHelper::GetViewStart (int *x
, int *y
) const 
 980         *x 
= m_xScrollPosition
; 
 982         *y 
= m_yScrollPosition
; 
 985 void wxScrollHelper::DoCalcScrolledPosition(int x
, int y
, int *xx
, int *yy
) const 
 988         *xx 
= x 
- m_xScrollPosition 
* m_xScrollPixelsPerLine
; 
 990         *yy 
= y 
- m_yScrollPosition 
* m_yScrollPixelsPerLine
; 
 993 void wxScrollHelper::DoCalcUnscrolledPosition(int x
, int y
, int *xx
, int *yy
) const 
 996         *xx 
= x 
+ m_xScrollPosition 
* m_xScrollPixelsPerLine
; 
 998         *yy 
= y 
+ m_yScrollPosition 
* m_yScrollPixelsPerLine
; 
1001 // ---------------------------------------------------------------------------- 
1003 // ---------------------------------------------------------------------------- 
1005 bool wxScrollHelper::ScrollLayout() 
1007     if ( m_win
->GetSizer() && m_targetWindow 
== m_win 
) 
1009         // If we're the scroll target, take into account the 
1010         // virtual size and scrolled position of the window. 
1012         int x 
= 0, y 
= 0, w 
= 0, h 
= 0; 
1013         CalcScrolledPosition(0,0, &x
,&y
); 
1014         m_win
->GetVirtualSize(&w
, &h
); 
1015         m_win
->GetSizer()->SetDimension(x
, y
, w
, h
); 
1019     // fall back to default for LayoutConstraints 
1020     return m_win
->wxWindow::Layout(); 
1023 void wxScrollHelper::ScrollDoSetVirtualSize(int x
, int y
) 
1025     m_win
->wxWindow::DoSetVirtualSize( x
, y 
); 
1028     if (m_win
->GetAutoLayout()) 
1032 // wxWindow's GetBestVirtualSize returns the actual window size, 
1033 // whereas we want to return the virtual size 
1034 wxSize 
wxScrollHelper::ScrollGetBestVirtualSize() const 
1036     wxSize 
clientSize(m_win
->GetClientSize()); 
1037     if ( m_win
->GetSizer() ) 
1038         clientSize
.IncTo(m_win
->GetSizer()->CalcMin()); 
1043 // return the window best size from the given best virtual size 
1045 wxScrollHelper::ScrollGetWindowSizeForVirtualSize(const wxSize
& size
) const 
1047     // Only use the content to set the window size in the direction 
1048     // where there's no scrolling; otherwise we're going to get a huge 
1049     // window in the direction in which scrolling is enabled 
1051     GetScrollPixelsPerUnit(&ppuX
, &ppuY
); 
1053     wxSize minSize 
= m_win
->GetMinSize(); 
1054     if ( !minSize
.IsFullySpecified() ) 
1055         minSize 
= m_win
->GetSize(); 
1066 // ---------------------------------------------------------------------------- 
1068 // ---------------------------------------------------------------------------- 
1070 // Default OnSize resets scrollbars, if any 
1071 void wxScrollHelper::HandleOnSize(wxSizeEvent
& WXUNUSED(event
)) 
1073     if ( m_targetWindow
->GetAutoLayout() ) 
1075         wxSize size 
= m_targetWindow
->GetBestVirtualSize(); 
1077         // This will call ::Layout() and ::AdjustScrollbars() 
1078         m_win
->SetVirtualSize( size 
); 
1086 // This calls OnDraw, having adjusted the origin according to the current 
1088 void wxScrollHelper::HandleOnPaint(wxPaintEvent
& WXUNUSED(event
)) 
1090     // don't use m_targetWindow here, this is always called for ourselves 
1091     wxPaintDC 
dc(m_win
); 
1097 // kbd handling: notice that we use OnChar() and not OnKeyDown() for 
1098 // compatibility here - if we used OnKeyDown(), the programs which process 
1099 // arrows themselves in their OnChar() would never get the message and like 
1100 // this they always have the priority 
1101 void wxScrollHelper::HandleOnChar(wxKeyEvent
& event
) 
1103     int stx 
= 0, sty 
= 0,       // view origin 
1104         szx 
= 0, szy 
= 0,       // view size (total) 
1105         clix 
= 0, cliy 
= 0;     // view size (on screen) 
1107     GetViewStart(&stx
, &sty
); 
1108     GetTargetSize(&clix
, &cliy
); 
1109     m_targetWindow
->GetVirtualSize(&szx
, &szy
); 
1111     if( m_xScrollPixelsPerLine 
) 
1113         clix 
/= m_xScrollPixelsPerLine
; 
1114         szx 
/= m_xScrollPixelsPerLine
; 
1121     if( m_yScrollPixelsPerLine 
) 
1123         cliy 
/= m_yScrollPixelsPerLine
; 
1124         szy 
/= m_yScrollPixelsPerLine
; 
1132     int xScrollOld 
= m_xScrollPosition
, 
1133         yScrollOld 
= m_yScrollPosition
; 
1136     switch ( event
.GetKeyCode() ) 
1139             dsty 
= sty 
- (5 * cliy 
/ 6); 
1140             Scroll(-1, (dsty 
== -1) ? 0 : dsty
); 
1144             Scroll(-1, sty 
+ (5 * cliy 
/ 6)); 
1148             Scroll(0, event
.ControlDown() ? 0 : -1); 
1152             Scroll(szx 
- clix
, event
.ControlDown() ? szy 
- cliy 
: -1); 
1156             Scroll(-1, sty 
- 1); 
1160             Scroll(-1, sty 
+ 1); 
1164             Scroll(stx 
- 1, -1); 
1168             Scroll(stx 
+ 1, -1); 
1176     if ( m_xScrollPosition 
!= xScrollOld 
) 
1178         wxScrollWinEvent 
event(wxEVT_SCROLLWIN_THUMBTRACK
, m_xScrollPosition
, 
1180         event
.SetEventObject(m_win
); 
1181         m_win
->GetEventHandler()->ProcessEvent(event
); 
1184     if ( m_yScrollPosition 
!= yScrollOld 
) 
1186         wxScrollWinEvent 
event(wxEVT_SCROLLWIN_THUMBTRACK
, m_yScrollPosition
, 
1188         event
.SetEventObject(m_win
); 
1189         m_win
->GetEventHandler()->ProcessEvent(event
); 
1193 // ---------------------------------------------------------------------------- 
1194 // autoscroll stuff: these functions deal with sending fake scroll events when 
1195 // a captured mouse is being held outside the window 
1196 // ---------------------------------------------------------------------------- 
1198 bool wxScrollHelper::SendAutoScrollEvents(wxScrollWinEvent
& event
) const 
1200     // only send the event if the window is scrollable in this direction 
1201     wxWindow 
*win 
= (wxWindow 
*)event
.GetEventObject(); 
1202     return win
->HasScrollbar(event
.GetOrientation()); 
1205 void wxScrollHelper::StopAutoScrolling() 
1208     if ( m_timerAutoScroll 
) 
1210         delete m_timerAutoScroll
; 
1211         m_timerAutoScroll 
= (wxTimer 
*)NULL
; 
1216 void wxScrollHelper::HandleOnMouseEnter(wxMouseEvent
& event
) 
1218     StopAutoScrolling(); 
1223 void wxScrollHelper::HandleOnMouseLeave(wxMouseEvent
& event
) 
1225     // don't prevent the usual processing of the event from taking place 
1228     // when a captured mouse leave a scrolled window we start generate 
1229     // scrolling events to allow, for example, extending selection beyond the 
1230     // visible area in some controls 
1231     if ( wxWindow::GetCapture() == m_targetWindow 
) 
1233         // where is the mouse leaving? 
1235         wxPoint pt 
= event
.GetPosition(); 
1238             orient 
= wxHORIZONTAL
; 
1241         else if ( pt
.y 
< 0 ) 
1243             orient 
= wxVERTICAL
; 
1246         else // we're lower or to the right of the window 
1248             wxSize size 
= m_targetWindow
->GetClientSize(); 
1249             if ( pt
.x 
> size
.x 
) 
1251                 orient 
= wxHORIZONTAL
; 
1252                 pos 
= m_xScrollLines
; 
1254             else if ( pt
.y 
> size
.y 
) 
1256                 orient 
= wxVERTICAL
; 
1257                 pos 
= m_yScrollLines
; 
1259             else // this should be impossible 
1261                 // but seems to happen sometimes under wxMSW - maybe it's a bug 
1262                 // there but for now just ignore it 
1264                 //wxFAIL_MSG( _T("can't understand where has mouse gone") ); 
1270         // only start the auto scroll timer if the window can be scrolled in 
1272         if ( !m_targetWindow
->HasScrollbar(orient
) ) 
1276         delete m_timerAutoScroll
; 
1277         m_timerAutoScroll 
= new wxAutoScrollTimer
 
1279                                     m_targetWindow
, this, 
1280                                     pos 
== 0 ? wxEVT_SCROLLWIN_LINEUP
 
1281                                              : wxEVT_SCROLLWIN_LINEDOWN
, 
1285         m_timerAutoScroll
->Start(50); // FIXME: make configurable 
1292 #if wxUSE_MOUSEWHEEL 
1294 void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent
& event
) 
1296     m_wheelRotation 
+= event
.GetWheelRotation(); 
1297     int lines 
= m_wheelRotation 
/ event
.GetWheelDelta(); 
1298     m_wheelRotation 
-= lines 
* event
.GetWheelDelta(); 
1303         wxScrollWinEvent newEvent
; 
1305         newEvent
.SetPosition(0); 
1306         newEvent
.SetOrientation( event
.GetWheelAxis() == 0 ? wxVERTICAL 
: wxHORIZONTAL
); 
1307         newEvent
.SetEventObject(m_win
); 
1309         if (event
.IsPageScroll()) 
1312                 newEvent
.SetEventType(wxEVT_SCROLLWIN_PAGEUP
); 
1314                 newEvent
.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN
); 
1316             m_win
->GetEventHandler()->ProcessEvent(newEvent
); 
1320             lines 
*= event
.GetLinesPerAction(); 
1322                 newEvent
.SetEventType(wxEVT_SCROLLWIN_LINEUP
); 
1324                 newEvent
.SetEventType(wxEVT_SCROLLWIN_LINEDOWN
); 
1326             int times 
= abs(lines
); 
1327             for (; times 
> 0; times
--) 
1328                 m_win
->GetEventHandler()->ProcessEvent(newEvent
); 
1333 #endif // wxUSE_MOUSEWHEEL 
1335 // ---------------------------------------------------------------------------- 
1336 // wxScrolledWindow implementation 
1337 // ---------------------------------------------------------------------------- 
1339 IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow
, wxPanel
) 
1341 BEGIN_EVENT_TABLE(wxScrolledWindow
, wxPanel
) 
1342     EVT_PAINT(wxScrolledWindow::OnPaint
) 
1345 bool wxScrolledWindow::Create(wxWindow 
*parent
, 
1350                               const wxString
& name
) 
1352     m_targetWindow 
= this; 
1354     MacSetClipChildren( true ) ; 
1357     bool ok 
= wxPanel::Create(parent
, id
, pos
, size
, style
|wxHSCROLL
|wxVSCROLL
, name
); 
1362 wxScrolledWindow::~wxScrolledWindow() 
1366 void wxScrolledWindow::OnPaint(wxPaintEvent
& event
) 
1368     // the user code didn't really draw the window if we got here, so set this 
1369     // flag to try to call OnDraw() later 
1370     m_handler
->ResetDrawnFlag(); 
1376 WXLRESULT 
wxScrolledWindow::MSWWindowProc(WXUINT nMsg
, 
1380     WXLRESULT rc 
= wxPanel::MSWWindowProc(nMsg
, wParam
, lParam
); 
1383     // we need to process arrows ourselves for scrolling 
1384     if ( nMsg 
== WM_GETDLGCODE 
) 
1386         rc 
|= DLGC_WANTARROWS
;