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