scrolbar.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 959 行 · 第 1/2 页

CPP
959
字号
/////////////////////////////////////////////////////////////////////////////
// Name:        univ/scrolbar.cpp
// Purpose:     wxScrollBar implementation
// Author:      Vadim Zeitlin
// Modified by:
// Created:     20.08.00
// RCS-ID:      $Id: scrolbar.cpp,v 1.25 2005/07/28 21:38:43 VZ Exp $
// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

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

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

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #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)
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, validator, 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 everything 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::OnInternalIdle()
{
    UpdateThumb();
    wxControl::OnInternalIdle();
}

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)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?