X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/52b7ece54c7c9231c5c212ac47967fe86db5c144..da6f998486f1070e205f2e0a54f22a1b67cb32a4:/src/generic/vscroll.cpp diff --git a/src/generic/vscroll.cpp b/src/generic/vscroll.cpp index 492b3ead2c..efbef67e33 100644 --- a/src/generic/vscroll.cpp +++ b/src/generic/vscroll.cpp @@ -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 // 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; + } + } - if ( processed ) - event.Skip(wasSkipped); + 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; @@ -539,7 +575,7 @@ bool wxVarScrollHelperBase::ScrollLayout() return m_targetWindow->wxWindow::Layout(); } -int wxVarScrollHelperBase::HitTest(wxCoord coord) const +int wxVarScrollHelperBase::VirtualHitTest(wxCoord coord) const { const size_t unitMax = GetVisibleEnd(); for ( size_t unit = GetVisibleBegin(); unit < unitMax; ++unit ) @@ -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; @@ -874,10 +948,10 @@ void wxVarHVScrollHelper::RefreshRowsColumns(size_t fromRow, size_t toRow, } } -wxPosition wxVarHVScrollHelper::HitTest(wxCoord x, wxCoord y) const +wxPosition wxVarHVScrollHelper::VirtualHitTest(wxCoord x, wxCoord y) const { - return wxPosition(wxVarVScrollHelper::HitTest(y), - wxVarHScrollHelper::HitTest(x)); + return wxPosition(wxVarVScrollHelper::VirtualHitTest(y), + wxVarHScrollHelper::VirtualHitTest(x)); } void wxVarHVScrollHelper::DoPrepareDC(wxDC& dc) @@ -923,3 +997,60 @@ IMPLEMENT_ABSTRACT_CLASS(wxVScrolledWindow, wxPanel) IMPLEMENT_ABSTRACT_CLASS(wxHScrolledWindow, wxPanel) IMPLEMENT_ABSTRACT_CLASS(wxHVScrolledWindow, wxPanel) + +#if WXWIN_COMPATIBILITY_2_8 + +// =========================================================================== +// wxVarVScrollLegacyAdaptor +// =========================================================================== + +size_t wxVarVScrollLegacyAdaptor::GetFirstVisibleLine() const +{ return GetVisibleRowsBegin(); } + +size_t wxVarVScrollLegacyAdaptor::GetLastVisibleLine() const +{ return GetVisibleRowsEnd() - 1; } + +size_t wxVarVScrollLegacyAdaptor::GetLineCount() const +{ return GetRowCount(); } + +void wxVarVScrollLegacyAdaptor::SetLineCount(size_t count) +{ SetRowCount(count); } + +void wxVarVScrollLegacyAdaptor::RefreshLine(size_t line) +{ RefreshRow(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( wxT("OnGetLineHeight() must be overridden if OnGetRowHeight() isn't!") ); + return -1; +} + +void wxVarVScrollLegacyAdaptor::OnGetLinesHint(size_t WXUNUSED(lineMin), + size_t WXUNUSED(lineMax)) const +{ +} + +wxCoord wxVarVScrollLegacyAdaptor::OnGetRowHeight(size_t n) const +{ + return OnGetLineHeight(n); +} + +void wxVarVScrollLegacyAdaptor::OnGetRowsHeightHint(size_t rowMin, + size_t rowMax) const +{ + OnGetLinesHint(rowMin, rowMax); +} + +#endif // WXWIN_COMPATIBILITY_2_8