/////////////////////////////////////////////////////////////////////////////
// Name:        univ/scrolbar.cpp
// Purpose:     wxScrollBar implementation
// Author:      Vadim Zeitlin
// Modified by:
// Created:     20.08.00
// RCS-ID:      $Id: scrolbar.cpp,v 1.15 2003/03/17 11:55:48 JS Exp $
// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#ifdef __GNUG__
    #pragma implementation "univscrolbar.h"
#endif

#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_SCROLLBAR

#ifndef WX_PRECOMP
    #include "wx/timer.h"

    #include "wx/dcclient.h"
    #include "wx/scrolbar.h"
    #include "wx/validate.h"
#endif

#include "wx/univ/scrtimer.h"

#include "wx/univ/renderer.h"
#include "wx/univ/inphand.h"
#include "wx/univ/theme.h"
#include "wx/log.h"

#define WXDEBUG_SCROLLBAR

#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
// when the mouse is help pressed on the arrow or on the bar. It generates the
// given scroll action command periodically.
// ----------------------------------------------------------------------------

class wxScrollBarTimer : public wxScrollTimer
{
public:
    wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
                     const wxControlAction& action,
                     wxScrollBar *control);

protected:
    virtual bool DoNotify();

private:
    wxStdScrollBarInputHandler *m_handler;
    wxControlAction m_action;
    wxScrollBar    *m_control;
};

// ============================================================================
// implementation
// ============================================================================

IMPLEMENT_DYNAMIC_CLASS(wxScrollBar, wxControl)

BEGIN_EVENT_TABLE(wxScrollBar, wxScrollBarBase)
    EVT_IDLE(wxScrollBar::OnIdle)
END_EVENT_TABLE()

// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------

#ifdef __VISUALC__
    // warning C4355: 'this' : used in base member initializer list
    #pragma warning(disable:4355)  // so what? disable it...
#endif

wxScrollBar::wxScrollBar()
           : m_arrows(this)
{
    Init();
}

wxScrollBar::wxScrollBar(wxWindow *parent,
                         wxWindowID id,
                         const wxPoint& pos,
                         const wxSize& size,
                         long style,
                         const wxValidator& validator,
                         const wxString& name)
           : m_arrows(this)
{
    Init();

    (void)Create(parent, id, pos, size, style, validator, name);
}

#ifdef __VISUALC__
    // warning C4355: 'this' : used in base member initializer list
    #pragma warning(default:4355)
#endif

void wxScrollBar::Init()
{
    m_range =
    m_thumbSize =
    m_thumbPos =
    m_pageSize = 0;

    m_thumbPosOld = -1;

    for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
    {
        m_elementsState[n] = 0;
    }

    m_dirty = FALSE;
}

bool wxScrollBar::Create(wxWindow *parent,
                         wxWindowID id,
                         const wxPoint &pos,
                         const wxSize &size,
                         long style,
                         const wxValidator& validator,
                         const wxString &name)
{
    // the scrollbars never have the border
    style &= ~wxBORDER_MASK;

    if ( !wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name) )
        return FALSE;

    SetBestSize(size);

    // override the cursor of the target window (if any)
    SetCursor(wxCURSOR_ARROW);

    CreateInputHandler(wxINP_HANDLER_SCROLLBAR);

    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
// ----------------------------------------------------------------------------

void wxScrollBar::DoSetThumb(int pos)
{
    // don't assert hecks here, we're a private function which is meant to be
    // called with any args at all
    if ( pos < 0 )
    {
        pos = 0;
    }
    else if ( pos > m_range - m_thumbSize )
    {
        pos = m_range - m_thumbSize;
    }

    if ( m_thumbPos == pos )
    {
        // nothing changed, avoid refreshes which would provoke flicker
        return;
    }

    if ( m_thumbPosOld == -1 )
    {
        // remember the old thumb position
        m_thumbPosOld = m_thumbPos;
    }

    m_thumbPos = pos;

    // we have to refresh the part of the bar which was under the thumb and the
    // thumb itself
    m_elementsState[Element_Thumb] |= wxCONTROL_DIRTY;
    m_elementsState[m_thumbPos > m_thumbPosOld
                        ? Element_Bar_1 : Element_Bar_2] |= wxCONTROL_DIRTY;
    m_dirty = TRUE;
}

int wxScrollBar::GetThumbPosition() const
{
    return m_thumbPos;
}

int wxScrollBar::GetThumbSize() const
{
    return m_thumbSize;
}

int wxScrollBar::GetPageSize() const
{
    return m_pageSize;
}

int wxScrollBar::GetRange() const
{
    return m_range;
}

void wxScrollBar::SetThumbPosition(int pos)
{
    wxCHECK_RET( pos >= 0 && pos <= m_range, _T("thumb position out of range") );

    DoSetThumb(pos);
}

void wxScrollBar::SetScrollbar(int position, int thumbSize,
                               int range, int pageSize,
                               bool refresh)
{
    // we only refresh everythign when the range changes, thumb position
    // changes are handled in OnIdle
    bool needsRefresh = (range != m_range) ||
                        (thumbSize != m_thumbSize) ||
                        (pageSize != m_pageSize);

    // set all parameters
    m_range = range;
    m_thumbSize = thumbSize;
    SetThumbPosition(position);
    m_pageSize = pageSize;

    // ignore refresh parameter unless we really need to refresh everything -
    // there ir a lot of existing code which just calls SetScrollbar() without
    // specifying the last parameter even though it doesn't need at all to
    // refresh the window immediately
    if ( refresh && needsRefresh )
    {
        // and update the window
        Refresh();
        Update();
    }
}

// ----------------------------------------------------------------------------
// geometry
// ----------------------------------------------------------------------------

wxSize wxScrollBar::DoGetBestClientSize() const
{
    // this dimension is completely arbitrary
    static const wxCoord SIZE = 140;

    wxSize size = m_renderer->GetScrollbarArrowSize();
    if ( IsVertical() )
    {
        size.y = SIZE;
    }
    else // horizontal
    {
        size.x = SIZE;
    }

    return size;
}

wxScrollArrows::Arrow wxScrollBar::HitTest(const wxPoint& pt) const
{
    switch ( m_renderer->HitTestScrollbar(this, pt) )
    {
        case wxHT_SCROLLBAR_ARROW_LINE_1:
            return wxScrollArrows::Arrow_First;

        case wxHT_SCROLLBAR_ARROW_LINE_2:
            return wxScrollArrows::Arrow_Second;

        default:
            return wxScrollArrows::Arrow_None;
    }
}

// ----------------------------------------------------------------------------
// drawing
// ----------------------------------------------------------------------------

void wxScrollBar::OnIdle(wxIdleEvent& event)
{
    UpdateThumb();
    event.Skip();
}

void wxScrollBar::UpdateThumb()
{
    if ( m_dirty )
    {
        for ( size_t n = 0; n < WXSIZEOF(m_elementsState); n++ )
        {
            if ( m_elementsState[n] & wxCONTROL_DIRTY )
            {
                wxRect rect = GetRenderer()->GetScrollbarRect(this, (Element)n);

                if ( rect.width && rect.height )
                {
                    // we try to avoid redrawing the entire shaft (which might
                    // be quite long) if possible by only redrawing the area
                    // wich really changed
                    if ( (n == Element_Bar_1 || n == Element_Bar_2) &&
                            (m_thumbPosOld != -1) )
                    {
                        // the less efficient but more reliable (i.e. this will
                        // probably work everywhere) version: refresh the
                        // distance covered by thumb since the last update
#if 0
                        wxRect rectOld =
                            GetRenderer()->GetScrollbarRect(this,
                                                            (Element)n,
                                                            m_thumbPosOld);
                        if ( IsVertical() )
                        {
                            if ( n == Element_Bar_1 )
                                rect.SetTop(rectOld.GetBottom());
                            else
                                rect.SetBottom(rectOld.GetBottom());
                        }
                        else // horizontal
                        {
                            if ( n == Element_Bar_1 )
                                rect.SetLeft(rectOld.GetRight());
                            else
                                rect.SetRight(rectOld.GetRight());
                        }
#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);
#endif // 0/1
                    }

#ifdef WXDEBUG_SCROLLBAR
        static bool s_refreshDebug = FALSE;
        if ( s_refreshDebug )
        {
            wxClientDC dc(this);
            dc.SetBrush(*wxCYAN_BRUSH);
            dc.SetPen(*wxTRANSPARENT_PEN);
            dc.DrawRectangle(rect);

            // under Unix we use "--sync" X option for this
            #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
                ::GdiFlush();
                ::Sleep(200);
            #endif // __WXMSW__
        }
#endif // WXDEBUG_SCROLLBAR

                    Refresh(FALSE, &rect);
                }

                m_elementsState[n] &= ~wxCONTROL_DIRTY;
            }
        }

        m_dirty = FALSE;
    }
}

void wxScrollBar::DoDraw(wxControlRenderer *renderer)
{
    renderer->DrawScrollbar(this, m_thumbPosOld);

    // clear all dirty flags
    m_dirty = FALSE;
    m_thumbPosOld = -1;
}

// ----------------------------------------------------------------------------
// state flags
// ----------------------------------------------------------------------------

static inline wxScrollBar::Element ElementForArrow(wxScrollArrows::Arrow arrow)
{
    return arrow == wxScrollArrows::Arrow_First
            ? wxScrollBar::Element_Arrow_Line_1
            : wxScrollBar::Element_Arrow_Line_2;
}

int wxScrollBar::GetArrowState(wxScrollArrows::Arrow arrow) const
{
    return GetState(ElementForArrow(arrow));
}

void wxScrollBar::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
{
    Element which = ElementForArrow(arrow);
    int state = GetState(which);
    if ( set )
        state |= flag;
    else
        state &= ~flag;

    SetState(which, state);
}

int wxScrollBar::GetState(Element which) const
{
    // if the entire scrollbar is disabled, all of its elements are too
    int flags = m_elementsState[which];
    if ( !IsEnabled() )
        flags |= wxCONTROL_DISABLED;

    return flags;
}

void wxScrollBar::SetState(Element which, int flags)
{
    if ( (int)(m_elementsState[which] & ~wxCONTROL_DIRTY) != flags )
    {
        m_elementsState[which] = flags | wxCONTROL_DIRTY;

        m_dirty = TRUE;
    }
}

// ----------------------------------------------------------------------------
// input processing
// ----------------------------------------------------------------------------

bool wxScrollBar::OnArrow(wxScrollArrows::Arrow arrow)
{
    int oldThumbPos = GetThumbPosition();
    PerformAction(arrow == wxScrollArrows::Arrow_First
                    ? wxACTION_SCROLL_LINE_UP
                    : wxACTION_SCROLL_LINE_DOWN);

    // did we scroll till the end?
    return GetThumbPosition() != oldThumbPos;
}

bool wxScrollBar::PerformAction(const wxControlAction& action,
                                long numArg,
                                const wxString& strArg)
{
    int thumbOld = m_thumbPos;

    bool notify = FALSE; // send an event about the change?

    wxEventType scrollType;

    // test for thumb move first as these events happen in quick succession
    if ( action == wxACTION_SCROLL_THUMB_MOVE )
    {
        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 )
    {
        scrollType = wxEVT_SCROLLWIN_LINEUP;
        ScrollLines(-1);
    }
    else if ( action == wxACTION_SCROLL_LINE_DOWN )
    {
        scrollType = wxEVT_SCROLLWIN_LINEDOWN;
        ScrollLines(1);
    }
    else if ( action == wxACTION_SCROLL_PAGE_UP )
    {
        scrollType = wxEVT_SCROLLWIN_PAGEUP;
        ScrollPages(-1);
    }
    else if ( action == wxACTION_SCROLL_PAGE_DOWN )
    {
        scrollType = wxEVT_SCROLLWIN_PAGEDOWN;
        ScrollPages(1);
    }
    else if ( action == wxACTION_SCROLL_START )
    {
        scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
        ScrollToStart();
    }
    else if ( action == wxACTION_SCROLL_END )
    {
        scrollType = wxEVT_SCROLLWIN_THUMBRELEASE; // anything better?
        ScrollToEnd();
    }
    else if ( action == wxACTION_SCROLL_THUMB_DRAG )
    {
        // we won't use it but this line suppresses the compiler
        // warning about "variable may be used without having been
        // initialized"
        scrollType = wxEVT_NULL;
    }
    else if ( action == wxACTION_SCROLL_THUMB_RELEASE )
    {
        // always notify about this
        notify = TRUE;
        scrollType = wxEVT_SCROLLWIN_THUMBRELEASE;
    }
    else
        return wxControl::PerformAction(action, numArg, strArg);

    // has scrollbar position changed?
    bool changed = m_thumbPos != thumbOld;
    if ( notify || changed )
    {
        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;
        }

        wxScrollWinEvent event(scrollType, m_thumbPos,
                               IsVertical() ? wxVERTICAL : wxHORIZONTAL);
        event.SetEventObject(this);
        GetParent()->GetEventHandler()->ProcessEvent(event);
    }

    return TRUE;
}

void wxScrollBar::ScrollToStart()
{
    DoSetThumb(0);
}

void wxScrollBar::ScrollToEnd()
{
    DoSetThumb(m_range - m_thumbSize);
}

bool wxScrollBar::ScrollLines(int nLines)
{
    DoSetThumb(m_thumbPos + nLines);
    return TRUE;
}

bool wxScrollBar::ScrollPages(int nPages)
{
    DoSetThumb(m_thumbPos + nPages*m_pageSize);
    return TRUE;
}

// ============================================================================
// scroll bar input handler
// ============================================================================

// ----------------------------------------------------------------------------
// wxScrollBarTimer
// ----------------------------------------------------------------------------

wxScrollBarTimer::wxScrollBarTimer(wxStdScrollBarInputHandler *handler,
                                   const wxControlAction& action,
                                   wxScrollBar *control)
{
    m_handler = handler;
    m_action = action;
    m_control = control;
}

bool wxScrollBarTimer::DoNotify()
{
    return m_handler->OnScrollTimer(m_control, m_action);
}

// ----------------------------------------------------------------------------
// wxStdScrollBarInputHandler
// ----------------------------------------------------------------------------

wxStdScrollBarInputHandler::wxStdScrollBarInputHandler(wxRenderer *renderer,
                                                       wxInputHandler *handler)
                          : wxStdInputHandler(handler)
{
    m_renderer = renderer;
    m_winCapture = NULL;
    m_htLast = wxHT_NOWHERE;
    m_timerScroll = NULL;
}

wxStdScrollBarInputHandler::~wxStdScrollBarInputHandler()
{
    // normally, it's NULL by now but just in case the user somehow managed to
    // keep the mouse captured until now...
    delete m_timerScroll;
}

void wxStdScrollBarInputHandler::SetElementState(wxScrollBar *control,
                                                 int flag,
                                                 bool doIt)
{
    if ( m_htLast > wxHT_SCROLLBAR_FIRST && m_htLast < wxHT_SCROLLBAR_LAST )
    {
        wxScrollBar::Element
            elem = (wxScrollBar::Element)(m_htLast - wxHT_SCROLLBAR_FIRST - 1);

        int flags = control->GetState(elem);
        if ( doIt )
            flags |= flag;
        else
            flags &= ~flag;
        control->SetState(elem, flags);
    }
}

bool wxStdScrollBarInputHandler::OnScrollTimer(wxScrollBar *scrollbar,
                                               const wxControlAction& action)
{
    int oldThumbPos = scrollbar->GetThumbPosition();
    scrollbar->PerformAction(action);
    if ( scrollbar->GetThumbPosition() != oldThumbPos )
        return TRUE;

    // we scrolled till the end
    m_timerScroll->Stop();

    return FALSE;
}

void wxStdScrollBarInputHandler::StopScrolling(wxScrollBar *control)
{
    // return everything to the normal state
    if ( m_winCapture )
    {
        m_winCapture->ReleaseMouse();
        m_winCapture = NULL;
    }

    m_btnCapture = -1;

    if ( m_timerScroll )
    {
        delete m_timerScroll;
        m_timerScroll = NULL;
    }

    // unpress the arrow and highlight the current element
    Press(control, FALSE);
}

wxCoord
wxStdScrollBarInputHandler::GetMouseCoord(const wxScrollBar *scrollbar,
                                          const wxMouseEvent& event) const
{
    wxPoint pt = event.GetPosition();
    return scrollbar->GetWindowStyle() & wxVERTICAL ? pt.y : pt.x;
}

void wxStdScrollBarInputHandler::HandleThumbMove(wxScrollBar *scrollbar,
                                                 const wxMouseEvent& event)
{
    int thumbPos = GetMouseCoord(scrollbar, event) - m_ofsMouse;
    thumbPos = m_renderer->PixelToScrollbar(scrollbar, thumbPos);
    scrollbar->PerformAction(wxACTION_SCROLL_THUMB_MOVE, thumbPos);
}

bool wxStdScrollBarInputHandler::HandleKey(wxInputConsumer *consumer,
                                           const wxKeyEvent& event,
                                           bool pressed)
{
    // we only react to the key presses here
    if ( pressed )
    {
        wxControlAction action;
        switch ( event.GetKeyCode() )
        {
            case WXK_DOWN:
            case WXK_RIGHT:     action = wxACTION_SCROLL_LINE_DOWN; break;
            case WXK_UP:
            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;
        }

        if ( !!action )
        {
            consumer->PerformAction(action);

            return TRUE;
        }
    }

    return wxStdInputHandler::HandleKey(consumer, event, pressed);
}

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) )
    {
        // determine which part of the window mouse is in
        wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);
        wxHitTest ht = m_renderer->HitTestScrollbar
                                   (
                                    scrollbar,
                                    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 ( event.ButtonDown() || event.ButtonDClick() )
        {
            if ( !m_winCapture )
            {
                m_btnCapture = btn;
                m_winCapture = consumer->GetInputWindow();
                m_winCapture->CaptureMouse();

                // generate the command
                bool hasAction = TRUE;
                wxControlAction action;
                switch ( ht )
                {
                    case wxHT_SCROLLBAR_ARROW_LINE_1:
                        action = wxACTION_SCROLL_LINE_UP;
                        break;

                    case wxHT_SCROLLBAR_ARROW_LINE_2:
                        action = wxACTION_SCROLL_LINE_DOWN;
                        break;

                    case wxHT_SCROLLBAR_BAR_1:
                        action = wxACTION_SCROLL_PAGE_UP;
                        m_ptStartScrolling = event.GetPosition();
                        break;

                    case wxHT_SCROLLBAR_BAR_2:
                        action = wxACTION_SCROLL_PAGE_DOWN;
                        m_ptStartScrolling = event.GetPosition();
                        break;

                    case wxHT_SCROLLBAR_THUMB:
                        consumer->PerformAction(wxACTION_SCROLL_THUMB_DRAG);
                        m_ofsMouse = GetMouseCoord(scrollbar, event) -
                                     m_renderer->ScrollbarToPixel(scrollbar);

                        // fall through: there is no immediate action

                    default:
                        hasAction = FALSE;
                }

                // remove highlighting
                Highlight(scrollbar, FALSE);
                m_htLast = ht;

                // and press the arrow or highlight thumb now instead
                if ( m_htLast == wxHT_SCROLLBAR_THUMB )
                    Highlight(scrollbar, TRUE);
                else
                    Press(scrollbar, TRUE);

                // start dragging
                if ( hasAction )
                {
                    m_timerScroll = new wxScrollBarTimer(this, action,
                                                         scrollbar);
                    m_timerScroll->StartAutoScroll();
                }
                //else: no (immediate) action

            }
            //else: mouse already captured, nothing to do
        }
        // release mouse if the *same* button went up
        else if ( btn == m_btnCapture )
        {
            if ( m_winCapture )
            {
                StopScrolling(scrollbar);

                // if we were dragging the thumb, send the last event
                if ( m_htLast == wxHT_SCROLLBAR_THUMB )
                {
                    scrollbar->PerformAction(wxACTION_SCROLL_THUMB_RELEASE);
                }

                m_htLast = ht;
                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") );
            }
        }
    }

    return wxStdInputHandler::HandleMouse(consumer, event);
}

bool wxStdScrollBarInputHandler::HandleMouseMove(wxInputConsumer *consumer,
                                                 const wxMouseEvent& event)
{
    wxScrollBar *scrollbar = wxStaticCast(consumer->GetInputWindow(), wxScrollBar);

    if ( m_winCapture )
    {
        if ( (m_htLast == wxHT_SCROLLBAR_THUMB) && event.Moving() )
        {
            // 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;
        }

        // no other changes are possible while the mouse is captured
        return FALSE;
    }

    bool isArrow = scrollbar->GetArrows().HandleMouseMove(event);

    if ( event.Moving() )
    {
        wxHitTest ht = m_renderer->HitTestScrollbar
                                   (
                                    scrollbar,
                                    event.GetPosition()
                                   );
        if ( ht == m_htLast )
        {
            // nothing changed
            return FALSE;
        }

#ifdef DEBUG_MOUSE
        wxLogDebug("Scrollbar::OnMouseMove: ht = %d", ht);
#endif // DEBUG_MOUSE

        Highlight(scrollbar, FALSE);
        m_htLast = ht;

        if ( !isArrow )
            Highlight(scrollbar, TRUE);
        //else: already done by wxScrollArrows::HandleMouseMove
    }
    else if ( event.Leaving() )
    {
        if ( !isArrow )
            Highlight(scrollbar, FALSE);

        m_htLast = wxHT_NOWHERE;
    }
    else // event.Entering()
    {
        // we don't process this event
        return FALSE;
    }

    // we did something
    return TRUE;
}

#endif // wxUSE_SCROLLBAR

// ----------------------------------------------------------------------------
// wxScrollTimer
// ----------------------------------------------------------------------------

wxScrollTimer::wxScrollTimer()
{
    m_skipNext = FALSE;
}

void wxScrollTimer::StartAutoScroll()
{
    // start scrolling immediately
    if ( !DoNotify() )
    {
        // ... and end it too
        return;
    }

    // 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;
    Start(200); // FIXME: hardcoded delay
}

void wxScrollTimer::Notify()
{
    if ( m_skipNext )
    {
        // scroll normally now - reduce the delay
        Stop();
        Start(50); // FIXME: hardcoded delay

        m_skipNext = FALSE;
    }
    else
    {
        // if DoNotify() returns false, we're already deleted by the timer
        // event handler, so don't do anything else here
        (void)DoNotify();
    }
}

