/////////////////////////////////////////////////////////////////////////////
-// 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
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// headers
// ----------------------------------------------------------------------------
-#ifdef __GNUG__
- #pragma implementation "scrolbar.h"
-#endif
-
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#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"
#include "wx/univ/inphand.h"
#include "wx/univ/theme.h"
-#define WXDEBUG_SCROLLBAR
+#if wxDEBUG_LEVEL >= 2
+ #define WXDEBUG_SCROLLBAR
+#endif
-#ifndef __WXDEBUG__
- #undef WXDEBUG_SCROLLBAR
-#endif // !__WXDEBUG__
+#if defined(WXDEBUG_SCROLLBAR) && defined(__WXMSW__) && !defined(__WXMICROWIN__)
+#include "wx/msw/private.h"
+#endif
// ----------------------------------------------------------------------------
// wxScrollBarTimer: this class is used to repeatedly scroll the scrollbar
IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl)
BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase)
- EVT_IDLE(wxScrollBar::OnIdle)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------
m_elementsState[n] = 0;
}
- m_dirty = FALSE;
+ m_dirty = false;
}
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
// ----------------------------------------------------------------------------
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
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);
}
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) ||
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;
}
}
+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 )
{
{
if ( m_elementsState[n] & wxCONTROL_DIRTY )
{
- wxRect rect = GetRenderer()->GetScrollbarRect(this, (Element)n);
+ wxRect rect = GetScrollbarRect((Element)n);
if ( rect.width && rect.height )
{
}
#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);
}
#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)
renderer->DrawScrollbar(this, m_thumbPosOld);
// clear all dirty flags
- m_dirty = FALSE;
+ m_dirty = false;
m_thumbPosOld = -1;
}
{
m_elementsState[which] = flags | wxCONTROL_DIRTY;
- m_dirty = TRUE;
+ m_dirty = true;
}
}
{
int thumbOld = m_thumbPos;
- bool notify = FALSE; // send an event about the change?
+ bool notify = false; // send an event about the change?
wxEventType scrollType;
{
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 )
else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
{
// always notify about this
- notify = TRUE;
+ notify = true;
scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
}
else
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()
DoSetThumb(m_range - m_thumbSize);
}
-void wxScrollBar::ScrollLines(int nLines)
+bool wxScrollBar::ScrollLines(int nLines)
{
DoSetThumb(m_thumbPos + nLines);
+ return true;
}
-void wxScrollBar::ScrollPages(int nPages)
+bool wxScrollBar::ScrollPages(int nPages)
{
DoSetThumb(m_thumbPos + nPages*m_pageSize);
+ return true;
+}
+
+/* static */
+wxInputHandler *wxScrollBar::GetStdInputHandler(wxInputHandler *handlerDef)
+{
+ static wxStdScrollBarInputHandler
+ s_handler(wxTheme::Get()->GetRenderer(), handlerDef);
+
+ return &s_handler;
}
// ============================================================================
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)
}
// unpress the arrow and highlight the current element
- Press(control, FALSE);
+ Press(control, false);
}
wxCoord
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);
}
-bool wxStdScrollBarInputHandler::HandleKey(wxControl *control,
+bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
const wxKeyEvent& event,
bool pressed)
{
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_PRIOR: action = wxACTION_SCROLL_PAGE_UP; break;
- 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() )
{
- control->PerformAction(action);
+ consumer->PerformAction(action);
- return TRUE;
+ return true;
}
}
- return wxStdInputHandler::HandleKey(control, event, pressed);
+ return wxStdInputHandler::HandleKey(consumer, event, pressed);
}
-bool wxStdScrollBarInputHandler::HandleMouse(wxControl *control,
+bool wxStdScrollBarInputHandler::HandleMouse(wxInputConsumer *consumer,
const wxMouseEvent& event)
{
// 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(control, wxScrollBar);
- wxHitTest ht = m_renderer->HitTestScrollbar
- (
- scrollbar,
- event.GetPosition()
- );
+ wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
+ 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
if ( !m_winCapture )
{
m_btnCapture = btn;
- m_winCapture = control;
+ m_winCapture = consumer->GetInputWindow();
m_winCapture->CaptureMouse();
// generate the command
- bool hasAction = TRUE;
+ bool hasAction = true;
wxControlAction action;
switch ( ht )
{
break;
case wxHT_SCROLLBAR_THUMB:
- control->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
+ 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 )
}
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") );
}
}
}
- return wxStdInputHandler::HandleMouse(control, event);
+ return wxStdInputHandler::HandleMouse(consumer, event);
}
-bool wxStdScrollBarInputHandler::HandleMouseMove(wxControl *control,
+bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
const wxMouseEvent& event)
{
- wxScrollBar *scrollbar = wxStaticCast(control, wxScrollBar);
+ wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
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()
// 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
}
Stop();
Start(50); // FIXME: hardcoded delay
- m_skipNext = FALSE;
+ m_skipNext = false;
}
else
{
}
}
+#endif // wxUSE_TIMER