+#ifndef WX_PRECOMP
+ #include "wx/timer.h"
+#endif
+
+#include "wx/spinctrl.h"
+
+#ifdef __VMS__
+#pragma message disable nosimpint
+#endif
+#include <Xm/ArrowBG.h>
+#include <Xm/ArrowB.h>
+#ifdef __VMS__
+#pragma message enable nosimpint
+#endif
+
+#include "wx/motif/private.h"
+
+// helper class
+enum ArrowDirection
+{
+ wxARROW_UP,
+ wxARROW_DOWN,
+ wxARROW_LEFT,
+ wxARROW_RIGHT
+};
+
+class wxArrowButtonTimer;
+class wxArrowButton;
+
+// ----------------------------------------------------------------------------
+// wxArrowButtonTimer
+// ----------------------------------------------------------------------------
+
+static const unsigned int TICK_BEFORE_START = 10;
+static const unsigned int TICK_BEFORE_EXPONENTIAL = 40;
+static const unsigned int MAX_INCREMENT = 150;
+static const unsigned int TICK_INTERVAL = 113;
+
+class wxArrowButtonTimer : public wxTimer
+{
+public:
+ wxArrowButtonTimer( wxArrowButton* btn, int sign )
+ : m_sign( sign ),
+ m_button( btn )
+ { Reset(); };
+
+ void Notify();
+ void Reset() { m_ticks = 0; m_increment = 1; }
+private:
+ unsigned int m_ticks;
+ unsigned int m_increment;
+ int m_sign;
+ wxArrowButton* m_button;
+};
+
+// ----------------------------------------------------------------------------
+// wxArrowButton
+// ----------------------------------------------------------------------------
+
+class wxArrowButton : public wxControl
+{
+ friend class wxArrowButtonTimer;
+public:
+ wxArrowButton( int increment )
+ : m_increment( increment ),
+ m_timer( 0 ) {}
+
+ wxArrowButton( wxSpinButton* parent, wxWindowID id, ArrowDirection d,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize, int increment = 1 )
+ : wxControl(),
+ m_increment( increment ),
+ m_timer( 0 )
+ {
+ Create( parent, id, d, pos, size );
+ }
+
+ virtual ~wxArrowButton()
+ { delete m_timer; }
+
+ bool Create( wxSpinButton* parent, wxWindowID id, ArrowDirection d,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize );
+private:
+ // creates a new timer object, or stops the currently running one
+ wxTimer* GetFreshTimer();
+ wxSpinButton* GetSpinButton() { return (wxSpinButton*)GetParent(); }
+ static void SpinButtonCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) );
+ static void StartTimerCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) );
+
+ static void StopTimerCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) );
+
+ int m_increment;
+ wxArrowButtonTimer* m_timer;
+};
+
+// ----------------------------------------------------------------------------
+// wxArrowButtonTimer implementation
+// ----------------------------------------------------------------------------
+
+void wxArrowButtonTimer::Notify()
+{
+ ++m_ticks;
+ if( m_ticks < TICK_BEFORE_START ) return;
+ // increment every other tick
+ if( m_ticks <= TICK_BEFORE_EXPONENTIAL && m_ticks & 1 )
+ return;
+ if( m_ticks > TICK_BEFORE_EXPONENTIAL )
+ m_increment = 2 * m_increment;
+ if( m_increment >= MAX_INCREMENT ) m_increment = MAX_INCREMENT;
+ m_button->GetSpinButton()->Increment( m_sign * m_increment );
+}
+
+// ----------------------------------------------------------------------------
+// wxArrowButton implementation
+// ----------------------------------------------------------------------------
+
+wxTimer* wxArrowButton::GetFreshTimer()
+{
+ if( m_timer )
+ {
+ m_timer->Stop();
+ m_timer->Reset();
+ }
+ else
+ m_timer = new wxArrowButtonTimer( this, m_increment );
+
+ return m_timer;
+}
+
+void wxArrowButton::SpinButtonCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) )
+{
+ if( !wxGetWindowFromTable( w ) )
+ // Widget has been deleted!
+ return;
+
+ wxArrowButton* btn = (wxArrowButton*)clientData;
+
+ btn->GetSpinButton()->Increment( btn->m_increment );
+}
+
+void wxArrowButton::StartTimerCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) )
+{
+ if( !wxGetWindowFromTable( w ) )
+ // Widget has been deleted!
+ return;
+
+ wxArrowButton* btn = (wxArrowButton*)clientData;
+ btn->GetFreshTimer()->Start( TICK_INTERVAL );
+}
+
+void wxArrowButton::StopTimerCallback( Widget w, XtPointer clientData,
+ XtPointer WXUNUSED(ptr) )
+{
+ if( !wxGetWindowFromTable( w ) )
+ // Widget has been deleted!
+ return;
+
+ wxArrowButton* btn = (wxArrowButton*)clientData;
+ delete btn->m_timer;
+ btn->m_timer = 0;
+}
+
+bool wxArrowButton::Create( wxSpinButton* parent,
+ wxWindowID WXUNUSED(id),
+ ArrowDirection d,
+ const wxPoint& pos, const wxSize& size )
+{
+ wxCHECK_MSG( parent, false, _T("must have a valid parent") );
+
+ int arrow_dir = XmARROW_UP;
+
+ switch( d )
+ {
+ case wxARROW_UP:
+ arrow_dir = XmARROW_UP;
+ break;
+ case wxARROW_DOWN:
+ arrow_dir = XmARROW_DOWN;
+ break;
+ case wxARROW_LEFT:
+ arrow_dir = XmARROW_LEFT;
+ break;
+ case wxARROW_RIGHT:
+ arrow_dir = XmARROW_RIGHT;
+ break;
+ }
+
+ parent->AddChild( this );
+ PreCreation();
+
+ Widget parentWidget = (Widget) parent->GetClientWidget();
+ m_mainWidget = (WXWidget) XtVaCreateManagedWidget( "XmArrowButton",
+ xmArrowButtonWidgetClass,
+ parentWidget,
+ XmNarrowDirection, arrow_dir,
+ XmNborderWidth, 0,
+ XmNshadowThickness, 0,
+ NULL );
+
+ XtAddCallback( (Widget) m_mainWidget,
+ XmNactivateCallback, (XtCallbackProc) SpinButtonCallback,
+ (XtPointer) this );
+ XtAddCallback( (Widget) m_mainWidget,
+ XmNarmCallback, (XtCallbackProc) StartTimerCallback,
+ (XtPointer) this );
+ XtAddCallback( (Widget) m_mainWidget,
+ XmNactivateCallback, (XtCallbackProc) StopTimerCallback,
+ (XtPointer) this );
+
+ PostCreation();
+ AttachWidget( parent, m_mainWidget, (WXWidget) NULL,
+ pos.x, pos.y, size.x, size.y );
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// wxSpinButton
+// ----------------------------------------------------------------------------
+