1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/spinbutt.cpp
3 // Purpose:     wxSpinButton
4 // Author:      Robert
5 // Modified by:
6 // Copyright:   (c) Robert Roebling
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #if wxUSE_SPINBTN
14 
15 #include "wx/spinbutt.h"
16 
17 #include <gtk/gtk.h>
18 
19 //-----------------------------------------------------------------------------
20 // data
21 //-----------------------------------------------------------------------------
22 
23 extern bool   g_blockEventsOnDrag;
24 
25 //-----------------------------------------------------------------------------
26 // "value_changed"
27 //-----------------------------------------------------------------------------
28 
29 extern "C" {
30 static void
gtk_value_changed(GtkSpinButton * spinbutton,wxSpinButton * win)31 gtk_value_changed(GtkSpinButton* spinbutton, wxSpinButton* win)
32 {
33     const double value = gtk_spin_button_get_value(spinbutton);
34     const int pos = int(value);
35     const int oldPos = win->m_pos;
36     if (g_blockEventsOnDrag || pos == oldPos)
37     {
38         win->m_pos = pos;
39         return;
40     }
41 
42     wxSpinEvent event(pos > oldPos ? wxEVT_SCROLL_LINEUP : wxEVT_SCROLL_LINEDOWN, win->GetId());
43     event.SetPosition(pos);
44     event.SetEventObject(win);
45 
46     if ((win->HandleWindowEvent( event )) &&
47         !event.IsAllowed() )
48     {
49         /* program has vetoed */
50         // this will cause another "value_changed" signal,
51         // but because pos == oldPos nothing will happen
52         gtk_spin_button_set_value(spinbutton, oldPos);
53         return;
54     }
55 
56     win->m_pos = pos;
57 
58     /* always send a thumbtrack event */
59     wxSpinEvent event2(wxEVT_SCROLL_THUMBTRACK, win->GetId());
60     event2.SetPosition(pos);
61     event2.SetEventObject(win);
62     win->HandleWindowEvent(event2);
63 }
64 }
65 
66 //-----------------------------------------------------------------------------
67 // wxSpinButton
68 //-----------------------------------------------------------------------------
69 
wxSpinButton()70 wxSpinButton::wxSpinButton()
71 {
72     m_pos = 0;
73 }
74 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)75 bool wxSpinButton::Create(wxWindow *parent,
76                           wxWindowID id,
77                           const wxPoint& pos,
78                           const wxSize& size,
79                           long style,
80                           const wxString& name)
81 {
82     if (!PreCreation(parent, pos, size) ||
83         !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name))
84     {
85         wxFAIL_MSG( wxT("wxSpinButton creation failed") );
86         return false;
87     }
88 
89     m_pos = 0;
90 
91     m_widget = gtk_spin_button_new_with_range(0, 100, 1);
92     g_object_ref(m_widget);
93 
94     gtk_entry_set_width_chars(GTK_ENTRY(m_widget), 0);
95 #if GTK_CHECK_VERSION(3,12,0)
96     if (gtk_check_version(3,12,0) == NULL)
97         gtk_entry_set_max_width_chars(GTK_ENTRY(m_widget), 0);
98 #endif
99 #ifdef __WXGTK3__
100     if (gtk_check_version(3,20,0) == NULL)
101     {
102         GTKApplyCssStyle(
103             "entry { min-width:0; padding-left:0; padding-right:0 }"
104             "button.down { border-style:none }");
105     }
106 #endif
107     gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(m_widget),
108                               (int)(m_windowStyle & wxSP_WRAP) );
109 
110     g_signal_connect_after(
111         m_widget, "value_changed", G_CALLBACK(gtk_value_changed), this);
112 
113     m_parent->DoAddChild( this );
114 
115     PostCreation(size);
116 
117     return true;
118 }
119 
GetMin() const120 int wxSpinButton::GetMin() const
121 {
122     wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
123 
124     double min;
125     gtk_spin_button_get_range((GtkSpinButton*)m_widget, &min, NULL);
126     return int(min);
127 }
128 
GetMax() const129 int wxSpinButton::GetMax() const
130 {
131     wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
132 
133     double max;
134     gtk_spin_button_get_range((GtkSpinButton*)m_widget, NULL, &max);
135     return int(max);
136 }
137 
GetValue() const138 int wxSpinButton::GetValue() const
139 {
140     wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
141 
142     return m_pos;
143 }
144 
SetValue(int value)145 void wxSpinButton::SetValue( int value )
146 {
147     wxCHECK_RET( (m_widget != NULL), wxT("invalid spin button") );
148 
149     GtkDisableEvents();
150     gtk_spin_button_set_value((GtkSpinButton*)m_widget, value);
151     m_pos = int(gtk_spin_button_get_value((GtkSpinButton*)m_widget));
152     GtkEnableEvents();
153 }
154 
SetRange(int minVal,int maxVal)155 void wxSpinButton::SetRange(int minVal, int maxVal)
156 {
157     wxCHECK_RET( (m_widget != NULL), wxT("invalid spin button") );
158 
159     GtkDisableEvents();
160     gtk_spin_button_set_range((GtkSpinButton*)m_widget, minVal, maxVal);
161     m_pos = int(gtk_spin_button_get_value((GtkSpinButton*)m_widget));
162     GtkEnableEvents();
163 }
164 
Enable(bool enable)165 bool wxSpinButton::Enable( bool enable )
166 {
167     if (!base_type::Enable(enable))
168         return false;
169 
170     // Work around lack of visual update when enabling
171     if (enable)
172         GTKFixSensitivity(false /* fix even if not under mouse */);
173 
174     return true;
175 }
176 
GtkDisableEvents() const177 void wxSpinButton::GtkDisableEvents() const
178 {
179     g_signal_handlers_block_by_func(m_widget,
180         (gpointer)gtk_value_changed, (void*) this);
181 }
182 
GtkEnableEvents() const183 void wxSpinButton::GtkEnableEvents() const
184 {
185     g_signal_handlers_unblock_by_func(m_widget,
186         (gpointer)gtk_value_changed, (void*) this);
187 }
188 
GTKGetWindow(wxArrayGdkWindows & windows) const189 GdkWindow *wxSpinButton::GTKGetWindow(wxArrayGdkWindows& windows) const
190 {
191 #ifdef __WXGTK3__
192     void wxGTKFindWindow(GtkWidget* widget, wxArrayGdkWindows& windows);
193     wxGTKFindWindow(m_widget, windows);
194     return NULL;
195 #else
196     wxUnusedVar(windows);
197     return GTK_SPIN_BUTTON(m_widget)->panel;
198 #endif
199 }
200 
DoGetBestSize() const201 wxSize wxSpinButton::DoGetBestSize() const
202 {
203     wxSize best = base_type::DoGetBestSize();
204 #ifdef __WXGTK3__
205     GtkStyleContext* sc = gtk_widget_get_style_context(m_widget);
206     GtkBorder pad = { 0, 0, 0, 0 };
207     gtk_style_context_get_padding(sc, gtk_style_context_get_state(sc), &pad);
208     best.x -= pad.left + pad.right;
209 #else
210     gtk_widget_ensure_style(m_widget);
211     int w = PANGO_PIXELS(pango_font_description_get_size(m_widget->style->font_desc));
212     w &= ~1;
213     if (w < 6)
214         w = 6;
215     best.x = w + 2 * m_widget->style->xthickness;
216 #endif
217     CacheBestSize(best);
218     return best;
219 }
220 
221 // static
222 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))223 wxSpinButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
224 {
225     return GetDefaultAttributesFromGTKWidget(gtk_spin_button_new_with_range(0, 100, 1));
226 }
227 
228 #endif // wxUSE_SPINBTN
229