X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/187c183c6a57140886bac1c93cb42b93aa5f4e29..911219b2abc5ab610481fe8d4fcc3674d74e637e:/src/univ/scrolbar.cpp diff --git a/src/univ/scrolbar.cpp b/src/univ/scrolbar.cpp index 4445239eb9..ff451e18fc 100644 --- a/src/univ/scrolbar.cpp +++ b/src/univ/scrolbar.cpp @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: univ/scrolbar.cpp +// Name: src/univ/scrolbar.cpp // Purpose: wxScrollBar implementation // Author: Vadim Zeitlin // Modified by: // Created: 20.08.00 // RCS-ID: $Id$ // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com) -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -17,10 +17,6 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "univscrolbar.h" -#endif - #include "wx/wxprec.h" #ifdef __BORLANDC__ @@ -29,12 +25,13 @@ #if wxUSE_SCROLLBAR +#include "wx/scrolbar.h" + #ifndef WX_PRECOMP #include "wx/timer.h" - #include "wx/dcclient.h" - #include "wx/scrolbar.h" #include "wx/validate.h" + #include "wx/log.h" #endif #include "wx/univ/scrtimer.h" @@ -43,11 +40,9 @@ #include "wx/univ/inphand.h" #include "wx/univ/theme.h" -#define WXDEBUG_SCROLLBAR - -#ifndef __WXDEBUG__ - #undef WXDEBUG_SCROLLBAR -#endif // !__WXDEBUG__ +#if wxDEBUG_LEVEL >= 2 + #define WXDEBUG_SCROLLBAR +#endif #if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__) #include "wx/msw/private.h" @@ -79,10 +74,7 @@ private: // implementation // ============================================================================ -IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl) - BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase) - EVT_IDLE(wxScrollBar::OnIdle) END_EVENT_TABLE() // ---------------------------------------------------------------------------- @@ -133,7 +125,7 @@ void wxScrollBar::Init() m_elementsState[n] = 0; } - m_dirty = FALSE; + m_dirty = false; } bool wxScrollBar::Create(wxWindow *parent, @@ -147,23 +139,45 @@ bool wxScrollBar::Create(wxWindow *parent, // the scrollbars never have the border style &= ~wxBORDER_MASK; - if ( !wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name) ) - return FALSE; + if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) + return false; - SetBestSize(size); + SetInitialSize(size); // override the cursor of the target window (if any) SetCursor(wxCURSOR_ARROW); CreateInputHandler(wxINP_HANDLER_SCROLLBAR); - return TRUE; + return true; } wxScrollBar::~wxScrollBar() { } +// ---------------------------------------------------------------------------- +// misc accessors +// ---------------------------------------------------------------------------- + +bool wxScrollBar::IsStandalone() const +{ + wxWindow *parent = GetParent(); + if ( !parent ) + { + return true; + } + + return (parent->GetScrollbar(wxHORIZONTAL) != this) && + (parent->GetScrollbar(wxVERTICAL) != this); +} + +bool wxScrollBar::AcceptsFocus() const +{ + // the window scrollbars never accept focus + return wxScrollBarBase::AcceptsFocus() && IsStandalone(); +} + // ---------------------------------------------------------------------------- // scrollbar API // ---------------------------------------------------------------------------- @@ -200,7 +214,7 @@ void wxScrollBar::DoSetThumb(int pos) m_elementsState[Element_Thumb] |= wxCONTROL_DIRTY; m_elementsState[m_thumbPos > m_thumbPosOld ? Element_Bar_1 : Element_Bar_2] |= wxCONTROL_DIRTY; - m_dirty = TRUE; + m_dirty = true; } int wxScrollBar::GetThumbPosition() const @@ -225,7 +239,7 @@ int wxScrollBar::GetRange() const void wxScrollBar::SetThumbPosition(int pos) { - wxCHECK_RET( pos >= 0 && pos <= m_range, _T("thumb position out of range") ); + wxCHECK_RET( pos >= 0 && pos <= m_range, wxT("thumb position out of range") ); DoSetThumb(pos); } @@ -234,7 +248,7 @@ void wxScrollBar::SetScrollbar(int position, int thumbSize, int range, int pageSize, bool refresh) { - // we only refresh everythign when the range changes, thumb position + // we only refresh everything when the range changes, thumb position // changes are handled in OnIdle bool needsRefresh = (range != m_range) || (thumbSize != m_thumbSize) || @@ -280,9 +294,9 @@ wxSize wxScrollBar::DoGetBestClientSize() const return size; } -wxScrollArrows::Arrow wxScrollBar::HitTest(const wxPoint& pt) const +wxScrollArrows::Arrow wxScrollBar::HitTestArrow(const wxPoint& pt) const { - switch ( m_renderer->HitTestScrollbar(this, pt) ) + switch ( HitTestBar(pt) ) { case wxHT_SCROLLBAR_ARROW_LINE_1: return wxScrollArrows::Arrow_First; @@ -295,11 +309,270 @@ wxScrollArrows::Arrow wxScrollBar::HitTest(const wxPoint& pt) const } } +wxHitTest wxScrollBar::HitTestBar(const wxPoint& pt) const +{ + // we only need to work with either x or y coord depending on the + // orientation, choose one (but still check the other one to verify if the + // mouse is in the window at all) + const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize(); + + wxCoord coord, sizeArrow, sizeTotal; + wxSize size = GetSize(); + if ( GetWindowStyle() & wxVERTICAL ) + { + if ( pt.x < 0 || pt.x > size.x ) + return wxHT_NOWHERE; + + coord = pt.y; + sizeArrow = sizeArrowSB.y; + sizeTotal = size.y; + } + else // horizontal + { + if ( pt.y < 0 || pt.y > size.y ) + return wxHT_NOWHERE; + + coord = pt.x; + sizeArrow = sizeArrowSB.x; + sizeTotal = size.x; + } + + // test for the arrows first as it's faster + if ( coord < 0 || coord > sizeTotal ) + { + return wxHT_NOWHERE; + } + else if ( coord < sizeArrow ) + { + return wxHT_SCROLLBAR_ARROW_LINE_1; + } + else if ( coord > sizeTotal - sizeArrow ) + { + return wxHT_SCROLLBAR_ARROW_LINE_2; + } + else + { + // calculate the thumb position in pixels + sizeTotal -= 2*sizeArrow; + wxCoord thumbStart, thumbEnd; + int range = GetRange(); + if ( !range ) + { + // clicking the scrollbar without range has no effect + return wxHT_NOWHERE; + } + else + { + GetScrollBarThumbSize(sizeTotal, + GetThumbPosition(), + GetThumbSize(), + range, + &thumbStart, + &thumbEnd); + } + + // now compare with the thumb position + coord -= sizeArrow; + if ( coord < thumbStart ) + return wxHT_SCROLLBAR_BAR_1; + else if ( coord > thumbEnd ) + return wxHT_SCROLLBAR_BAR_2; + else + return wxHT_SCROLLBAR_THUMB; + } +} + +/* static */ +void wxScrollBar::GetScrollBarThumbSize(wxCoord length, + int thumbPos, + int thumbSize, + int range, + wxCoord *thumbStart, + wxCoord *thumbEnd) +{ + // the thumb can't be made less than this number of pixels + static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable + + *thumbStart = (length*thumbPos) / range; + *thumbEnd = (length*(thumbPos + thumbSize)) / range; + + if ( *thumbEnd - *thumbStart < thumbMinWidth ) + { + // adjust the end if possible + if ( *thumbStart <= length - thumbMinWidth ) + { + // yes, just make it wider + *thumbEnd = *thumbStart + thumbMinWidth; + } + else // it is at the bottom of the scrollbar + { + // so move it a bit up + *thumbStart = length - thumbMinWidth; + *thumbEnd = length; + } + } +} + +wxRect wxScrollBar::GetScrollbarRect(wxScrollBar::Element elem, + int thumbPos) const +{ + if ( thumbPos == -1 ) + { + thumbPos = GetThumbPosition(); + } + + const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize(); + + wxSize sizeTotal = GetClientSize(); + wxCoord *start, *width; + wxCoord length, arrow; + wxRect rect; + if ( IsVertical() ) + { + rect.x = 0; + rect.width = sizeTotal.x; + length = sizeTotal.y; + start = &rect.y; + width = &rect.height; + arrow = sizeArrow.y; + } + else // horizontal + { + rect.y = 0; + rect.height = sizeTotal.y; + length = sizeTotal.x; + start = &rect.x; + width = &rect.width; + arrow = sizeArrow.x; + } + + switch ( elem ) + { + case wxScrollBar::Element_Arrow_Line_1: + *start = 0; + *width = arrow; + break; + + case wxScrollBar::Element_Arrow_Line_2: + *start = length - arrow; + *width = arrow; + break; + + case wxScrollBar::Element_Arrow_Page_1: + case wxScrollBar::Element_Arrow_Page_2: + // we don't have them at all + break; + + case wxScrollBar::Element_Thumb: + case wxScrollBar::Element_Bar_1: + case wxScrollBar::Element_Bar_2: + // we need to calculate the thumb position - do it + { + length -= 2*arrow; + wxCoord thumbStart, thumbEnd; + int range = GetRange(); + if ( !range ) + { + thumbStart = + thumbEnd = 0; + } + else + { + GetScrollBarThumbSize(length, + thumbPos, + GetThumbSize(), + range, + &thumbStart, + &thumbEnd); + } + + if ( elem == wxScrollBar::Element_Thumb ) + { + *start = thumbStart; + *width = thumbEnd - thumbStart; + } + else if ( elem == wxScrollBar::Element_Bar_1 ) + { + *start = 0; + *width = thumbStart; + } + else // elem == wxScrollBar::Element_Bar_2 + { + *start = thumbEnd; + *width = length - thumbEnd; + } + + // everything is relative to the start of the shaft so far + *start += arrow; + } + break; + + case wxScrollBar::Element_Max: + default: + wxFAIL_MSG( wxT("unknown scrollbar element") ); + } + + return rect; +} + +wxCoord wxScrollBar::GetScrollbarSize() const +{ + const wxSize sizeArrowSB = m_renderer->GetScrollbarArrowSize(); + + wxCoord sizeArrow, sizeTotal; + if ( GetWindowStyle() & wxVERTICAL ) + { + sizeArrow = sizeArrowSB.y; + sizeTotal = GetSize().y; + } + else // horizontal + { + sizeArrow = sizeArrowSB.x; + sizeTotal = GetSize().x; + } + + return sizeTotal - 2*sizeArrow; +} + + +wxCoord wxScrollBar::ScrollbarToPixel(int thumbPos) +{ + int range = GetRange(); + if ( !range ) + { + // the only valid position anyhow + return 0; + } + + if ( thumbPos == -1 ) + { + // by default use the current thumb position + thumbPos = GetThumbPosition(); + } + + const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize(); + return (thumbPos * GetScrollbarSize()) / range + + (IsVertical() ? sizeArrow.y : sizeArrow.x); +} + +int wxScrollBar::PixelToScrollbar(wxCoord coord) +{ + const wxSize sizeArrow = m_renderer->GetScrollbarArrowSize(); + return ((coord - (IsVertical() ? sizeArrow.y : sizeArrow.x)) * + GetRange() ) / GetScrollbarSize(); +} + // ---------------------------------------------------------------------------- // drawing // ---------------------------------------------------------------------------- -void wxScrollBar::OnIdle(wxIdleEvent& event) +void wxScrollBar::OnInternalIdle() +{ + UpdateThumb(); + wxControl::OnInternalIdle(); +} + +void wxScrollBar::UpdateThumb() { if ( m_dirty ) { @@ -307,7 +580,7 @@ void wxScrollBar::OnIdle(wxIdleEvent& event) { if ( m_elementsState[n] & wxCONTROL_DIRTY ) { - wxRect rect = GetRenderer()->GetScrollbarRect(this, (Element)n); + wxRect rect = GetScrollbarRect((Element)n); if ( rect.width && rect.height ) { @@ -341,14 +614,12 @@ void wxScrollBar::OnIdle(wxIdleEvent& event) } #else // efficient version: only repaint the area occupied by // the thumb previously - we can't do better than this - rect = GetRenderer()->GetScrollbarRect(this, - Element_Thumb, - m_thumbPosOld); + rect = GetScrollbarRect(Element_Thumb, m_thumbPosOld); #endif // 0/1 } #ifdef WXDEBUG_SCROLLBAR - static bool s_refreshDebug = FALSE; + static bool s_refreshDebug = false; if ( s_refreshDebug ) { wxClientDC dc(this); @@ -364,17 +635,15 @@ void wxScrollBar::OnIdle(wxIdleEvent& event) } #endif // WXDEBUG_SCROLLBAR - Refresh(TRUE, &rect); + Refresh(false, &rect); } m_elementsState[n] &= ~wxCONTROL_DIRTY; } } - m_dirty = FALSE; + m_dirty = false; } - - event.Skip(); } void wxScrollBar::DoDraw(wxControlRenderer *renderer) @@ -382,7 +651,7 @@ void wxScrollBar::DoDraw(wxControlRenderer *renderer) renderer->DrawScrollbar(this, m_thumbPosOld); // clear all dirty flags - m_dirty = FALSE; + m_dirty = false; m_thumbPosOld = -1; } @@ -430,7 +699,7 @@ void wxScrollBar::SetState(Element which, int flags) { m_elementsState[which] = flags | wxCONTROL_DIRTY; - m_dirty = TRUE; + m_dirty = true; } } @@ -455,7 +724,7 @@ bool wxScrollBar::PerformAction(const wxControlAction& action, { int thumbOld = m_thumbPos; - bool notify = FALSE; // send an event about the change? + bool notify = false; // send an event about the change? wxEventType scrollType; @@ -464,6 +733,10 @@ bool wxScrollBar::PerformAction(const wxControlAction& action, { DoSetThumb(numArg); + // VS: we have to force redraw here, otherwise the thumb will lack + // behind mouse cursor + UpdateThumb(); + scrollType = wxEVT_SCROLLWIN_THUMBTRACK; } else if ( action == wxACTION_SCROLL_LINE_UP ) @@ -506,7 +779,7 @@ bool wxScrollBar::PerformAction(const wxControlAction& action, else if ( action == wxACTION_SCROLL_THUMB_RELEASE ) { // always notify about this - notify = TRUE; + notify = true; scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; } else @@ -516,13 +789,29 @@ bool wxScrollBar::PerformAction(const wxControlAction& action, bool changed = m_thumbPos != thumbOld; if ( notify || changed ) { - wxScrollWinEvent event(scrollType, m_thumbPos, - IsVertical() ? wxVERTICAL : wxHORIZONTAL); - event.SetEventObject(this); - GetParent()->GetEventHandler()->ProcessEvent(event); + if ( IsStandalone() ) + { + // we should generate EVT_SCROLL events for the standalone + // scrollbars and not the EVT_SCROLLWIN ones + // + // NB: we assume that scrollbar events are sequentially numbered + // but this should be ok as other code relies on this as well + scrollType += wxEVT_SCROLL_TOP - wxEVT_SCROLLWIN_TOP; + wxScrollEvent event(scrollType, this->GetId(), m_thumbPos, + IsVertical() ? wxVERTICAL : wxHORIZONTAL); + event.SetEventObject(this); + GetEventHandler()->ProcessEvent(event); + } + else // part of the window + { + wxScrollWinEvent event(scrollType, m_thumbPos, + IsVertical() ? wxVERTICAL : wxHORIZONTAL); + event.SetEventObject(this); + GetParent()->GetEventHandler()->ProcessEvent(event); + } } - return TRUE; + return true; } void wxScrollBar::ScrollToStart() @@ -538,13 +827,22 @@ void wxScrollBar::ScrollToEnd() bool wxScrollBar::ScrollLines(int nLines) { DoSetThumb(m_thumbPos + nLines); - return TRUE; + return true; } bool wxScrollBar::ScrollPages(int nPages) { DoSetThumb(m_thumbPos + nPages*m_pageSize); - return TRUE; + return true; +} + +/* static */ +wxInputHandler *wxScrollBar::GetStdInputHandler(wxInputHandler *handlerDef) +{ + static wxStdScrollBarInputHandler + s_handler(wxTheme::Get()->GetRenderer(), handlerDef); + + return &s_handler; } // ============================================================================ @@ -614,12 +912,12 @@ bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar, int oldThumbPos = scrollbar->GetThumbPosition(); scrollbar->PerformAction(action); if ( scrollbar->GetThumbPosition() != oldThumbPos ) - return TRUE; + return true; // we scrolled till the end m_timerScroll->Stop(); - return FALSE; + return false; } void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control) @@ -633,14 +931,10 @@ void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control) m_btnCapture = -1; - if ( m_timerScroll ) - { - delete m_timerScroll; - m_timerScroll = NULL; - } + wxDELETE(m_timerScroll); // unpress the arrow and highlight the current element - Press(control, FALSE); + Press(control, false); } wxCoord @@ -655,7 +949,7 @@ void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar, const wxMouseEvent& event) { int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse; - thumbPos = m_renderer->PixelToScrollbar(scrollbar, thumbPos); + thumbPos = scrollbar->PixelToScrollbar(thumbPos); scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos); } @@ -675,17 +969,15 @@ bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer, case WXK_LEFT: action = wxACTION_SCROLL_LINE_UP; break; case WXK_HOME: action = wxACTION_SCROLL_START; break; case WXK_END: action = wxACTION_SCROLL_END; break; - case WXK_PAGEUP: - case WXK_PRIOR: action = wxACTION_SCROLL_PAGE_UP; break; - case WXK_PAGEDOWN: - case WXK_NEXT: action = wxACTION_SCROLL_PAGE_DOWN; break; + case WXK_PAGEUP: action = wxACTION_SCROLL_PAGE_UP; break; + case WXK_PAGEDOWN: action = wxACTION_SCROLL_PAGE_DOWN; break; } - if ( !!action ) + if ( !action.IsEmpty() ) { consumer->PerformAction(action); - return TRUE; + return true; } } @@ -697,15 +989,11 @@ bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer, { // is this a click event from an acceptable button? int btn = event.GetButton(); - if ( (btn != -1) && IsAllowedButton(btn) ) + if ( btn == wxMOUSE_BTN_LEFT ) { // determine which part of the window mouse is in wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar); - wxHitTest ht = m_renderer->HitTestScrollbar - ( - scrollbar, - event.GetPosition() - ); + wxHitTest ht = scrollbar->HitTestBar(event.GetPosition()); // when the mouse is pressed on any scrollbar element, we capture it // and hold capture until the same mouse button is released @@ -718,7 +1006,7 @@ bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer, m_winCapture->CaptureMouse(); // generate the command - bool hasAction = TRUE; + bool hasAction = true; wxControlAction action; switch ( ht ) { @@ -743,23 +1031,23 @@ bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer, case wxHT_SCROLLBAR_THUMB: consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG); m_ofsMouse = GetMouseCoord(scrollbar, event) - - m_renderer->ScrollbarToPixel(scrollbar); + scrollbar->ScrollbarToPixel(); // fall through: there is no immediate action default: - hasAction = FALSE; + hasAction = false; } // remove highlighting - Highlight(scrollbar, FALSE); + Highlight(scrollbar, false); m_htLast = ht; // and press the arrow or highlight thumb now instead if ( m_htLast == wxHT_SCROLLBAR_THUMB ) - Highlight(scrollbar, TRUE); + Highlight(scrollbar, true); else - Press(scrollbar, TRUE); + Press(scrollbar, true); // start dragging if ( hasAction ) @@ -787,14 +1075,14 @@ bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer, } m_htLast = ht; - Highlight(scrollbar, TRUE); + Highlight(scrollbar, true); } else { // this is not supposed to happen as the button can't go up // without going down previously and then we'd have // m_winCapture by now - wxFAIL_MSG( _T("logic error in mouse capturing code") ); + wxFAIL_MSG( wxT("logic error in mouse capturing code") ); } } } @@ -809,71 +1097,69 @@ bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer, if ( m_winCapture ) { - if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Moving() ) + if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Dragging() ) { // make the thumb follow the mouse by keeping the same offset // between the mouse position and the top/left of the thumb HandleThumbMove(scrollbar, event); - return TRUE; + return true; } // no other changes are possible while the mouse is captured - return FALSE; + return false; } bool isArrow = scrollbar->GetArrows().HandleMouseMove(event); - if ( event.Moving() ) + if ( event.Dragging() ) { - wxHitTest ht = m_renderer->HitTestScrollbar - ( - scrollbar, - event.GetPosition() - ); + wxHitTest ht = scrollbar->HitTestBar(event.GetPosition()); if ( ht == m_htLast ) { // nothing changed - return FALSE; + return false; } #ifdef DEBUG_MOUSE wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht); #endif // DEBUG_MOUSE - Highlight(scrollbar, FALSE); + Highlight(scrollbar, false); m_htLast = ht; if ( !isArrow ) - Highlight(scrollbar, TRUE); + Highlight(scrollbar, true); //else: already done by wxScrollArrows::HandleMouseMove } else if ( event.Leaving() ) { if ( !isArrow ) - Highlight(scrollbar, FALSE); + Highlight(scrollbar, false); m_htLast = wxHT_NOWHERE; } else // event.Entering() { // we don't process this event - return FALSE; + return false; } // we did something - return TRUE; + return true; } #endif // wxUSE_SCROLLBAR +#if wxUSE_TIMER + // ---------------------------------------------------------------------------- // wxScrollTimer // ---------------------------------------------------------------------------- wxScrollTimer::wxScrollTimer() { - m_skipNext = FALSE; + m_skipNext = false; } void wxScrollTimer::StartAutoScroll() @@ -888,7 +1174,7 @@ void wxScrollTimer::StartAutoScroll() // there is an initial delay before the scrollbar starts scrolling - // implement it by ignoring the first timer expiration and only start // scrolling from the second one - m_skipNext = TRUE; + m_skipNext = true; Start(200); // FIXME: hardcoded delay } @@ -900,7 +1186,7 @@ void wxScrollTimer::Notify() Stop(); Start(50); // FIXME: hardcoded delay - m_skipNext = FALSE; + m_skipNext = false; } else { @@ -910,3 +1196,4 @@ void wxScrollTimer::Notify() } } +#endif // wxUSE_TIMER