1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/spinbutt.cpp 
   7 // Copyright:   (c) Robert Roebling 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  14 #include "wx/spinctrl.h" 
  20 #include "wx/textctrl.h"    // for wxEVT_COMMAND_TEXT_UPDATED 
  22 #include "wx/gtk/private.h" 
  24 //----------------------------------------------------------------------------- 
  26 //----------------------------------------------------------------------------- 
  28 static const float sensitivity 
= 0.02; 
  30 //----------------------------------------------------------------------------- 
  32 //----------------------------------------------------------------------------- 
  34 extern bool   g_blockEventsOnDrag
; 
  36 //----------------------------------------------------------------------------- 
  38 //----------------------------------------------------------------------------- 
  41 static void gtk_spinctrl_callback( GtkWidget 
*WXUNUSED(widget
), wxSpinCtrl 
*win 
) 
  43     if (g_isIdle
) wxapp_install_idle_handler(); 
  45     if (!win
->m_hasVMT
) return; 
  46     if (g_blockEventsOnDrag
) return; 
  48     wxCommandEvent 
event( wxEVT_COMMAND_SPINCTRL_UPDATED
, win
->GetId()); 
  49     event
.SetEventObject( win 
); 
  51     // note that we don't use wxSpinCtrl::GetValue() here because it would 
  52     // adjust the value to fit into the control range and this means that we 
  53     // would never be able to enter an "invalid" value in the control, even 
  54     // temporarily - and trying to enter 10 into the control which accepts the 
  55     // values in range 5..50 is then, ummm, quite challenging (hint: you can't 
  57     event
.SetInt( (int)ceil(win
->m_adjust
->value
) ); 
  58     win
->GetEventHandler()->ProcessEvent( event 
); 
  62 //----------------------------------------------------------------------------- 
  64 //----------------------------------------------------------------------------- 
  68 gtk_spinctrl_text_changed_callback( GtkWidget 
*WXUNUSED(widget
), wxSpinCtrl 
*win 
) 
  70     if (!win
->m_hasVMT
) return; 
  73         wxapp_install_idle_handler(); 
  75     wxCommandEvent 
event( wxEVT_COMMAND_TEXT_UPDATED
, win
->GetId() ); 
  76     event
.SetEventObject( win 
); 
  79     event
.SetInt( (int)ceil(win
->m_adjust
->value
) ); 
  80     win
->GetEventHandler()->ProcessEvent( event 
); 
  84 //----------------------------------------------------------------------------- 
  86 //----------------------------------------------------------------------------- 
  88 IMPLEMENT_DYNAMIC_CLASS(wxSpinCtrl
,wxControl
) 
  90 BEGIN_EVENT_TABLE(wxSpinCtrl
, wxControl
) 
  91     EVT_CHAR(wxSpinCtrl::OnChar
) 
  94 bool wxSpinCtrl::Create(wxWindow 
*parent
, wxWindowID id
, 
  95                         const wxString
& value
, 
  96                         const wxPoint
& pos
,  const wxSize
& size
, 
  98                         int min
, int max
, int initial
, 
 102     m_acceptsFocus 
= true; 
 104     if (!PreCreation( parent
, pos
, size 
) || 
 105         !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name 
)) 
 107         wxFAIL_MSG( wxT("wxSpinCtrl creation failed") ); 
 113     m_adjust 
= (GtkAdjustment
*) gtk_adjustment_new( initial
, min
, max
, 1.0, 5.0, 0.0); 
 115     m_widget 
= gtk_spin_button_new( m_adjust
, 1, 0 ); 
 117     gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(m_widget
), 
 118                               (int)(m_windowStyle 
& wxSP_WRAP
) ); 
 122     m_parent
->DoAddChild( this ); 
 131 void wxSpinCtrl::GtkDisableEvents() 
 133     g_signal_handlers_disconnect_by_func (m_adjust
, 
 134                                           (gpointer
) gtk_spinctrl_callback
, 
 136     g_signal_handlers_disconnect_by_func (m_widget
, 
 137                                           (gpointer
) gtk_spinctrl_text_changed_callback
, 
 141 void wxSpinCtrl::GtkEnableEvents() 
 143     g_signal_connect (m_adjust
, "value_changed", 
 144                       G_CALLBACK (gtk_spinctrl_callback
), 
 146     g_signal_connect (m_widget
, "changed", 
 147                       G_CALLBACK (gtk_spinctrl_text_changed_callback
), 
 151 int wxSpinCtrl::GetMin() const 
 153     wxCHECK_MSG( (m_widget 
!= NULL
), 0, wxT("invalid spin button") ); 
 155     return (int)ceil(m_adjust
->lower
); 
 158 int wxSpinCtrl::GetMax() const 
 160     wxCHECK_MSG( (m_widget 
!= NULL
), 0, wxT("invalid spin button") ); 
 162     return (int)ceil(m_adjust
->upper
); 
 165 int wxSpinCtrl::GetValue() const 
 167     wxCHECK_MSG( (m_widget 
!= NULL
), 0, wxT("invalid spin button") ); 
 169     gtk_spin_button_update( GTK_SPIN_BUTTON(m_widget
) ); 
 171     return (int)ceil(m_adjust
->value
); 
 174 void wxSpinCtrl::SetValue( const wxString
& value 
) 
 176     wxCHECK_RET( (m_widget 
!= NULL
), wxT("invalid spin button") ); 
 179     if ( (wxSscanf(value
, wxT("%d"), &n
) == 1) ) 
 186         // invalid number - set text as is (wxMSW compatible) 
 188         gtk_entry_set_text( GTK_ENTRY(m_widget
), wxGTK_CONV( value 
) ); 
 193 void wxSpinCtrl::SetValue( int value 
) 
 195     wxCHECK_RET( (m_widget 
!= NULL
), wxT("invalid spin button") ); 
 197     float fpos 
= (float)value
; 
 199     if (fabs(fpos
-m_adjust
->value
) < sensitivity
) return; 
 201     m_adjust
->value 
= fpos
; 
 204     g_signal_emit_by_name (m_adjust
, "value_changed"); 
 208 void wxSpinCtrl::SetSelection(long from
, long to
) 
 210     // translate from wxWidgets conventions to GTK+ ones: (-1, -1) means the 
 212     if ( from 
== -1 && to 
== -1 ) 
 218     gtk_editable_select_region( GTK_EDITABLE(m_widget
), (gint
)from
, (gint
)to 
); 
 221 void wxSpinCtrl::SetRange(int minVal
, int maxVal
) 
 223     wxCHECK_RET( (m_widget 
!= NULL
), wxT("invalid spin button") ); 
 225     float fmin 
= (float)minVal
; 
 226     float fmax 
= (float)maxVal
; 
 228     if ((fabs(fmin
-m_adjust
->lower
) < sensitivity
) && 
 229         (fabs(fmax
-m_adjust
->upper
) < sensitivity
)) 
 234     m_adjust
->lower 
= fmin
; 
 235     m_adjust
->upper 
= fmax
; 
 237     g_signal_emit_by_name (m_adjust
, "changed"); 
 239     // these two calls are required due to some bug in GTK 
 244 void wxSpinCtrl::OnChar( wxKeyEvent 
&event 
) 
 246     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid spin ctrl") ); 
 248     if (event
.GetKeyCode() == WXK_RETURN
) 
 250         wxWindow 
*top_frame 
= m_parent
; 
 251         while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 252             top_frame 
= top_frame
->GetParent(); 
 254         if ( GTK_IS_WINDOW(top_frame
->m_widget
) ) 
 256             GtkWindow 
*window 
= GTK_WINDOW(top_frame
->m_widget
); 
 259                 GtkWidget 
*widgetDef 
= window
->default_widget
; 
 263                     gtk_widget_activate(widgetDef
); 
 270     if ((event
.GetKeyCode() == WXK_RETURN
) && (m_windowStyle 
& wxTE_PROCESS_ENTER
)) 
 272         wxCommandEvent 
evt( wxEVT_COMMAND_TEXT_ENTER
, m_windowId 
); 
 273         evt
.SetEventObject(this); 
 274         GtkSpinButton 
*gsb 
= GTK_SPIN_BUTTON(m_widget
); 
 275         wxString val 
= wxGTK_CONV_BACK( gtk_entry_get_text( &gsb
->entry 
) ); 
 276         evt
.SetString( val 
); 
 277         if (GetEventHandler()->ProcessEvent(evt
)) return; 
 283 bool wxSpinCtrl::IsOwnGtkWindow( GdkWindow 
*window 
) 
 285     if (GTK_SPIN_BUTTON(m_widget
)->entry
.text_area 
== window
) return true; 
 287     if (GTK_SPIN_BUTTON(m_widget
)->panel 
== window
) return true; 
 292 wxSize 
wxSpinCtrl::DoGetBestSize() const 
 294     wxSize 
ret( wxControl::DoGetBestSize() ); 
 295     wxSize 
best(95, ret
.y
); 
 302 wxSpinCtrl::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
 304     // TODO: overload to accept functions like gtk_spin_button_new? 
 305     // Until then use a similar type 
 306     return GetDefaultAttributesFromGTKWidget(gtk_entry_new
, true);