1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/anybutton.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Created: 1998-05-20 (extracted from button.cpp)
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #ifdef wxHAS_ANY_BUTTON
14
15 #ifndef WX_PRECOMP
16 #include "wx/anybutton.h"
17 #endif
18
19 #include "wx/stockitem.h"
20
21 #include "wx/gtk/private/wrapgtk.h"
22 #include "wx/gtk/private/image.h"
23
24 // ----------------------------------------------------------------------------
25 // GTK callbacks
26 // ----------------------------------------------------------------------------
27
28 extern "C"
29 {
30
31 static void
wxgtk_button_enter_callback(GtkWidget * WXUNUSED (widget),wxAnyButton * button)32 wxgtk_button_enter_callback(GtkWidget *WXUNUSED(widget), wxAnyButton *button)
33 {
34 if ( button->GTKShouldIgnoreEvent() )
35 return;
36
37 button->GTKMouseEnters();
38 }
39
40 static void
wxgtk_button_leave_callback(GtkWidget * WXUNUSED (widget),wxAnyButton * button)41 wxgtk_button_leave_callback(GtkWidget *WXUNUSED(widget), wxAnyButton *button)
42 {
43 if ( button->GTKShouldIgnoreEvent() )
44 return;
45
46 button->GTKMouseLeaves();
47 }
48
49 static void
wxgtk_button_press_callback(GtkWidget * WXUNUSED (widget),wxAnyButton * button)50 wxgtk_button_press_callback(GtkWidget *WXUNUSED(widget), wxAnyButton *button)
51 {
52 if ( button->GTKShouldIgnoreEvent() )
53 return;
54
55 button->GTKPressed();
56 }
57
58 static void
wxgtk_button_released_callback(GtkWidget * WXUNUSED (widget),wxAnyButton * button)59 wxgtk_button_released_callback(GtkWidget *WXUNUSED(widget), wxAnyButton *button)
60 {
61 if ( button->GTKShouldIgnoreEvent() )
62 return;
63
64 button->GTKReleased();
65 }
66
67 } // extern "C"
68
69 //-----------------------------------------------------------------------------
70 // wxAnyButton
71 //-----------------------------------------------------------------------------
72
DoEnable(bool enable)73 void wxAnyButton::DoEnable(bool enable)
74 {
75 // See wxWindow::DoEnable()
76 if ( !m_widget )
77 return;
78
79 base_type::DoEnable(enable);
80
81 gtk_widget_set_sensitive(gtk_bin_get_child(GTK_BIN(m_widget)), enable);
82
83 if (enable)
84 GTKFixSensitivity();
85
86 GTKUpdateBitmap();
87 }
88
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const89 GdkWindow *wxAnyButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
90 {
91 return gtk_button_get_event_window(GTK_BUTTON(m_widget));
92 }
93
94 // static
95 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))96 wxAnyButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
97 {
98 return GetDefaultAttributesFromGTKWidget(gtk_button_new());
99 }
100
101 // ----------------------------------------------------------------------------
102 // bitmaps support
103 // ----------------------------------------------------------------------------
104
GTKMouseEnters()105 void wxAnyButton::GTKMouseEnters()
106 {
107 m_isCurrent = true;
108
109 GTKUpdateBitmap();
110 }
111
GTKMouseLeaves()112 void wxAnyButton::GTKMouseLeaves()
113 {
114 m_isCurrent = false;
115
116 GTKUpdateBitmap();
117 }
118
GTKPressed()119 void wxAnyButton::GTKPressed()
120 {
121 m_isPressed = true;
122
123 GTKUpdateBitmap();
124 }
125
GTKReleased()126 void wxAnyButton::GTKReleased()
127 {
128 m_isPressed = false;
129
130 GTKUpdateBitmap();
131 }
132
GTKOnFocus(wxFocusEvent & event)133 void wxAnyButton::GTKOnFocus(wxFocusEvent& event)
134 {
135 event.Skip();
136
137 GTKUpdateBitmap();
138 }
139
GTKGetCurrentBitmapState() const140 wxAnyButton::State wxAnyButton::GTKGetCurrentBitmapState() const
141 {
142 if ( !IsThisEnabled() )
143 {
144 if ( m_bitmaps[State_Disabled].IsOk() )
145 return State_Disabled;
146 }
147 else
148 {
149 if ( m_isPressed && m_bitmaps[State_Pressed].IsOk() )
150 return State_Pressed;
151
152 if ( m_isCurrent && m_bitmaps[State_Current].IsOk() )
153 return State_Current;
154
155 if ( HasFocus() && m_bitmaps[State_Focused].IsOk() )
156 return State_Focused;
157 }
158
159 // Fall back on the normal state: which still might be different from
160 // State_Normal for the toggle buttons, so the check for bitmap validity is
161 // still needed.
162 const State normalState = GetNormalState();
163 if ( m_bitmaps[normalState].IsOk() )
164 return normalState;
165
166 // And if nothing else can (or should) be used, finally fall back to the
167 // normal state which is the only one guaranteed to have a bitmap (if we're
168 // using bitmaps at all and we're only called in this case).
169 return State_Normal;
170 }
171
GTKUpdateBitmap()172 void wxAnyButton::GTKUpdateBitmap()
173 {
174 // if we don't show bitmaps at all, there is nothing to update
175 if ( m_bitmaps[State_Normal].IsOk() )
176 {
177 // if we do show them, this will return a state for which we do have a
178 // valid bitmap
179 State state = GTKGetCurrentBitmapState();
180
181 GTKDoShowBitmap(m_bitmaps[state]);
182 }
183 }
184
GTKDoShowBitmap(const wxBitmap & bitmap)185 void wxAnyButton::GTKDoShowBitmap(const wxBitmap& bitmap)
186 {
187 wxCHECK_RET(bitmap.IsOk(), "invalid bitmap");
188
189 GtkWidget* image = gtk_button_get_image(GTK_BUTTON(m_widget));
190 if (image == NULL)
191 image = gtk_bin_get_child(GTK_BIN(m_widget));
192
193 wxCHECK_RET(GTK_IS_IMAGE(image), "must have image widget");
194
195 WX_GTK_IMAGE(image)->Set(bitmap);
196 }
197
DoGetBitmap(State which) const198 wxBitmap wxAnyButton::DoGetBitmap(State which) const
199 {
200 return m_bitmaps[which];
201 }
202
DoSetBitmap(const wxBitmap & bitmap,State which)203 void wxAnyButton::DoSetBitmap(const wxBitmap& bitmap, State which)
204 {
205 switch ( which )
206 {
207 case State_Normal:
208 if ( DontShowLabel() )
209 {
210 // we only have the bitmap in this button, never remove it but
211 // do invalidate the best size when the bitmap (and presumably
212 // its size) changes
213 InvalidateBestSize();
214 }
215 // normal image is special: setting it enables images for the button and
216 // resetting it to nothing disables all of them
217 else
218 {
219 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
220 if ( image && !bitmap.IsOk() )
221 {
222 gtk_container_remove(GTK_CONTAINER(m_widget), image);
223 }
224 else if ( !image && bitmap.IsOk() )
225 {
226 image = wxGtkImage::New(this);
227 gtk_button_set_image(GTK_BUTTON(m_widget), image);
228
229 // Setting the image recreates the label, so we need to
230 // reapply the styles to it to preserve the existing text
231 // font and colour if they're different from defaults.
232 GTKApplyWidgetStyle();
233 }
234 else // image presence or absence didn't change
235 {
236 // don't invalidate best size below
237 break;
238 }
239
240 InvalidateBestSize();
241 }
242 break;
243
244 case State_Pressed:
245 if ( bitmap.IsOk() )
246 {
247 if ( !m_bitmaps[which].IsOk() )
248 {
249 // we need to install the callbacks to be notified about
250 // the button pressed state change
251 g_signal_connect
252 (
253 m_widget,
254 "pressed",
255 G_CALLBACK(wxgtk_button_press_callback),
256 this
257 );
258
259 g_signal_connect
260 (
261 m_widget,
262 "released",
263 G_CALLBACK(wxgtk_button_released_callback),
264 this
265 );
266 }
267 }
268 else // no valid bitmap
269 {
270 if ( m_bitmaps[which].IsOk() )
271 {
272 // we don't need to be notified about the button pressed
273 // state changes any more
274 g_signal_handlers_disconnect_by_func
275 (
276 m_widget,
277 (gpointer)wxgtk_button_press_callback,
278 this
279 );
280
281 g_signal_handlers_disconnect_by_func
282 (
283 m_widget,
284 (gpointer)wxgtk_button_released_callback,
285 this
286 );
287
288 // also make sure we don't remain stuck in pressed state
289 if ( m_isPressed )
290 {
291 m_isPressed = false;
292 GTKUpdateBitmap();
293 }
294 }
295 }
296 break;
297
298 case State_Current:
299 // the logic here is the same as above for State_Pressed: we need
300 // to connect the handlers if we must be notified about the changes
301 // in the button current state and we disconnect them when/if we
302 // don't need them any more
303 if ( bitmap.IsOk() )
304 {
305 if ( !m_bitmaps[which].IsOk() )
306 {
307 g_signal_connect
308 (
309 m_widget,
310 "enter",
311 G_CALLBACK(wxgtk_button_enter_callback),
312 this
313 );
314
315 g_signal_connect
316 (
317 m_widget,
318 "leave",
319 G_CALLBACK(wxgtk_button_leave_callback),
320 this
321 );
322 }
323 }
324 else // no valid bitmap
325 {
326 if ( m_bitmaps[which].IsOk() )
327 {
328 g_signal_handlers_disconnect_by_func
329 (
330 m_widget,
331 (gpointer)wxgtk_button_enter_callback,
332 this
333 );
334
335 g_signal_handlers_disconnect_by_func
336 (
337 m_widget,
338 (gpointer)wxgtk_button_leave_callback,
339 this
340 );
341
342 if ( m_isCurrent )
343 {
344 m_isCurrent = false;
345 GTKUpdateBitmap();
346 }
347 }
348 }
349 break;
350
351 case State_Focused:
352 if ( bitmap.IsOk() )
353 {
354 Bind(wxEVT_SET_FOCUS, &wxAnyButton::GTKOnFocus, this);
355 Bind(wxEVT_KILL_FOCUS, &wxAnyButton::GTKOnFocus, this);
356 }
357 else // no valid focused bitmap
358 {
359 Unbind(wxEVT_SET_FOCUS, &wxAnyButton::GTKOnFocus, this);
360 Unbind(wxEVT_KILL_FOCUS, &wxAnyButton::GTKOnFocus, this);
361 }
362 break;
363
364 default:
365 // no callbacks to connect/disconnect
366 ;
367 }
368
369 m_bitmaps[which] = bitmap;
370
371 #if GTK_CHECK_VERSION(3,6,0) && !defined(__WXGTK4__)
372 // Allow explicitly set bitmaps to be shown regardless of theme setting
373 if (gtk_check_version(3,6,0) == NULL && bitmap.IsOk())
374 gtk_button_set_always_show_image(GTK_BUTTON(m_widget), true);
375 #endif
376
377 // update the bitmap immediately if necessary, otherwise it will be done
378 // when the bitmap for the corresponding state is needed the next time by
379 // GTKUpdateBitmap()
380 if ( bitmap.IsOk() && which == GTKGetCurrentBitmapState() )
381 {
382 GTKDoShowBitmap(bitmap);
383 }
384 }
385
DoSetBitmapPosition(wxDirection dir)386 void wxAnyButton::DoSetBitmapPosition(wxDirection dir)
387 {
388 #ifdef __WXGTK210__
389 if ( wx_is_at_least_gtk2(10) )
390 {
391 GtkPositionType gtkpos;
392 switch ( dir )
393 {
394 default:
395 wxFAIL_MSG( "invalid position" );
396 wxFALLTHROUGH;
397
398 case wxLEFT:
399 gtkpos = GTK_POS_LEFT;
400 break;
401
402 case wxRIGHT:
403 gtkpos = GTK_POS_RIGHT;
404 break;
405
406 case wxTOP:
407 gtkpos = GTK_POS_TOP;
408 break;
409
410 case wxBOTTOM:
411 gtkpos = GTK_POS_BOTTOM;
412 break;
413 }
414
415 gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
416
417 // As in DoSetBitmap() above, the above call can invalidate the label
418 // style, so reapply it to preserve its font and colour.
419 GTKApplyWidgetStyle();
420
421 InvalidateBestSize();
422 }
423 #endif // GTK+ 2.10+
424 }
425
426 #endif // wxHAS_ANY_BUTTON
427