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.
7// Created:     01/02/97
8// RCS-ID:      $Id: scrlwing.cpp 60600 2009-05-12 10:33:49Z VZ $
9// Copyright:   (c) wxWidgets team
10// Licence:     wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#ifdef __BORLANDC__
25    #pragma hdrstop
26#endif
27
28#include "wx/scrolwin.h"
29
30#ifndef WX_PRECOMP
31    #include "wx/utils.h"
32    #include "wx/panel.h"
33    #include "wx/dcclient.h"
34    #if wxUSE_TIMER
35        #include "wx/timer.h"
36    #endif
37    #include "wx/sizer.h"
38    #include "wx/settings.h"
39#endif
40
41#ifdef __WXMAC__
42#include "wx/scrolbar.h"
43#endif
44
45#include "wx/recguard.h"
46
47#ifdef __WXMSW__
48    #include <windows.h> // for DLGC_WANTARROWS
49    #include "wx/msw/winundef.h"
50#endif
51
52#ifdef __WXMOTIF__
53// For wxRETAINED implementation
54#ifdef __VMS__ //VMS's Xm.h is not (yet) compatible with C++
55               //This code switches off the compiler warnings
56# pragma message disable nosimpint
57#endif
58#include <Xm/Xm.h>
59#ifdef __VMS__
60# pragma message enable nosimpint
61#endif
62#endif
63
64/*
65    TODO PROPERTIES
66        style wxHSCROLL | wxVSCROLL
67*/
68
69// ----------------------------------------------------------------------------
70// wxScrollHelperEvtHandler: intercept the events from the window and forward
71// them to wxScrollHelper
72// ----------------------------------------------------------------------------
73
74class WXDLLEXPORT wxScrollHelperEvtHandler : public wxEvtHandler
75{
76public:
77    wxScrollHelperEvtHandler(wxScrollHelper *scrollHelper)
78    {
79        m_scrollHelper = scrollHelper;
80    }
81
82    virtual bool ProcessEvent(wxEvent& event);
83
84    void ResetDrawnFlag() { m_hasDrawnWindow = false; }
85
86private:
87    wxScrollHelper *m_scrollHelper;
88
89    bool m_hasDrawnWindow;
90
91    DECLARE_NO_COPY_CLASS(wxScrollHelperEvtHandler)
92};
93
94#if wxUSE_TIMER
95// ----------------------------------------------------------------------------
96// wxAutoScrollTimer: the timer used to generate a stream of scroll events when
97// a captured mouse is held outside the window
98// ----------------------------------------------------------------------------
99
100class wxAutoScrollTimer : public wxTimer
101{
102public:
103    wxAutoScrollTimer(wxWindow *winToScroll, wxScrollHelper *scroll,
104                      wxEventType eventTypeToSend,
105                      int pos, int orient);
106
107    virtual void Notify();
108
109private:
110    wxWindow *m_win;
111    wxScrollHelper *m_scrollHelper;
112    wxEventType m_eventType;
113    int m_pos,
114        m_orient;
115
116    DECLARE_NO_COPY_CLASS(wxAutoScrollTimer)
117};
118
119// ============================================================================
120// implementation
121// ============================================================================
122
123// ----------------------------------------------------------------------------
124// wxAutoScrollTimer
125// ----------------------------------------------------------------------------
126
127wxAutoScrollTimer::wxAutoScrollTimer(wxWindow *winToScroll,
128                                     wxScrollHelper *scroll,
129                                     wxEventType eventTypeToSend,
130                                     int pos, int orient)
131{
132    m_win = winToScroll;
133    m_scrollHelper = scroll;
134    m_eventType = eventTypeToSend;
135    m_pos = pos;
136    m_orient = orient;
137}
138
139void wxAutoScrollTimer::Notify()
140{
141    // only do all this as long as the window is capturing the mouse
142    if ( wxWindow::GetCapture() != m_win )
143    {
144        Stop();
145    }
146    else // we still capture the mouse, continue generating events
147    {
148        // first scroll the window if we are allowed to do it
149        wxScrollWinEvent event1(m_eventType, m_pos, m_orient);
150        event1.SetEventObject(m_win);
151        if ( m_scrollHelper->SendAutoScrollEvents(event1) &&
152                m_win->GetEventHandler()->ProcessEvent(event1) )
153        {
154            // and then send a pseudo mouse-move event to refresh the selection
155            wxMouseEvent event2(wxEVT_MOTION);
156            wxGetMousePosition(&event2.m_x, &event2.m_y);
157
158            // the mouse event coordinates should be client, not screen as
159            // returned by wxGetMousePosition
160            wxWindow *parentTop = m_win;
161            while ( parentTop->GetParent() )
162                parentTop = parentTop->GetParent();
163            wxPoint ptOrig = parentTop->GetPosition();
164            event2.m_x -= ptOrig.x;
165            event2.m_y -= ptOrig.y;
166
167            event2.SetEventObject(m_win);
168
169            // FIXME: we don't fill in the other members - ok?
170
171            m_win->GetEventHandler()->ProcessEvent(event2);
172        }
173        else // can't scroll further, stop
174        {
175            Stop();
176        }
177    }
178}
179#endif
180
181// ----------------------------------------------------------------------------
182// wxScrollHelperEvtHandler
183// ----------------------------------------------------------------------------
184
185bool wxScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
186{
187    wxEventType evType = event.GetEventType();
188
189    // the explanation of wxEVT_PAINT processing hack: for historic reasons
190    // there are 2 ways to process this event in classes deriving from
191    // wxScrolledWindow. The user code may
192    //
193    //  1. override wxScrolledWindow::OnDraw(dc)
194    //  2. define its own OnPaint() handler
195    //
196    // In addition, in wxUniversal wxWindow defines OnPaint() itself and
197    // always processes the draw event, so we can't just try the window
198    // OnPaint() first and call our HandleOnPaint() if it doesn't process it
199    // (the latter would never be called in wxUniversal).
200    //
201    // So the solution is to have a flag telling us whether the user code drew
202    // anything in the window. We set it to true here but reset it to false in
203    // wxScrolledWindow::OnPaint() handler (which wouldn't be called if the
204    // user code defined OnPaint() in the derived class)
205    m_hasDrawnWindow = true;
206
207    // pass it on to the real handler
208    bool processed = wxEvtHandler::ProcessEvent(event);
209
210    // always process the size events ourselves, even if the user code handles
211    // them as well, as we need to AdjustScrollbars()
212    //
213    // NB: it is important to do it after processing the event in the normal
214    //     way as HandleOnSize() may generate a wxEVT_SIZE itself if the
215    //     scrollbar[s] (dis)appear and it should be seen by the user code
216    //     after this one
217    if ( evType == wxEVT_SIZE )
218    {
219        m_scrollHelper->HandleOnSize((wxSizeEvent &)event);
220
221        return true;
222    }
223
224    if ( processed )
225    {
226        // normally, nothing more to do here - except if it was a paint event
227        // which wasn't really processed, then we'll try to call our
228        // OnDraw() below (from HandleOnPaint)
229        if ( m_hasDrawnWindow || event.IsCommandEvent() )
230        {
231            return true;
232        }
233    }
234
235    // reset the skipped flag to false as it might have been set to true in
236    // ProcessEvent() above
237    event.Skip(false);
238
239    if ( evType == wxEVT_PAINT )
240    {
241        m_scrollHelper->HandleOnPaint((wxPaintEvent &)event);
242        return true;
243    }
244
245    if ( evType == wxEVT_CHILD_FOCUS )
246    {
247        m_scrollHelper->HandleOnChildFocus((wxChildFocusEvent &)event);
248        return true;
249    }
250
251    if ( evType == wxEVT_SCROLLWIN_TOP ||
252         evType == wxEVT_SCROLLWIN_BOTTOM ||
253         evType == wxEVT_SCROLLWIN_LINEUP ||
254         evType == wxEVT_SCROLLWIN_LINEDOWN ||
255         evType == wxEVT_SCROLLWIN_PAGEUP ||
256         evType == wxEVT_SCROLLWIN_PAGEDOWN ||
257         evType == wxEVT_SCROLLWIN_THUMBTRACK ||
258         evType == wxEVT_SCROLLWIN_THUMBRELEASE )
259    {
260            m_scrollHelper->HandleOnScroll((wxScrollWinEvent &)event);
261            return !event.GetSkipped();
262    }
263
264    if ( evType == wxEVT_ENTER_WINDOW )
265    {
266        m_scrollHelper->HandleOnMouseEnter((wxMouseEvent &)event);
267    }
268    else if ( evType == wxEVT_LEAVE_WINDOW )
269    {
270        m_scrollHelper->HandleOnMouseLeave((wxMouseEvent &)event);
271    }
272#if wxUSE_MOUSEWHEEL
273    else if ( evType == wxEVT_MOUSEWHEEL )
274    {
275        m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event);
276        return true;
277    }
278#endif // wxUSE_MOUSEWHEEL
279    else if ( evType == wxEVT_CHAR )
280    {
281        m_scrollHelper->HandleOnChar((wxKeyEvent &)event);
282        return !event.GetSkipped();
283    }
284
285    return false;
286}
287
288// ----------------------------------------------------------------------------
289// wxScrollHelper construction
290// ----------------------------------------------------------------------------
291
292wxScrollHelper::wxScrollHelper(wxWindow *win)
293{
294    wxASSERT_MSG( win, _T("associated window can't be NULL in wxScrollHelper") );
295
296    m_xScrollPixelsPerLine =
297    m_yScrollPixelsPerLine =
298    m_xScrollPosition =
299    m_yScrollPosition =
300    m_xScrollLines =
301    m_yScrollLines =
302    m_xScrollLinesPerPage =
303    m_yScrollLinesPerPage = 0;
304
305    m_xScrollingEnabled =
306    m_yScrollingEnabled = true;
307
308    m_scaleX =
309    m_scaleY = 1.0;
310#if wxUSE_MOUSEWHEEL
311    m_wheelRotation = 0;
312#endif
313
314    m_win =
315    m_targetWindow = (wxWindow *)NULL;
316
317    m_timerAutoScroll = (wxTimer *)NULL;
318
319    m_handler = NULL;
320
321    m_win = win;
322
323    m_win->SetScrollHelper( this );
324
325    // by default, the associated window is also the target window
326    DoSetTargetWindow(win);
327}
328
329wxScrollHelper::~wxScrollHelper()
330{
331    StopAutoScrolling();
332
333    DeleteEvtHandler();
334}
335
336// ----------------------------------------------------------------------------
337// setting scrolling parameters
338// ----------------------------------------------------------------------------
339
340void wxScrollHelper::SetScrollbars(int pixelsPerUnitX,
341                                   int pixelsPerUnitY,
342                                   int noUnitsX,
343                                   int noUnitsY,
344                                   int xPos,
345                                   int yPos,
346                                   bool noRefresh)
347{
348    int xpos, ypos;
349
350    CalcUnscrolledPosition(xPos, yPos, &xpos, &ypos);
351    bool do_refresh =
352    (
353      (noUnitsX != 0 && m_xScrollLines == 0) ||
354      (noUnitsX < m_xScrollLines && xpos > pixelsPerUnitX * noUnitsX) ||
355
356      (noUnitsY != 0 && m_yScrollLines == 0) ||
357      (noUnitsY < m_yScrollLines && ypos > pixelsPerUnitY * noUnitsY) ||
358      (xPos != m_xScrollPosition) ||
359      (yPos != m_yScrollPosition)
360    );
361
362    m_xScrollPixelsPerLine = pixelsPerUnitX;
363    m_yScrollPixelsPerLine = pixelsPerUnitY;
364    m_xScrollPosition = xPos;
365    m_yScrollPosition = yPos;
366
367    int w = noUnitsX * pixelsPerUnitX;
368    int h = noUnitsY * pixelsPerUnitY;
369
370    // For better backward compatibility we set persisting limits
371    // here not just the size.  It makes SetScrollbars 'sticky'
372    // emulating the old non-autoscroll behaviour.
373    //   m_targetWindow->SetVirtualSizeHints( w, h );
374
375    // The above should arguably be deprecated, this however we still need.
376
377    // take care not to set 0 virtual size, 0 means that we don't have any
378    // scrollbars and hence we should use the real size instead of the virtual
379    // one which is indicated by using wxDefaultCoord
380    m_targetWindow->SetVirtualSize( w ? w : wxDefaultCoord,
381                                    h ? h : wxDefaultCoord);
382
383    if (do_refresh && !noRefresh)
384        m_targetWindow->Refresh(true, GetScrollRect());
385
386#ifndef __WXUNIVERSAL__
387    // If the target is not the same as the window with the scrollbars,
388    // then we need to update the scrollbars here, since they won't have
389    // been updated by SetVirtualSize().
390    if ( m_targetWindow != m_win )
391#endif // !__WXUNIVERSAL__
392    {
393        AdjustScrollbars();
394    }
395#ifndef __WXUNIVERSAL__
396    else
397    {
398        // otherwise this has been done by AdjustScrollbars, above
399    }
400#endif // !__WXUNIVERSAL__
401}
402
403// ----------------------------------------------------------------------------
404// [target] window handling
405// ----------------------------------------------------------------------------
406
407void wxScrollHelper::DeleteEvtHandler()
408{
409    // search for m_handler in the handler list
410    if ( m_win && m_handler )
411    {
412        if ( m_win->RemoveEventHandler(m_handler) )
413        {
414            delete m_handler;
415        }
416        //else: something is very wrong, so better [maybe] leak memory than
417        //      risk a crash because of double deletion
418
419        m_handler = NULL;
420    }
421}
422
423void wxScrollHelper::DoSetTargetWindow(wxWindow *target)
424{
425    m_targetWindow = target;
426#ifdef __WXMAC__
427    target->MacSetClipChildren( true ) ;
428#endif
429
430    // install the event handler which will intercept the events we're
431    // interested in (but only do it for our real window, not the target window
432    // which we scroll - we don't need to hijack its events)
433    if ( m_targetWindow == m_win )
434    {
435        // if we already have a handler, delete it first
436        DeleteEvtHandler();
437
438        m_handler = new wxScrollHelperEvtHandler(this);
439        m_targetWindow->PushEventHandler(m_handler);
440    }
441}
442
443void wxScrollHelper::SetTargetWindow(wxWindow *target)
444{
445    wxCHECK_RET( target, wxT("target window must not be NULL") );
446
447    if ( target == m_targetWindow )
448        return;
449
450    DoSetTargetWindow(target);
451}
452
453wxWindow *wxScrollHelper::GetTargetWindow() const
454{
455    return m_targetWindow;
456}
457
458// ----------------------------------------------------------------------------
459// scrolling implementation itself
460// ----------------------------------------------------------------------------
461
462void wxScrollHelper::HandleOnScroll(wxScrollWinEvent& event)
463{
464    int nScrollInc = CalcScrollInc(event);
465    if ( nScrollInc == 0 )
466    {
467        // can't scroll further
468        event.Skip();
469
470        return;
471    }
472
473    bool needsRefresh = false;
474
475    int dx = 0,
476        dy = 0;
477    int orient = event.GetOrientation();
478    if (orient == wxHORIZONTAL)
479    {
480       if ( m_xScrollingEnabled )
481       {
482           dx = -m_xScrollPixelsPerLine * nScrollInc;
483       }
484       else
485       {
486           needsRefresh = true;
487       }
488    }
489    else
490    {
491        if ( m_yScrollingEnabled )
492        {
493            dy = -m_yScrollPixelsPerLine * nScrollInc;
494        }
495        else
496        {
497            needsRefresh = true;
498        }
499    }
500
501    if ( !needsRefresh )
502    {
503        // flush all pending repaints before we change m_{x,y}ScrollPosition, as
504        // otherwise invalidated area could be updated incorrectly later when
505        // ScrollWindow() makes sure they're repainted before scrolling them
506#ifdef __WXMAC__
507        // wxWindowMac is taking care of making sure the update area is correctly
508        // set up, while not forcing an immediate redraw
509#else
510        m_targetWindow->Update();
511#endif
512    }
513
514    if (orient == wxHORIZONTAL)
515    {
516        m_xScrollPosition += nScrollInc;
517        m_win->SetScrollPos(wxHORIZONTAL, m_xScrollPosition);
518    }
519    else
520    {
521        m_yScrollPosition += nScrollInc;
522        m_win->SetScrollPos(wxVERTICAL, m_yScrollPosition);
523    }
524
525    if ( needsRefresh )
526    {
527        m_targetWindow->Refresh(true, GetScrollRect());
528    }
529    else
530    {
531        m_targetWindow->ScrollWindow(dx, dy, GetScrollRect());
532    }
533}
534
535int wxScrollHelper::CalcScrollInc(wxScrollWinEvent& event)
536{
537    int pos = event.GetPosition();
538    int orient = event.GetOrientation();
539
540    int nScrollInc = 0;
541    if (event.GetEventType() == wxEVT_SCROLLWIN_TOP)
542    {
543            if (orient == wxHORIZONTAL)
544                nScrollInc = - m_xScrollPosition;
545            else
546                nScrollInc = - m_yScrollPosition;
547    } else
548    if (event.GetEventType() == wxEVT_SCROLLWIN_BOTTOM)
549    {
550            if (orient == wxHORIZONTAL)
551                nScrollInc = m_xScrollLines - m_xScrollPosition;
552            else
553                nScrollInc = m_yScrollLines - m_yScrollPosition;
554    } else
555    if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP)
556    {
557            nScrollInc = -1;
558    } else
559    if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN)
560    {
561            nScrollInc = 1;
562    } else
563    if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEUP)
564    {
565            if (orient == wxHORIZONTAL)
566                nScrollInc = -GetScrollPageSize(wxHORIZONTAL);
567            else
568                nScrollInc = -GetScrollPageSize(wxVERTICAL);
569    } else
570    if (event.GetEventType() == wxEVT_SCROLLWIN_PAGEDOWN)
571    {
572            if (orient == wxHORIZONTAL)
573                nScrollInc = GetScrollPageSize(wxHORIZONTAL);
574            else
575                nScrollInc = GetScrollPageSize(wxVERTICAL);
576    } else
577    if ((event.GetEventType() == wxEVT_SCROLLWIN_THUMBTRACK) ||
578        (event.GetEventType() == wxEVT_SCROLLWIN_THUMBRELEASE))
579    {
580            if (orient == wxHORIZONTAL)
581                nScrollInc = pos - m_xScrollPosition;
582            else
583                nScrollInc = pos - m_yScrollPosition;
584    }
585
586    if (orient == wxHORIZONTAL)
587    {
588        if (m_xScrollPixelsPerLine > 0)
589        {
590            if ( m_xScrollPosition + nScrollInc < 0 )
591            {
592                // As -ve as we can go
593                nScrollInc = -m_xScrollPosition;
594            }
595            else // check for the other bound
596            {
597                const int posMax = m_xScrollLines - m_xScrollLinesPerPage;
598                if ( m_xScrollPosition + nScrollInc > posMax )
599                {
600                    // As +ve as we can go
601                    nScrollInc = posMax - m_xScrollPosition;
602                }
603            }
604        }
605        else
606            m_targetWindow->Refresh(true, GetScrollRect());
607    }
608    else
609    {
610        if ( m_yScrollPixelsPerLine > 0 )
611        {
612            if ( m_yScrollPosition + nScrollInc < 0 )
613            {
614                // As -ve as we can go
615                nScrollInc = -m_yScrollPosition;
616            }
617            else // check for the other bound
618            {
619                const int posMax = m_yScrollLines - m_yScrollLinesPerPage;
620                if ( m_yScrollPosition + nScrollInc > posMax )
621                {
622                    // As +ve as we can go
623                    nScrollInc = posMax - m_yScrollPosition;
624                }
625            }
626        }
627        else
628        {
629            // VZ: why do we do this? (FIXME)
630            m_targetWindow->Refresh(true, GetScrollRect());
631        }
632    }
633
634    return nScrollInc;
635}
636
637// Adjust the scrollbars - new version.
638void wxScrollHelper::AdjustScrollbars()
639{
640    static wxRecursionGuardFlag s_flagReentrancy;
641    wxRecursionGuard guard(s_flagReentrancy);
642    if ( guard.IsInside() )
643    {
644        // don't reenter AdjustScrollbars() while another call to
645        // AdjustScrollbars() is in progress because this may lead to calling
646        // ScrollWindow() twice and this can really happen under MSW if
647        // SetScrollbar() call below adds or removes the scrollbar which
648        // changes the window size and hence results in another
649        // AdjustScrollbars() call
650        return;
651    }
652
653    int w = 0, h = 0;
654    int oldw, oldh;
655
656    int oldXScroll = m_xScrollPosition;
657    int oldYScroll = m_yScrollPosition;
658
659    // VZ: at least under Windows this loop is useless because when scrollbars
660    //     [dis]appear we get a WM_SIZE resulting in another call to
661    //     AdjustScrollbars() anyhow. As it doesn't seem to do any harm I leave
662    //     it here for now but it would be better to ensure that all ports
663    //     generate EVT_SIZE when scrollbars [dis]appear, emulating it if
664    //     necessary, and remove it later
665    // JACS: Stop potential infinite loop by limiting number of iterations
666    int iterationCount = 0;
667    const int iterationMax = 5;
668    do
669    {
670        iterationCount ++;
671
672        GetTargetSize(&w, 0);
673
674        // scroll lines per page: if 0, no scrolling is needed
675        int linesPerPage;
676
677        if ( m_xScrollPixelsPerLine == 0 )
678        {
679            // scrolling is disabled
680            m_xScrollLines = 0;
681            m_xScrollPosition = 0;
682            linesPerPage = 0;
683        }
684        else // might need scrolling
685        {
686            // Round up integer division to catch any "leftover" client space.
687            const int wVirt = m_targetWindow->GetVirtualSize().GetWidth();
688            m_xScrollLines = (wVirt + m_xScrollPixelsPerLine - 1) / m_xScrollPixelsPerLine;
689
690            // Calculate page size i.e. number of scroll units you get on the
691            // current client window.
692            linesPerPage = w / m_xScrollPixelsPerLine;
693
694            // Special case. When client and virtual size are very close but
695            // the client is big enough, kill scrollbar.
696            if ((linesPerPage < m_xScrollLines) && (w >= wVirt)) ++linesPerPage;
697
698            if (linesPerPage >= m_xScrollLines)
699            {
700                // we're big enough to not need scrolling
701                linesPerPage =
702                m_xScrollLines =
703                m_xScrollPosition = 0;
704            }
705            else // we do need a scrollbar
706            {
707                if ( linesPerPage < 1 )
708                    linesPerPage = 1;
709
710                // Correct position if greater than extent of canvas minus
711                // the visible portion of it or if below zero
712                const int posMax = m_xScrollLines - linesPerPage;
713                if ( m_xScrollPosition > posMax )
714                    m_xScrollPosition = posMax;
715                else if ( m_xScrollPosition < 0 )
716                    m_xScrollPosition = 0;
717            }
718        }
719
720        m_win->SetScrollbar(wxHORIZONTAL, m_xScrollPosition,
721                            linesPerPage, m_xScrollLines);
722
723        // The amount by which we scroll when paging
724        SetScrollPageSize(wxHORIZONTAL, linesPerPage);
725
726        GetTargetSize(0, &h);
727
728        if ( m_yScrollPixelsPerLine == 0 )
729        {
730            // scrolling is disabled
731            m_yScrollLines = 0;
732            m_yScrollPosition = 0;
733            linesPerPage = 0;
734        }
735        else // might need scrolling
736        {
737            // Round up integer division to catch any "leftover" client space.
738            const int hVirt = m_targetWindow->GetVirtualSize().GetHeight();
739            m_yScrollLines = ( hVirt + m_yScrollPixelsPerLine - 1 ) / m_yScrollPixelsPerLine;
740
741            // Calculate page size i.e. number of scroll units you get on the
742            // current client window.
743            linesPerPage = h / m_yScrollPixelsPerLine;
744
745            // Special case. When client and virtual size are very close but
746            // the client is big enough, kill scrollbar.
747            if ((linesPerPage < m_yScrollLines) && (h >= hVirt)) ++linesPerPage;
748
749            if (linesPerPage >= m_yScrollLines)
750            {
751                // we're big enough to not need scrolling
752                linesPerPage =
753                m_yScrollLines =
754                m_yScrollPosition = 0;
755            }
756            else // we do need a scrollbar
757            {
758                if ( linesPerPage < 1 )
759                    linesPerPage = 1;
760
761                // Correct position if greater than extent of canvas minus
762                // the visible portion of it or if below zero
763                const int posMax = m_yScrollLines - linesPerPage;
764                if ( m_yScrollPosition > posMax )
765                    m_yScrollPosition = posMax;
766                else if ( m_yScrollPosition < 0 )
767                    m_yScrollPosition = 0;
768            }
769        }
770
771        m_win->SetScrollbar(wxVERTICAL, m_yScrollPosition,
772                            linesPerPage, m_yScrollLines);
773
774        // The amount by which we scroll when paging
775        SetScrollPageSize(wxVERTICAL, linesPerPage);
776
777
778        // If a scrollbar (dis)appeared as a result of this, adjust them again.
779        oldw = w;
780        oldh = h;
781
782        GetTargetSize( &w, &h );
783    } while ( (w != oldw || h != oldh) && (iterationCount < iterationMax) );
784
785#ifdef __WXMOTIF__
786    // Sorry, some Motif-specific code to implement a backing pixmap
787    // for the wxRETAINED style. Implementing a backing store can't
788    // be entirely generic because it relies on the wxWindowDC implementation
789    // to duplicate X drawing calls for the backing pixmap.
790
791    if ( m_targetWindow->GetWindowStyle() & wxRETAINED )
792    {
793        Display* dpy = XtDisplay((Widget)m_targetWindow->GetMainWidget());
794
795        int totalPixelWidth = m_xScrollLines * m_xScrollPixelsPerLine;
796        int totalPixelHeight = m_yScrollLines * m_yScrollPixelsPerLine;
797        if (m_targetWindow->GetBackingPixmap() &&
798           !((m_targetWindow->GetPixmapWidth() == totalPixelWidth) &&
799             (m_targetWindow->GetPixmapHeight() == totalPixelHeight)))
800        {
801            XFreePixmap (dpy, (Pixmap) m_targetWindow->GetBackingPixmap());
802            m_targetWindow->SetBackingPixmap((WXPixmap) 0);
803        }
804
805        if (!m_targetWindow->GetBackingPixmap() &&
806           (m_xScrollLines != 0) && (m_yScrollLines != 0))
807        {
808            int depth = wxDisplayDepth();
809            m_targetWindow->SetPixmapWidth(totalPixelWidth);
810            m_targetWindow->SetPixmapHeight(totalPixelHeight);
811            m_targetWindow->SetBackingPixmap((WXPixmap) XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
812              m_targetWindow->GetPixmapWidth(), m_targetWindow->GetPixmapHeight(), depth));
813        }
814
815    }
816#endif // Motif
817
818    if (oldXScroll != m_xScrollPosition)
819    {
820       if (m_xScrollingEnabled)
821            m_targetWindow->ScrollWindow( m_xScrollPixelsPerLine * (oldXScroll - m_xScrollPosition), 0,
822                                          GetScrollRect() );
823       else
824            m_targetWindow->Refresh(true, GetScrollRect());
825    }
826
827    if (oldYScroll != m_yScrollPosition)
828    {
829        if (m_yScrollingEnabled)
830            m_targetWindow->ScrollWindow( 0, m_yScrollPixelsPerLine * (oldYScroll-m_yScrollPosition),
831                                          GetScrollRect() );
832        else
833            m_targetWindow->Refresh(true, GetScrollRect());
834    }
835}
836
837void wxScrollHelper::DoPrepareDC(wxDC& dc)
838{
839    wxPoint pt = dc.GetDeviceOrigin();
840#ifdef __WXGTK__
841    // It may actually be correct to always query
842    // the m_sign from the DC here, but I leve the
843    // #ifdef GTK for now.
844    if (m_win->GetLayoutDirection() == wxLayout_RightToLeft)
845        dc.SetDeviceOrigin( pt.x + m_xScrollPosition * m_xScrollPixelsPerLine,
846                            pt.y - m_yScrollPosition * m_yScrollPixelsPerLine );
847    else
848#endif
849        dc.SetDeviceOrigin( pt.x - m_xScrollPosition * m_xScrollPixelsPerLine,
850                            pt.y - m_yScrollPosition * m_yScrollPixelsPerLine );
851    dc.SetUserScale( m_scaleX, m_scaleY );
852}
853
854void wxScrollHelper::SetScrollRate( int xstep, int ystep )
855{
856    int old_x = m_xScrollPixelsPerLine * m_xScrollPosition;
857    int old_y = m_yScrollPixelsPerLine * m_yScrollPosition;
858
859    m_xScrollPixelsPerLine = xstep;
860    m_yScrollPixelsPerLine = ystep;
861
862    int new_x = m_xScrollPixelsPerLine * m_xScrollPosition;
863    int new_y = m_yScrollPixelsPerLine * m_yScrollPosition;
864
865    m_win->SetScrollPos( wxHORIZONTAL, m_xScrollPosition );
866    m_win->SetScrollPos( wxVERTICAL, m_yScrollPosition );
867    m_targetWindow->ScrollWindow( old_x - new_x, old_y - new_y );
868
869    AdjustScrollbars();
870}
871
872void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const
873{
874    if ( x_unit )
875        *x_unit = m_xScrollPixelsPerLine;
876    if ( y_unit )
877        *y_unit = m_yScrollPixelsPerLine;
878}
879
880
881int wxScrollHelper::GetScrollLines( int orient ) const
882{
883    if ( orient == wxHORIZONTAL )
884        return m_xScrollLines;
885    else
886        return m_yScrollLines;
887}
888
889int wxScrollHelper::GetScrollPageSize(int orient) const
890{
891    if ( orient == wxHORIZONTAL )
892        return m_xScrollLinesPerPage;
893    else
894        return m_yScrollLinesPerPage;
895}
896
897void wxScrollHelper::SetScrollPageSize(int orient, int pageSize)
898{
899    if ( orient == wxHORIZONTAL )
900        m_xScrollLinesPerPage = pageSize;
901    else
902        m_yScrollLinesPerPage = pageSize;
903}
904
905/*
906 * Scroll to given position (scroll position, not pixel position)
907 */
908void wxScrollHelper::Scroll( int x_pos, int y_pos )
909{
910    if (!m_targetWindow)
911        return;
912
913    if (((x_pos == -1) || (x_pos == m_xScrollPosition)) &&
914        ((y_pos == -1) || (y_pos == m_yScrollPosition))) return;
915
916    int w = 0, h = 0;
917    GetTargetSize(&w, &h);
918
919    // compute new position:
920    int new_x = m_xScrollPosition;
921    int new_y = m_yScrollPosition;
922
923    if ((x_pos != -1) && (m_xScrollPixelsPerLine))
924    {
925        new_x = x_pos;
926
927        // Calculate page size i.e. number of scroll units you get on the
928        // current client window
929        int noPagePositions = w/m_xScrollPixelsPerLine;
930        if (noPagePositions < 1) noPagePositions = 1;
931
932        // Correct position if greater than extent of canvas minus
933        // the visible portion of it or if below zero
934        new_x = wxMin( m_xScrollLines-noPagePositions, new_x );
935        new_x = wxMax( 0, new_x );
936    }
937    if ((y_pos != -1) && (m_yScrollPixelsPerLine))
938    {
939        new_y = y_pos;
940
941        // Calculate page size i.e. number of scroll units you get on the
942        // current client window
943        int noPagePositions = h/m_yScrollPixelsPerLine;
944        if (noPagePositions < 1) noPagePositions = 1;
945
946        // Correct position if greater than extent of canvas minus
947        // the visible portion of it or if below zero
948        new_y = wxMin( m_yScrollLines-noPagePositions, new_y );
949        new_y = wxMax( 0, new_y );
950    }
951
952    if ( new_x == m_xScrollPosition && new_y == m_yScrollPosition )
953        return; // nothing to do, the position didn't change
954
955    // flush all pending repaints before we change m_{x,y}ScrollPosition, as
956    // otherwise invalidated area could be updated incorrectly later when
957    // ScrollWindow() makes sure they're repainted before scrolling them
958    m_targetWindow->Update();
959
960    // update the position and scroll the window now:
961    if (m_xScrollPosition != new_x)
962    {
963        int old_x = m_xScrollPosition;
964        m_xScrollPosition = new_x;
965        m_win->SetScrollPos( wxHORIZONTAL, new_x );
966        m_targetWindow->ScrollWindow( (old_x-new_x)*m_xScrollPixelsPerLine, 0,
967                                      GetScrollRect() );
968    }
969
970    if (m_yScrollPosition != new_y)
971    {
972        int old_y = m_yScrollPosition;
973        m_yScrollPosition = new_y;
974        m_win->SetScrollPos( wxVERTICAL, new_y );
975        m_targetWindow->ScrollWindow( 0, (old_y-new_y)*m_yScrollPixelsPerLine,
976                                      GetScrollRect() );
977    }
978}
979
980void wxScrollHelper::EnableScrolling (bool x_scroll, bool y_scroll)
981{
982    m_xScrollingEnabled = x_scroll;
983    m_yScrollingEnabled = y_scroll;
984}
985
986// Where the current view starts from
987void wxScrollHelper::GetViewStart (int *x, int *y) const
988{
989    if ( x )
990        *x = m_xScrollPosition;
991    if ( y )
992        *y = m_yScrollPosition;
993}
994
995void wxScrollHelper::DoCalcScrolledPosition(int x, int y, int *xx, int *yy) const
996{
997    if ( xx )
998        *xx = x - m_xScrollPosition * m_xScrollPixelsPerLine;
999    if ( yy )
1000        *yy = y - m_yScrollPosition * m_yScrollPixelsPerLine;
1001}
1002
1003void wxScrollHelper::DoCalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
1004{
1005    if ( xx )
1006        *xx = x + m_xScrollPosition * m_xScrollPixelsPerLine;
1007    if ( yy )
1008        *yy = y + m_yScrollPosition * m_yScrollPixelsPerLine;
1009}
1010
1011// ----------------------------------------------------------------------------
1012// geometry
1013// ----------------------------------------------------------------------------
1014
1015bool wxScrollHelper::ScrollLayout()
1016{
1017    if ( m_win->GetSizer() && m_targetWindow == m_win )
1018    {
1019        // If we're the scroll target, take into account the
1020        // virtual size and scrolled position of the window.
1021
1022        int x = 0, y = 0, w = 0, h = 0;
1023        CalcScrolledPosition(0,0, &x,&y);
1024        m_win->GetVirtualSize(&w, &h);
1025        m_win->GetSizer()->SetDimension(x, y, w, h);
1026        return true;
1027    }
1028
1029    // fall back to default for LayoutConstraints
1030    return m_win->wxWindow::Layout();
1031}
1032
1033void wxScrollHelper::ScrollDoSetVirtualSize(int x, int y)
1034{
1035    m_win->wxWindow::DoSetVirtualSize( x, y );
1036    AdjustScrollbars();
1037
1038    if (m_win->GetAutoLayout())
1039        m_win->Layout();
1040}
1041
1042// wxWindow's GetBestVirtualSize returns the actual window size,
1043// whereas we want to return the virtual size
1044wxSize wxScrollHelper::ScrollGetBestVirtualSize() const
1045{
1046    wxSize clientSize(m_win->GetClientSize());
1047    if ( m_win->GetSizer() )
1048        clientSize.IncTo(m_win->GetSizer()->CalcMin());
1049
1050    return clientSize;
1051}
1052
1053// return the window best size from the given best virtual size
1054wxSize
1055wxScrollHelper::ScrollGetWindowSizeForVirtualSize(const wxSize& size) const
1056{
1057    // Only use the content to set the window size in the direction
1058    // where there's no scrolling; otherwise we're going to get a huge
1059    // window in the direction in which scrolling is enabled
1060    int ppuX, ppuY;
1061    GetScrollPixelsPerUnit(&ppuX, &ppuY);
1062
1063    wxSize minSize = m_win->GetMinSize();
1064
1065    wxSize best(size);
1066    if (ppuX > 0)
1067        best.x = minSize.x + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
1068    if (ppuY > 0)
1069        best.y = minSize.y + wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
1070
1071    return best;
1072}
1073
1074// ----------------------------------------------------------------------------
1075// event handlers
1076// ----------------------------------------------------------------------------
1077
1078// Default OnSize resets scrollbars, if any
1079void wxScrollHelper::HandleOnSize(wxSizeEvent& WXUNUSED(event))
1080{
1081    if ( m_targetWindow->GetAutoLayout() )
1082    {
1083        wxSize size = m_targetWindow->GetBestVirtualSize();
1084
1085        // This will call ::Layout() and ::AdjustScrollbars()
1086        m_win->SetVirtualSize( size );
1087    }
1088    else
1089    {
1090        AdjustScrollbars();
1091    }
1092}
1093
1094// This calls OnDraw, having adjusted the origin according to the current
1095// scroll position
1096void wxScrollHelper::HandleOnPaint(wxPaintEvent& WXUNUSED(event))
1097{
1098    // don't use m_targetWindow here, this is always called for ourselves
1099    wxPaintDC dc(m_win);
1100    DoPrepareDC(dc);
1101
1102    OnDraw(dc);
1103}
1104
1105// kbd handling: notice that we use OnChar() and not OnKeyDown() for
1106// compatibility here - if we used OnKeyDown(), the programs which process
1107// arrows themselves in their OnChar() would never get the message and like
1108// this they always have the priority
1109void wxScrollHelper::HandleOnChar(wxKeyEvent& event)
1110{
1111    int stx = 0, sty = 0,       // view origin
1112        szx = 0, szy = 0,       // view size (total)
1113        clix = 0, cliy = 0;     // view size (on screen)
1114
1115    GetViewStart(&stx, &sty);
1116    GetTargetSize(&clix, &cliy);
1117    m_targetWindow->GetVirtualSize(&szx, &szy);
1118
1119    if( m_xScrollPixelsPerLine )
1120    {
1121        clix /= m_xScrollPixelsPerLine;
1122        szx /= m_xScrollPixelsPerLine;
1123    }
1124    else
1125    {
1126        clix = 0;
1127        szx = -1;
1128    }
1129    if( m_yScrollPixelsPerLine )
1130    {
1131        cliy /= m_yScrollPixelsPerLine;
1132        szy /= m_yScrollPixelsPerLine;
1133    }
1134    else
1135    {
1136        cliy = 0;
1137        szy = -1;
1138    }
1139
1140    int xScrollOld = m_xScrollPosition,
1141        yScrollOld = m_yScrollPosition;
1142
1143    int dsty;
1144    switch ( event.GetKeyCode() )
1145    {
1146        case WXK_PAGEUP:
1147            dsty = sty - (5 * cliy / 6);
1148            Scroll(-1, (dsty == -1) ? 0 : dsty);
1149            break;
1150
1151        case WXK_PAGEDOWN:
1152            Scroll(-1, sty + (5 * cliy / 6));
1153            break;
1154
1155        case WXK_HOME:
1156            Scroll(0, event.ControlDown() ? 0 : -1);
1157            break;
1158
1159        case WXK_END:
1160            Scroll(szx - clix, event.ControlDown() ? szy - cliy : -1);
1161            break;
1162
1163        case WXK_UP:
1164            Scroll(-1, sty - 1);
1165            break;
1166
1167        case WXK_DOWN:
1168            Scroll(-1, sty + 1);
1169            break;
1170
1171        case WXK_LEFT:
1172            Scroll(stx - 1, -1);
1173            break;
1174
1175        case WXK_RIGHT:
1176            Scroll(stx + 1, -1);
1177            break;
1178
1179        default:
1180            // not for us
1181            event.Skip();
1182    }
1183
1184    if ( m_xScrollPosition != xScrollOld )
1185    {
1186        wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_xScrollPosition,
1187                               wxHORIZONTAL);
1188        event.SetEventObject(m_win);
1189        m_win->GetEventHandler()->ProcessEvent(event);
1190    }
1191
1192    if ( m_yScrollPosition != yScrollOld )
1193    {
1194        wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBTRACK, m_yScrollPosition,
1195                               wxVERTICAL);
1196        event.SetEventObject(m_win);
1197        m_win->GetEventHandler()->ProcessEvent(event);
1198    }
1199}
1200
1201// ----------------------------------------------------------------------------
1202// autoscroll stuff: these functions deal with sending fake scroll events when
1203// a captured mouse is being held outside the window
1204// ----------------------------------------------------------------------------
1205
1206bool wxScrollHelper::SendAutoScrollEvents(wxScrollWinEvent& event) const
1207{
1208    // only send the event if the window is scrollable in this direction
1209    wxWindow *win = (wxWindow *)event.GetEventObject();
1210    return win->HasScrollbar(event.GetOrientation());
1211}
1212
1213void wxScrollHelper::StopAutoScrolling()
1214{
1215#if wxUSE_TIMER
1216    if ( m_timerAutoScroll )
1217    {
1218        delete m_timerAutoScroll;
1219        m_timerAutoScroll = (wxTimer *)NULL;
1220    }
1221#endif
1222}
1223
1224void wxScrollHelper::HandleOnMouseEnter(wxMouseEvent& event)
1225{
1226    StopAutoScrolling();
1227
1228    event.Skip();
1229}
1230
1231void wxScrollHelper::HandleOnMouseLeave(wxMouseEvent& event)
1232{
1233    // don't prevent the usual processing of the event from taking place
1234    event.Skip();
1235
1236    // when a captured mouse leave a scrolled window we start generate
1237    // scrolling events to allow, for example, extending selection beyond the
1238    // visible area in some controls
1239    if ( wxWindow::GetCapture() == m_targetWindow )
1240    {
1241        // where is the mouse leaving?
1242        int pos, orient;
1243        wxPoint pt = event.GetPosition();
1244        if ( pt.x < 0 )
1245        {
1246            orient = wxHORIZONTAL;
1247            pos = 0;
1248        }
1249        else if ( pt.y < 0 )
1250        {
1251            orient = wxVERTICAL;
1252            pos = 0;
1253        }
1254        else // we're lower or to the right of the window
1255        {
1256            wxSize size = m_targetWindow->GetClientSize();
1257            if ( pt.x > size.x )
1258            {
1259                orient = wxHORIZONTAL;
1260                pos = m_xScrollLines;
1261            }
1262            else if ( pt.y > size.y )
1263            {
1264                orient = wxVERTICAL;
1265                pos = m_yScrollLines;
1266            }
1267            else // this should be impossible
1268            {
1269                // but seems to happen sometimes under wxMSW - maybe it's a bug
1270                // there but for now just ignore it
1271
1272                //wxFAIL_MSG( _T("can't understand where has mouse gone") );
1273
1274                return;
1275            }
1276        }
1277
1278        // only start the auto scroll timer if the window can be scrolled in
1279        // this direction
1280        if ( !m_targetWindow->HasScrollbar(orient) )
1281            return;
1282
1283#if wxUSE_TIMER
1284        delete m_timerAutoScroll;
1285        m_timerAutoScroll = new wxAutoScrollTimer
1286                                (
1287                                    m_targetWindow, this,
1288                                    pos == 0 ? wxEVT_SCROLLWIN_LINEUP
1289                                             : wxEVT_SCROLLWIN_LINEDOWN,
1290                                    pos,
1291                                    orient
1292                                );
1293        m_timerAutoScroll->Start(50); // FIXME: make configurable
1294#else
1295        wxUnusedVar(pos);
1296#endif
1297    }
1298}
1299
1300#if wxUSE_MOUSEWHEEL
1301
1302void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event)
1303{
1304    m_wheelRotation += event.GetWheelRotation();
1305    int lines = m_wheelRotation / event.GetWheelDelta();
1306    m_wheelRotation -= lines * event.GetWheelDelta();
1307
1308    if (lines != 0)
1309    {
1310
1311        wxScrollWinEvent newEvent;
1312
1313        newEvent.SetPosition(0);
1314        newEvent.SetOrientation(wxVERTICAL);
1315        newEvent.SetEventObject(m_win);
1316
1317        if (event.IsPageScroll())
1318        {
1319            if (lines > 0)
1320                newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEUP);
1321            else
1322                newEvent.SetEventType(wxEVT_SCROLLWIN_PAGEDOWN);
1323
1324            m_win->GetEventHandler()->ProcessEvent(newEvent);
1325        }
1326        else
1327        {
1328            lines *= event.GetLinesPerAction();
1329            if (lines > 0)
1330                newEvent.SetEventType(wxEVT_SCROLLWIN_LINEUP);
1331            else
1332                newEvent.SetEventType(wxEVT_SCROLLWIN_LINEDOWN);
1333
1334            int times = abs(lines);
1335            for (; times > 0; times--)
1336                m_win->GetEventHandler()->ProcessEvent(newEvent);
1337        }
1338    }
1339}
1340
1341#endif // wxUSE_MOUSEWHEEL
1342
1343void wxScrollHelper::HandleOnChildFocus(wxChildFocusEvent& event)
1344{
1345    // this event should be processed by all windows in parenthood chain,
1346    // e.g. so that nested wxScrolledWindows work correctly
1347    event.Skip();
1348
1349    // find the immediate child under which the window receiving focus is:
1350    wxWindow *win = event.GetWindow();
1351
1352    if ( win == m_targetWindow )
1353        return; // nothing to do
1354
1355#ifdef __WXMAC__
1356    if (wxDynamicCast(win, wxScrollBar))
1357        return;
1358#endif
1359
1360    // Fixing ticket: http://trac.wxwidgets.org/ticket/9563
1361    // When a child inside a wxControlContainer receives a focus, the
1362    // wxControlContainer generates an artificial wxChildFocusEvent for
1363    // itself, telling its parent that 'it' received the focus. The effect is
1364    // that this->HandleOnChildFocus is called twice, first with the
1365    // artificial wxChildFocusEvent and then with the original event.  We need
1366    // to ignore the artificial event here or otherwise HandleOnChildFocus
1367    // would first scroll the target window to make the entire
1368    // wxControlContainer visible and immediately afterwards scroll the target
1369    // window again to make the child widget visible. This leads to ugly
1370    // flickering when using nested wxPanels/wxScrolledWindows.
1371    //
1372    // Ignore this event if 'win' is derived from wxControlContainer AND its
1373    // parent is the m_targetWindow AND 'win' is not actually reciving the
1374    // focus (win != FindFocus).  TODO: This affects all wxControlContainer
1375    // objects, but wxControlContainer is not part of the wxWidgets RTTI and
1376    // so wxDynamicCast(win, wxControlContainer) does not compile.  Find a way
1377    // to determine if 'win' derives from wxControlContainer. Until then,
1378    // testing if 'win' derives from wxPanel will probably get >90% of all
1379    // cases.
1380
1381    wxWindow *actual_focus=wxWindow::FindFocus();
1382    if (win != actual_focus &&
1383        wxDynamicCast(win, wxPanel) != 0 &&
1384        win->GetParent() == m_targetWindow)
1385        // if win is a wxPanel and receives the focus, it should not be
1386        // scrolled into view
1387        return;
1388
1389    const wxRect viewRect(m_targetWindow->GetClientRect());
1390
1391    // For composite controls such as wxComboCtrl we should try to fit the
1392    // entire control inside the visible area of the target window, not just
1393    // the focused child of the control. Otherwise we'd make only the textctrl
1394    // part of a wxComboCtrl visible and the button would still be outside the
1395    // scrolled area.  But do so only if the parent fits *entirely* inside the
1396    // scrolled window. In other situations, such as nested wxPanel or
1397    // wxScrolledWindows, the parent might be way to big to fit inside the
1398    // scrolled window. If that is the case, then make only the focused window
1399    // visible
1400    if ( win->GetParent() != m_targetWindow)
1401    {
1402        wxWindow *parent=win->GetParent();
1403        wxSize parent_size=parent->GetSize();
1404        if (parent_size.GetWidth() <= viewRect.GetWidth() &&
1405            parent_size.GetHeight() <= viewRect.GetHeight())
1406            // make the immediate parent visible instead of the focused control
1407            win=parent;
1408    }
1409
1410    // make win position relative to the m_targetWindow viewing area instead of
1411    // its parent
1412    const wxRect
1413        winRect(m_targetWindow->ScreenToClient(win->GetScreenPosition()),
1414                win->GetSize());
1415
1416    // check if it's fully visible
1417    if ( viewRect.Contains(winRect) )
1418    {
1419        // it is, nothing to do
1420        return;
1421    }
1422
1423    // check if we can make it fully visible: this is only possible if it's not
1424    // larger than our view area
1425    if ( winRect.GetWidth() > viewRect.GetWidth() ||
1426            winRect.GetHeight() > viewRect.GetHeight() )
1427    {
1428        // we can't make it fit so avoid scrolling it at all, this is only
1429        // going to be confusing and not helpful
1430        return;
1431    }
1432
1433
1434    // do make the window fit inside the view area by scrolling to it
1435    int stepx, stepy;
1436    GetScrollPixelsPerUnit(&stepx, &stepy);
1437
1438    int startx, starty;
1439    GetViewStart(&startx, &starty);
1440
1441    // first in vertical direction:
1442    if ( stepy > 0 )
1443    {
1444        int diff = 0;
1445
1446        if ( winRect.GetTop() < 0 )
1447        {
1448            diff = winRect.GetTop();
1449        }
1450        else if ( winRect.GetBottom() > viewRect.GetHeight() )
1451        {
1452            diff = winRect.GetBottom() - viewRect.GetHeight() + 1;
1453            // round up to next scroll step if we can't get exact position,
1454            // so that the window is fully visible:
1455            diff += stepy - 1;
1456        }
1457
1458        starty = (starty * stepy + diff) / stepy;
1459    }
1460
1461    // then horizontal:
1462    if ( stepx > 0 )
1463    {
1464        int diff = 0;
1465
1466        if ( winRect.GetLeft() < 0 )
1467        {
1468            diff = winRect.GetLeft();
1469        }
1470        else if ( winRect.GetRight() > viewRect.GetWidth() )
1471        {
1472            diff = winRect.GetRight() - viewRect.GetWidth() + 1;
1473            // round up to next scroll step if we can't get exact position,
1474            // so that the window is fully visible:
1475            diff += stepx - 1;
1476        }
1477
1478        startx = (startx * stepx + diff) / stepx;
1479    }
1480
1481    Scroll(startx, starty);
1482}
1483
1484// ----------------------------------------------------------------------------
1485// wxScrolledWindow implementation
1486// ----------------------------------------------------------------------------
1487
1488IMPLEMENT_DYNAMIC_CLASS(wxScrolledWindow, wxPanel)
1489
1490BEGIN_EVENT_TABLE(wxScrolledWindow, wxPanel)
1491    EVT_PAINT(wxScrolledWindow::OnPaint)
1492END_EVENT_TABLE()
1493
1494bool wxScrolledWindow::Create(wxWindow *parent,
1495                              wxWindowID id,
1496                              const wxPoint& pos,
1497                              const wxSize& size,
1498                              long style,
1499                              const wxString& name)
1500{
1501    m_targetWindow = this;
1502#ifdef __WXMAC__
1503    MacSetClipChildren( true ) ;
1504#endif
1505
1506    bool ok = wxPanel::Create(parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name);
1507
1508    return ok;
1509}
1510
1511wxScrolledWindow::~wxScrolledWindow()
1512{
1513}
1514
1515void wxScrolledWindow::OnPaint(wxPaintEvent& event)
1516{
1517    // the user code didn't really draw the window if we got here, so set this
1518    // flag to try to call OnDraw() later
1519    m_handler->ResetDrawnFlag();
1520
1521    event.Skip();
1522}
1523
1524#ifdef __WXMSW__
1525WXLRESULT wxScrolledWindow::MSWWindowProc(WXUINT nMsg,
1526                                       WXWPARAM wParam,
1527                                       WXLPARAM lParam)
1528{
1529    WXLRESULT rc = wxPanel::MSWWindowProc(nMsg, wParam, lParam);
1530
1531#ifndef __WXWINCE__
1532    // we need to process arrows ourselves for scrolling
1533    if ( nMsg == WM_GETDLGCODE )
1534    {
1535        rc |= DLGC_WANTARROWS;
1536    }
1537#endif
1538
1539    return rc;
1540}
1541
1542#endif // __WXMSW__
1543