]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/vscroll.cpp
Somehow, setting a tint color makes gauge work :/.
[wxWidgets.git] / src / generic / vscroll.cpp
index 530417aca39dc84bd630f1efac547552cd2054c2..efbef67e3344e8953c598ea2e733dbdd28e4c1a4 100644 (file)
@@ -2,9 +2,8 @@
 // Name:        src/generic/vscroll.cpp
 // Purpose:     wxVScrolledWindow implementation
 // Author:      Vadim Zeitlin
-// Modified by: Brad Anderson
+// Modified by: Brad Anderson, David Warkentin
 // Created:     30.05.03
-// RCS-ID:      $Id$
 // Copyright:   (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -31,6 +30,8 @@
 
 #include "wx/vscroll.h"
 
+#include "wx/utils.h"   // For wxMin/wxMax().
+
 // ============================================================================
 // wxVarScrollHelperEvtHandler declaration
 // ============================================================================
@@ -53,19 +54,27 @@ public:
 private:
     wxVarScrollHelperBase *m_scrollHelper;
 
-    DECLARE_NO_COPY_CLASS(wxVarScrollHelperEvtHandler)
+    wxDECLARE_NO_COPY_CLASS(wxVarScrollHelperEvtHandler);
 };
 
 // ============================================================================
 // wxVarScrollHelperEvtHandler implementation
 // ============================================================================
 
+// FIXME: This method totally duplicates a method with the same name in
+//        wxScrollHelperEvtHandler, we really should merge them by reusing the
+//        common parts in wxAnyScrollHelperBase.
 bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
 {
     wxEventType evType = event.GetEventType();
 
-    // pass it on to the real handler
-    bool processed = wxEvtHandler::ProcessEvent(event);
+    // Pass it on to the real handler: notice that we must not call
+    // ProcessEvent() on this object itself as it wouldn't pass it to the next
+    // handler (i.e. the real window) if we're called from a previous handler
+    // (as indicated by "process here only" flag being set) and we do want to
+    // execute the handler defined in the window we're associated with right
+    // now, without waiting until TryAfter() is called from wxEvtHandler.
+    bool processed = m_nextHandler->ProcessEvent(event);
 
     // always process the size events ourselves, even if the user code handles
     // them as well, as we need to AdjustScrollbars()
@@ -77,18 +86,28 @@ bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
     if ( evType == wxEVT_SIZE )
     {
         m_scrollHelper->HandleOnSize((wxSizeEvent &)event);
-
-        return !event.GetSkipped();
+        return true;
     }
 
-    if ( processed )
+    if ( processed && event.IsCommandEvent())
+        return true;
+
+    // For wxEVT_PAINT the user code can either handle this event as usual or
+    // override virtual OnDraw(), so if the event hasn't been handled we need
+    // to call this virtual function ourselves.
+    if (
+#ifndef __WXUNIVERSAL__
+          // in wxUniversal "processed" will always be true, because
+          // all windows use the paint event to draw themselves.
+          // In this case we can't use this flag to determine if a custom
+          // paint event handler already drew our window and we just
+          // call OnDraw() anyway.
+          !processed &&
+#endif // !__WXUNIVERSAL__
+            evType == wxEVT_PAINT )
     {
-        // normally, nothing more to do here - except if we have a command
-        // event
-        if ( event.IsCommandEvent() )
-        {
-            return true;
-        }
+        m_scrollHelper->HandleOnPaint((wxPaintEvent &)event);
+        return true;
     }
 
     // reset the skipped flag (which might have been set to true in
@@ -97,10 +116,6 @@ bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
     if ( wasSkipped )
         event.Skip(false);
 
-    // reset the skipped flag to false as it might have been set to true in
-    // ProcessEvent() above
-    event.Skip(false);
-
     if ( evType == wxEVT_SCROLLWIN_TOP ||
          evType == wxEVT_SCROLLWIN_BOTTOM ||
          evType == wxEVT_SCROLLWIN_LINEUP ||
@@ -115,20 +130,44 @@ bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
         {
             // it makes sense to indicate that we processed the message as we
             // did scroll the window (and also notice that wxAutoScrollTimer
-            // relies on our return value for continuous scrolling)
+            // relies on our return value to stop scrolling when we are at top
+            // or bottom already)
             processed = true;
             wasSkipped = false;
         }
     }
 #if wxUSE_MOUSEWHEEL
+    // Use GTK's own scroll wheel handling in GtkScrolledWindow
+#ifndef __WXGTK20__
     else if ( evType == wxEVT_MOUSEWHEEL )
     {
         m_scrollHelper->HandleOnMouseWheel((wxMouseEvent &)event);
     }
+#endif
 #endif // wxUSE_MOUSEWHEEL
+    else if ( evType == wxEVT_CHAR &&
+                (m_scrollHelper->GetOrientation() == wxVERTICAL) )
+    {
+        m_scrollHelper->HandleOnChar((wxKeyEvent &)event);
+        if ( !event.GetSkipped() )
+        {
+            processed = true;
+            wasSkipped = false;
+        }
+    }
+
+    event.Skip(wasSkipped);
 
-    if ( processed )
-        event.Skip(wasSkipped);
+    // We called ProcessEvent() on the next handler, meaning that we explicitly
+    // worked around the request to process the event in this handler only. As
+    // explained above, this is unfortunately really necessary but the trouble
+    // is that the event will continue to be post-processed by the previous
+    // handler resulting in duplicate calls to event handlers. Call the special
+    // function below to prevent this from happening, base class DoTryChain()
+    // will check for it and behave accordingly.
+    //
+    // And if we're not called from DoTryChain(), this won't do anything anyhow.
+    event.DidntHonourProcessOnlyIn();
 
     return processed;
 }
@@ -143,9 +182,8 @@ bool wxVarScrollHelperEvtHandler::ProcessEvent(wxEvent& event)
 // ----------------------------------------------------------------------------
 
 wxVarScrollHelperBase::wxVarScrollHelperBase(wxWindow *win)
+    : wxAnyScrollHelperBase(win)
 {
-    wxASSERT_MSG( win, _T("associated window can't be NULL in wxVarScrollHelperBase") );
-
 #if wxUSE_MOUSEWHEEL
     m_sumWheelRotation = 0;
 #endif
@@ -154,16 +192,11 @@ wxVarScrollHelperBase::wxVarScrollHelperBase(wxWindow *win)
     m_sizeTotal = 0;
     m_unitFirst = 0;
 
-    m_win =
-    m_targetWindow = (wxWindow *)NULL;
-
+    m_physicalScrolling = true;
     m_handler = NULL;
 
-    m_win = win;
-
     // by default, the associated window is also the target window
     DoSetTargetWindow(win);
-
 }
 
 wxVarScrollHelperBase::~wxVarScrollHelperBase()
@@ -312,14 +345,17 @@ size_t wxVarScrollHelperBase::GetNewScrollPosition(wxScrollWinEvent& event) cons
     }
     else if ( evtType == wxEVT_SCROLLWIN_PAGEUP )
     {
-        return FindFirstVisibleFromLast(m_unitFirst);
+        // Page up should do at least as much as line up.
+        return wxMin(FindFirstVisibleFromLast(m_unitFirst),
+                    m_unitFirst ? m_unitFirst - 1 : 0);
     }
     else if ( evtType == wxEVT_SCROLLWIN_PAGEDOWN )
     {
+        // And page down should do at least as much as line down.
         if ( GetVisibleEnd() )
-            return GetVisibleEnd() - 1;
+            return wxMax(GetVisibleEnd() - 1, m_unitFirst + 1);
         else
-            return GetVisibleEnd();
+            return wxMax(GetVisibleEnd(), m_unitFirst + 1);
     }
     else if ( evtType == wxEVT_SCROLLWIN_THUMBRELEASE )
     {
@@ -331,7 +367,7 @@ size_t wxVarScrollHelperBase::GetNewScrollPosition(wxScrollWinEvent& event) cons
     }
 
     // unknown scroll event?
-    wxFAIL_MSG( _T("unknown scroll event type?") );
+    wxFAIL_MSG( wxT("unknown scroll event type?") );
     return 0;
 }
 
@@ -475,7 +511,7 @@ void wxVarScrollHelperBase::RefreshUnit(size_t unit)
 
 void wxVarScrollHelperBase::RefreshUnits(size_t from, size_t to)
 {
-    wxASSERT_MSG( from <= to, _T("RefreshUnits(): empty range") );
+    wxASSERT_MSG( from <= to, wxT("RefreshUnits(): empty range") );
 
     // clump the range to just the visible units -- it is useless to refresh
     // the other ones
@@ -486,10 +522,10 @@ void wxVarScrollHelperBase::RefreshUnits(size_t from, size_t to)
         to = GetVisibleEnd();
 
     // calculate the rect occupied by these units on screen
-    int orient_size, nonorient_size, orient_pos;
-    orient_size = nonorient_size = orient_pos = 0;
+    int orient_size = 0,
+        orient_pos = 0;
 
-    nonorient_size = GetNonOrientationTargetSize();
+    int nonorient_size = GetNonOrientationTargetSize();
 
     for ( size_t nBefore = GetVisibleBegin();
           nBefore < from;
@@ -592,7 +628,7 @@ bool wxVarScrollHelperBase::DoScrollToUnit(size_t unit)
     // to avoid flicker.  We can't do this if we have children because they
     // won't be scrolled
     if ( m_targetWindow->GetChildren().empty() &&
-         GetVisibleBegin() >= unitLastOld || GetVisibleEnd() <= unitFirstOld )
+         (GetVisibleBegin() >= unitLastOld || GetVisibleEnd() <= unitFirstOld) )
     {
         // the simplest case: we don't have any old units left, just redraw
         // everything
@@ -600,7 +636,12 @@ bool wxVarScrollHelperBase::DoScrollToUnit(size_t unit)
     }
     else // scroll the window
     {
+        // Avoid scrolling visible parts of the screen on Mac
+#ifdef __WXMAC__
+        if (m_physicalScrolling && m_targetWindow->IsShownOnScreen())
+#else
         if ( m_physicalScrolling )
+#endif
         {
             wxCoord dx = 0,
                     dy = GetUnitsSize(GetVisibleBegin(), unitFirstOld);
@@ -668,6 +709,39 @@ bool wxVarScrollHelperBase::DoScrollPages(int pages)
 
 void wxVarScrollHelperBase::HandleOnSize(wxSizeEvent& event)
 {
+    if ( m_unitMax )
+    {
+        // sometimes change in varscrollable window's size can result in
+        // unused empty space after the last item. Fix it by decrementing
+        // first visible item position according to the available space.
+
+        // determine free space
+        const wxCoord sWindow = GetOrientationTargetSize();
+        wxCoord s = 0;
+        size_t unit;
+        for ( unit = m_unitFirst; unit < m_unitMax; ++unit )
+        {
+            if ( s > sWindow )
+                break;
+
+            s += OnGetUnitSize(unit);
+        }
+        wxCoord freeSpace = sWindow - s;
+
+        // decrement first visible item index as long as there is free space
+        size_t idealUnitFirst;
+        for ( idealUnitFirst = m_unitFirst;
+              idealUnitFirst > 0;
+              idealUnitFirst-- )
+        {
+            wxCoord us = OnGetUnitSize(idealUnitFirst-1);
+            if ( freeSpace < us )
+                break;
+            freeSpace -= us;
+        }
+        m_unitFirst = idealUnitFirst;
+    }
+
     UpdateScrollbar();
 
     event.Skip();
@@ -788,7 +862,7 @@ void wxVarHVScrollHelper::RefreshRowColumn(size_t row, size_t column)
         h_rect.x += OnGetColumnWidth(n);
     }
 
-    // refresh but specialize the behavior if we have a single target window
+    // refresh but specialize the behaviour if we have a single target window
     if ( wxVarVScrollHelper::GetTargetWindow() == wxVarHScrollHelper::GetTargetWindow() )
     {
         v_rect.x = h_rect.x;
@@ -811,7 +885,7 @@ void wxVarHVScrollHelper::RefreshRowsColumns(size_t fromRow, size_t toRow,
                                              size_t fromColumn, size_t toColumn)
 {
     wxASSERT_MSG( fromRow <= toRow || fromColumn <= toColumn,
-        _T("RefreshRowsColumns(): empty range") );
+        wxT("RefreshRowsColumns(): empty range") );
 
     // clump the range to just the visible units -- it is useless to refresh
     // the other ones
@@ -855,7 +929,7 @@ void wxVarHVScrollHelper::RefreshRowsColumns(size_t fromRow, size_t toRow,
         h_rect.width += OnGetColumnWidth(nBetween);
     }
 
-    // refresh but specialize the behavior if we have a single target window
+    // refresh but specialize the behaviour if we have a single target window
     if ( wxVarVScrollHelper::GetTargetWindow() == wxVarHScrollHelper::GetTargetWindow() )
     {
         v_rect.x = h_rect.x;
@@ -930,7 +1004,7 @@ IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel)
 // wxVarVScrollLegacyAdaptor
 // ===========================================================================
 
-size_t wxVarVScrollLegacyAdaptor::GetFirstVisibleLine() const 
+size_t wxVarVScrollLegacyAdaptor::GetFirstVisibleLine() const
 { return GetVisibleRowsBegin(); }
 
 size_t wxVarVScrollLegacyAdaptor::GetLastVisibleLine() const
@@ -947,19 +1021,19 @@ void wxVarVScrollLegacyAdaptor::RefreshLine(size_t line)
 
 void wxVarVScrollLegacyAdaptor::RefreshLines(size_t from, size_t to)
 { RefreshRows(from, to); }
-        
+
 bool wxVarVScrollLegacyAdaptor::ScrollToLine(size_t line)
 { return ScrollToRow(line); }
 
 bool wxVarVScrollLegacyAdaptor::ScrollLines(int lines)
 { return ScrollRows(lines); }
-        
+
 bool wxVarVScrollLegacyAdaptor::ScrollPages(int pages)
 { return ScrollRowPages(pages); }
 
 wxCoord wxVarVScrollLegacyAdaptor::OnGetLineHeight(size_t WXUNUSED(n)) const
 {
-    wxFAIL_MSG( _T("OnGetLineHeight() must be overridden if OnGetRowHeight() isn't!") );
+    wxFAIL_MSG( wxT("OnGetLineHeight() must be overridden if OnGetRowHeight() isn't!") );
     return -1;
 }