--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: univ/spinbutt.cpp
+// Purpose: implementation of the universal version of wxSpinButton
+// Author: Vadim Zeitlin
+// Modified by:
+// Created: 21.01.01
+// RCS-ID: $Id$
+// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#ifdef __GNUG__
+ #pragma implementation "spinbutbase.h"
+ #pragma implementation "univspinbutt.h"
+#endif
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+#endif
+
+#include "wx/spinbutt.h"
+
+#if wxUSE_SPINBTN
+
+#include "wx/univ/renderer.h"
+#include "wx/univ/inphand.h"
+#include "wx/univ/theme.h"
+
+// ============================================================================
+// implementation of wxSpinButton
+// ============================================================================
+
+IMPLEMENT_DYNAMIC_CLASS(wxSpinEvent, wxNotifyEvent)
+IMPLEMENT_DYNAMIC_CLASS(wxSpinButton, wxControl)
+
+// ----------------------------------------------------------------------------
+// creation
+// ----------------------------------------------------------------------------
+
+#ifdef __VISUALC__
+ // warning C4355: 'this' : used in base member initializer list
+ #pragma warning(disable:4355) // so what? disable it...
+#endif
+
+wxSpinButton::wxSpinButton()
+ : m_arrows(this)
+{
+ Init();
+}
+
+wxSpinButton::wxSpinButton(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name)
+ : m_arrows(this)
+{
+ Init();
+
+ (void)Create(parent, id, pos, size, style, name);
+}
+
+#ifdef __VISUALC__
+ // warning C4355: 'this' : used in base member initializer list
+ #pragma warning(default:4355)
+#endif
+
+void wxSpinButton::Init()
+{
+ for ( size_t n = 0; n < WXSIZEOF(m_arrowsState); n++ )
+ {
+ m_arrowsState[n] = 0;
+ }
+
+ m_value = 0;
+}
+
+bool wxSpinButton::Create(wxWindow *parent,
+ wxWindowID id,
+ const wxPoint& pos,
+ const wxSize& size,
+ long style,
+ const wxString& name)
+{
+ // the spin buttons never have the border
+ style &= ~wxBORDER_MASK;
+
+ if ( !wxSpinButtonBase::Create(parent, id, pos, size, style,
+ wxDefaultValidator, name) )
+ return FALSE;
+
+ SetBestSize(size);
+
+ CreateInputHandler(wxINP_HANDLER_SPINBTN);
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// value access
+// ----------------------------------------------------------------------------
+
+void wxSpinButton::SetRange(int minVal, int maxVal)
+{
+ wxSpinButtonBase::SetRange(minVal, maxVal);
+
+ // because the arrows disabled state might have changed - we don't check if
+ // it really changed or not because SetRange() is called rarely enough and
+ // son an extre refresh here doesn't really hurt
+ Refresh();
+}
+
+int wxSpinButton::GetValue() const
+{
+ return m_value;
+}
+
+void wxSpinButton::SetValue(int val)
+{
+ if ( val != m_value )
+ {
+ m_value = val;
+
+ Refresh();
+ }
+}
+
+int wxSpinButton::NormalizeValue(int value) const
+{
+ if ( value > m_max )
+ {
+ if ( GetWindowStyleFlag() & wxSP_WRAP )
+ value = m_min + (value - m_max) % (m_max - m_min);
+ else
+ value = m_max;
+ }
+ else if ( value < m_min )
+ {
+ if ( GetWindowStyleFlag() & wxSP_WRAP )
+ value = m_max - (m_min - value) % (m_max - m_min);
+ else
+ value = m_min;
+ }
+
+ return value;
+}
+
+bool wxSpinButton::ChangeValue(int inc)
+{
+ int valueNew = NormalizeValue(m_value + inc);
+
+ if ( valueNew == m_value )
+ {
+ // nothing changed - most likely because we are already at min/max
+ // value
+ return FALSE;
+ }
+
+ wxSpinEvent event(inc > 0 ? wxEVT_SCROLL_LINEUP : wxEVT_SCROLL_LINEDOWN,
+ GetId());
+ event.SetPosition(valueNew);
+ event.SetEventObject(this);
+
+ if ( GetEventHandler()->ProcessEvent(event) && !event.IsAllowed() )
+ {
+ // programm has vetoed the event
+ return FALSE;
+ }
+
+ m_value = valueNew;
+
+ // send wxEVT_SCROLL_THUMBTRACK as well
+ event.SetEventType(wxEVT_SCROLL_THUMBTRACK);
+ (void)GetEventHandler()->ProcessEvent(event);
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// size calculations
+// ----------------------------------------------------------------------------
+
+wxSize wxSpinButton::DoGetBestClientSize() const
+{
+ // a spin button has by default the same size as two scrollbar arrows put
+ // together
+ wxSize size = m_renderer->GetScrollbarArrowSize();
+ if ( IsVertical() )
+ {
+ size.y *= 2;
+ }
+ else
+ {
+ size.x *= 2;
+ }
+
+ return size;
+}
+
+// ----------------------------------------------------------------------------
+// wxControlWithArrows methods
+// ----------------------------------------------------------------------------
+
+int wxSpinButton::GetArrowState(wxScrollArrows::Arrow arrow) const
+{
+ int state = m_arrowsState[arrow];
+
+ // the arrow may also be disabled: either because the control is completely
+ // disabled
+ bool disabled = !IsEnabled();
+
+ if ( !disabled && !(GetWindowStyleFlag() & wxSP_WRAP) )
+ {
+ // ... or because we can't go any further - note that this never
+ // happens if we just wrap
+ if ( IsVertical() )
+ {
+ if ( arrow == wxScrollArrows::Arrow_First )
+ disabled = m_value == m_max;
+ else
+ disabled = m_value == m_min;
+ }
+ else // horizontal
+ {
+ if ( arrow == wxScrollArrows::Arrow_First )
+ disabled = m_value == m_min;
+ else
+ disabled = m_value == m_max;
+ }
+ }
+
+ if ( disabled )
+ {
+ state |= wxCONTROL_DISABLED;
+ }
+
+ return state;
+}
+
+void wxSpinButton::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
+{
+ int state = m_arrowsState[arrow];
+ if ( set )
+ state |= flag;
+ else
+ state &= ~flag;
+
+ if ( state != m_arrowsState[arrow] )
+ {
+ m_arrowsState[arrow] = state;
+ Refresh();
+ }
+}
+
+bool wxSpinButton::OnArrow(wxScrollArrows::Arrow arrow)
+{
+ int valueOld = GetValue();
+
+ wxControlAction action;
+ if ( arrow == wxScrollArrows::Arrow_First )
+ action = IsVertical() ? wxACTION_SPIN_INC : wxACTION_SPIN_DEC;
+ else
+ action = IsVertical() ? wxACTION_SPIN_DEC : wxACTION_SPIN_INC;
+
+ PerformAction(action);
+
+ // did we scroll to the end?
+ return GetValue() != valueOld;
+}
+
+// ----------------------------------------------------------------------------
+// drawing
+// ----------------------------------------------------------------------------
+
+void wxSpinButton::DoDraw(wxControlRenderer *renderer)
+{
+ wxRect rectArrow1, rectArrow2;
+ CalcArrowRects(&rectArrow1, &rectArrow2);
+
+ wxDC& dc = renderer->GetDC();
+ m_arrows.DrawArrow(wxScrollArrows::Arrow_First, dc, rectArrow1);
+ m_arrows.DrawArrow(wxScrollArrows::Arrow_Second, dc, rectArrow2);
+}
+
+// ----------------------------------------------------------------------------
+// geometry
+// ----------------------------------------------------------------------------
+
+void wxSpinButton::CalcArrowRects(wxRect *rect1, wxRect *rect2) const
+{
+ // calculate the rectangles for both arrows: note that normally the 2
+ // arrows are adjacent to each other but if the total control width/height
+ // is odd, we can have 1 pixel between them
+ wxRect rectTotal = GetClientRect();
+
+ *rect1 =
+ *rect2 = rectTotal;
+ if ( IsVertical() )
+ {
+ rect1->height /= 2;
+ rect2->height /= 2;
+
+ rect2->y += rect1->height;
+ if ( rectTotal.height % 2 )
+ rect2->y++;
+ }
+ else // horizontal
+ {
+ rect1->width /= 2;
+ rect2->width /= 2;
+
+ rect2->x += rect1->width;
+ if ( rectTotal.width % 2 )
+ rect2->x++;
+ }
+}
+
+wxScrollArrows::Arrow wxSpinButton::HitTest(const wxPoint& pt) const
+{
+ wxRect rectArrow1, rectArrow2;
+ CalcArrowRects(&rectArrow1, &rectArrow2);
+
+ if ( rectArrow1.Inside(pt) )
+ return wxScrollArrows::Arrow_First;
+ else if ( rectArrow2.Inside(pt) )
+ return wxScrollArrows::Arrow_Second;
+ else
+ return wxScrollArrows::Arrow_None;
+}
+
+// ----------------------------------------------------------------------------
+// input processing
+// ----------------------------------------------------------------------------
+
+bool wxSpinButton::PerformAction(const wxControlAction& action,
+ long numArg,
+ const wxString& strArg)
+{
+ if ( action == wxACTION_SPIN_INC )
+ ChangeValue(+1);
+ else if ( action == wxACTION_SPIN_DEC )
+ ChangeValue(-1);
+ else
+ return wxControl::PerformAction(action, numArg, strArg);
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// wxStdSpinButtonInputHandler
+// ----------------------------------------------------------------------------
+
+wxStdSpinButtonInputHandler::
+wxStdSpinButtonInputHandler(wxInputHandler *inphand)
+ : wxStdInputHandler(inphand)
+{
+}
+
+bool wxStdSpinButtonInputHandler::HandleKey(wxControl *control,
+ const wxKeyEvent& event,
+ bool pressed)
+{
+ if ( pressed )
+ {
+ wxControlAction action;
+ switch ( event.GetKeyCode() )
+ {
+ case WXK_DOWN:
+ case WXK_RIGHT:
+ action = wxACTION_SPIN_DEC;
+ break;
+
+ case WXK_UP:
+ case WXK_LEFT:
+ action = wxACTION_SPIN_INC;
+ break;
+ }
+
+ if ( !!action )
+ {
+ control->PerformAction(action);
+
+ return TRUE;
+ }
+ }
+
+ return wxStdInputHandler::HandleKey(control, event, pressed);
+}
+
+bool wxStdSpinButtonInputHandler::HandleMouse(wxControl *control,
+ const wxMouseEvent& event)
+{
+ wxSpinButton *spinbtn = wxStaticCast(control, wxSpinButton);
+
+ if ( spinbtn->GetArrows().HandleMouse(event) )
+ {
+ // don't refresh, everything is already done
+ return FALSE;
+ }
+
+ return wxStdInputHandler::HandleMouse(control, event);
+}
+
+bool wxStdSpinButtonInputHandler::HandleMouseMove(wxControl *control,
+ const wxMouseEvent& event)
+{
+ wxSpinButton *spinbtn = wxStaticCast(control, wxSpinButton);
+
+ if ( spinbtn->GetArrows().HandleMouseMove(event) )
+ {
+ // processed by the arrows
+ return FALSE;
+ }
+
+ return wxStdInputHandler::HandleMouseMove(control, event);
+}
+
+
+#endif // wxUSE_SPINBTN