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 "wx/gtk/private/wrapgtk.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 int pos = gtk_spin_button_get_value_as_int(spinbutton);
34 const int oldPos = win->m_pos;
35 if (g_blockEventsOnDrag || pos == oldPos)
36 {
37 win->m_pos = pos;
38 return;
39 }
40
41 // Normally we can determine which way we're going by just comparing the
42 // old and the new values.
43 bool up = pos > oldPos;
44
45 // However we need to account for the possibility of wrapping around.
46 if ( win->HasFlag(wxSP_WRAP) )
47 {
48 // We have no way of distinguishing between wraparound and normal
49 // change when the range is just 1, as pressing either arrow results in
50 // the same change, so don't even try doing it in this case.
51 const int spinMin = win->GetMin();
52 const int spinMax = win->GetMax();
53
54 if ( spinMax - spinMin > 1 )
55 {
56 if ( up )
57 {
58 if ( oldPos == spinMin && pos == spinMax )
59 up = false;
60 }
61 else // down
62 {
63 if ( oldPos == spinMax && pos == spinMin )
64 up = true;
65 }
66 }
67 }
68
69 wxSpinEvent event(up ? wxEVT_SCROLL_LINEUP : wxEVT_SCROLL_LINEDOWN, win->GetId());
70 event.SetPosition(pos);
71 event.SetEventObject(win);
72
73 if ((win->HandleWindowEvent( event )) &&
74 !event.IsAllowed() )
75 {
76 /* program has vetoed */
77 // this will cause another "value_changed" signal,
78 // but because pos == oldPos nothing will happen
79 gtk_spin_button_set_value(spinbutton, oldPos);
80 return;
81 }
82
83 win->m_pos = pos;
84
85 /* always send a thumbtrack event */
86 wxSpinEvent event2(wxEVT_SCROLL_THUMBTRACK, win->GetId());
87 event2.SetPosition(pos);
88 event2.SetEventObject(win);
89 win->HandleWindowEvent(event2);
90 }
91 }
92
93 //-----------------------------------------------------------------------------
94 // wxSpinButton
95 //-----------------------------------------------------------------------------
96
wxSpinButton()97 wxSpinButton::wxSpinButton()
98 {
99 m_pos = 0;
100 }
101
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)102 bool wxSpinButton::Create(wxWindow *parent,
103 wxWindowID id,
104 const wxPoint& pos,
105 const wxSize& size,
106 long style,
107 const wxString& name)
108 {
109 if (!PreCreation(parent, pos, size) ||
110 !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name))
111 {
112 wxFAIL_MSG( wxT("wxSpinButton creation failed") );
113 return false;
114 }
115
116 m_pos = 0;
117
118 m_widget = gtk_spin_button_new_with_range(0, 100, 1);
119 g_object_ref(m_widget);
120
121 gtk_entry_set_width_chars(GTK_ENTRY(m_widget), 0);
122 #if GTK_CHECK_VERSION(3,12,0)
123 if (gtk_check_version(3,12,0) == NULL)
124 gtk_entry_set_max_width_chars(GTK_ENTRY(m_widget), 0);
125 #endif
126 #ifdef __WXGTK3__
127 if (gtk_check_version(3,20,0) == NULL)
128 {
129 GTKApplyCssStyle(
130 "entry { min-width:0; padding-left:0; padding-right:0 }"
131 "button.down { border-style:none }");
132 }
133 #endif
134 gtk_spin_button_set_wrap( GTK_SPIN_BUTTON(m_widget),
135 (int)(m_windowStyle & wxSP_WRAP) );
136
137 g_signal_connect_after(
138 m_widget, "value_changed", G_CALLBACK(gtk_value_changed), this);
139
140 m_parent->DoAddChild( this );
141
142 PostCreation(size);
143
144 return true;
145 }
146
GetMin() const147 int wxSpinButton::GetMin() const
148 {
149 wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
150
151 double min;
152 gtk_spin_button_get_range((GtkSpinButton*)m_widget, &min, NULL);
153 return int(min);
154 }
155
GetMax() const156 int wxSpinButton::GetMax() const
157 {
158 wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
159
160 double max;
161 gtk_spin_button_get_range((GtkSpinButton*)m_widget, NULL, &max);
162 return int(max);
163 }
164
GetValue() const165 int wxSpinButton::GetValue() const
166 {
167 wxCHECK_MSG( (m_widget != NULL), 0, wxT("invalid spin button") );
168
169 return m_pos;
170 }
171
SetValue(int value)172 void wxSpinButton::SetValue( int value )
173 {
174 wxCHECK_RET( (m_widget != NULL), wxT("invalid spin button") );
175
176 GtkDisableEvents();
177 gtk_spin_button_set_value((GtkSpinButton*)m_widget, value);
178 m_pos = int(gtk_spin_button_get_value((GtkSpinButton*)m_widget));
179 GtkEnableEvents();
180 }
181
SetRange(int minVal,int maxVal)182 void wxSpinButton::SetRange(int minVal, int maxVal)
183 {
184 wxCHECK_RET( (m_widget != NULL), wxT("invalid spin button") );
185
186 GtkDisableEvents();
187 gtk_spin_button_set_range((GtkSpinButton*)m_widget, minVal, maxVal);
188 m_pos = int(gtk_spin_button_get_value((GtkSpinButton*)m_widget));
189
190 // Use smaller page increment in the case of a narrow range for convenience
191 // and to limit possible up/down ambiguity in gtk_value_changed() when
192 // wrapping is on (The maximal page increment of 10 is consistent with the
193 // default page increment set by gtk_spin_button_new_with_range(0, 100, 1)
194 // in wxSpinButton::Create().)
195 const int range = maxVal - minVal;
196 int pageInc;
197 if ( range < 10 )
198 pageInc = 1;
199 else if ( range < 20 )
200 pageInc = 2;
201 else if ( range < 50 )
202 pageInc = 5;
203 else
204 pageInc = 10;
205
206 GtkAdjustment* adj = gtk_spin_button_get_adjustment((GtkSpinButton*)m_widget);
207 gtk_adjustment_set_page_increment(adj, pageInc);
208
209 GtkEnableEvents();
210 }
211
DoEnable(bool enable)212 void wxSpinButton::DoEnable(bool enable)
213 {
214 if ( !m_widget )
215 return;
216
217 base_type::DoEnable(enable);
218
219 // Work around lack of visual update when enabling
220 if (enable)
221 GTKFixSensitivity(false /* fix even if not under mouse */);
222 }
223
GtkDisableEvents() const224 void wxSpinButton::GtkDisableEvents() const
225 {
226 g_signal_handlers_block_by_func(m_widget,
227 (void*)gtk_value_changed, const_cast<wxSpinButton*>(this));
228 }
229
GtkEnableEvents() const230 void wxSpinButton::GtkEnableEvents() const
231 {
232 g_signal_handlers_unblock_by_func(m_widget,
233 (void*)gtk_value_changed, const_cast<wxSpinButton*>(this));
234 }
235
GTKGetWindow(wxArrayGdkWindows & WXUNUSED_IN_GTK2 (windows)) const236 GdkWindow *wxSpinButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED_IN_GTK2(windows)) const
237 {
238 #ifdef __WXGTK3__
239 GTKFindWindow(m_widget, windows);
240 return NULL;
241 #else
242 return GTK_SPIN_BUTTON(m_widget)->panel;
243 #endif
244 }
245
DoGetBestSize() const246 wxSize wxSpinButton::DoGetBestSize() const
247 {
248 wxSize best = base_type::DoGetBestSize();
249 #ifdef __WXGTK3__
250 GtkStyleContext* sc = gtk_widget_get_style_context(m_widget);
251 GtkBorder pad = { 0, 0, 0, 0 };
252 gtk_style_context_get_padding(sc, gtk_style_context_get_state(sc), &pad);
253 best.x -= pad.left + pad.right;
254 #else
255 gtk_widget_ensure_style(m_widget);
256 int w = PANGO_PIXELS(pango_font_description_get_size(m_widget->style->font_desc));
257 w &= ~1;
258 if (w < 6)
259 w = 6;
260 best.x = w + 2 * m_widget->style->xthickness;
261 #endif
262 return best;
263 }
264
265 // static
266 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))267 wxSpinButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
268 {
269 return GetDefaultAttributesFromGTKWidget(gtk_spin_button_new_with_range(0, 100, 1));
270 }
271
272 #endif // wxUSE_SPINBTN
273