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 <gtk/gtk.h>
22 #include "wx/gtk/private/gtk2-compat.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 
Enable(bool enable)73 bool wxAnyButton::Enable( bool enable )
74 {
75     if (!base_type::Enable(enable))
76         return false;
77 
78     gtk_widget_set_sensitive(gtk_bin_get_child(GTK_BIN(m_widget)), enable);
79 
80     if (enable)
81         GTKFixSensitivity();
82 
83     GTKUpdateBitmap();
84 
85     return true;
86 }
87 
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const88 GdkWindow *wxAnyButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
89 {
90     return gtk_button_get_event_window(GTK_BUTTON(m_widget));
91 }
92 
93 // static
94 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))95 wxAnyButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
96 {
97     return GetDefaultAttributesFromGTKWidget(gtk_button_new());
98 }
99 
100 // ----------------------------------------------------------------------------
101 // bitmaps support
102 // ----------------------------------------------------------------------------
103 
GTKMouseEnters()104 void wxAnyButton::GTKMouseEnters()
105 {
106     m_isCurrent = true;
107 
108     GTKUpdateBitmap();
109 }
110 
GTKMouseLeaves()111 void wxAnyButton::GTKMouseLeaves()
112 {
113     m_isCurrent = false;
114 
115     GTKUpdateBitmap();
116 }
117 
GTKPressed()118 void wxAnyButton::GTKPressed()
119 {
120     m_isPressed = true;
121 
122     GTKUpdateBitmap();
123 }
124 
GTKReleased()125 void wxAnyButton::GTKReleased()
126 {
127     m_isPressed = false;
128 
129     GTKUpdateBitmap();
130 }
131 
GTKOnFocus(wxFocusEvent & event)132 void wxAnyButton::GTKOnFocus(wxFocusEvent& event)
133 {
134     event.Skip();
135 
136     GTKUpdateBitmap();
137 }
138 
GTKGetCurrentState() const139 wxAnyButton::State wxAnyButton::GTKGetCurrentState() const
140 {
141     if ( !IsThisEnabled() )
142         return m_bitmaps[State_Disabled].IsOk() ? State_Disabled : State_Normal;
143 
144     if ( m_isPressed && m_bitmaps[State_Pressed].IsOk() )
145         return State_Pressed;
146 
147     if ( m_isCurrent && m_bitmaps[State_Current].IsOk() )
148         return State_Current;
149 
150     if ( HasFocus() && m_bitmaps[State_Focused].IsOk() )
151         return State_Focused;
152 
153     return State_Normal;
154 }
155 
GTKUpdateBitmap()156 void wxAnyButton::GTKUpdateBitmap()
157 {
158     // if we don't show bitmaps at all, there is nothing to update
159     if ( m_bitmaps[State_Normal].IsOk() )
160     {
161         // if we do show them, this will return a state for which we do have a
162         // valid bitmap
163         State state = GTKGetCurrentState();
164 
165         GTKDoShowBitmap(m_bitmaps[state]);
166     }
167 }
168 
GTKDoShowBitmap(const wxBitmap & bitmap)169 void wxAnyButton::GTKDoShowBitmap(const wxBitmap& bitmap)
170 {
171     wxASSERT_MSG( bitmap.IsOk(), "invalid bitmap" );
172 
173     GtkWidget *image;
174     if ( DontShowLabel() )
175     {
176         image = gtk_bin_get_child(GTK_BIN(m_widget));
177     }
178     else // have both label and bitmap
179     {
180         image = gtk_button_get_image(GTK_BUTTON(m_widget));
181     }
182 
183     wxCHECK_RET( image && GTK_IS_IMAGE(image), "must have image widget" );
184 
185     gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
186 }
187 
DoGetBitmap(State which) const188 wxBitmap wxAnyButton::DoGetBitmap(State which) const
189 {
190     return m_bitmaps[which];
191 }
192 
DoSetBitmap(const wxBitmap & bitmap,State which)193 void wxAnyButton::DoSetBitmap(const wxBitmap& bitmap, State which)
194 {
195     switch ( which )
196     {
197         case State_Normal:
198             if ( DontShowLabel() )
199             {
200                 // we only have the bitmap in this button, never remove it but
201                 // do invalidate the best size when the bitmap (and presumably
202                 // its size) changes
203                 InvalidateBestSize();
204             }
205             // normal image is special: setting it enables images for the button and
206             // resetting it to nothing disables all of them
207             else
208             {
209                 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
210                 if ( image && !bitmap.IsOk() )
211                 {
212                     gtk_container_remove(GTK_CONTAINER(m_widget), image);
213                 }
214                 else if ( !image && bitmap.IsOk() )
215                 {
216                     image = gtk_image_new();
217                     gtk_button_set_image(GTK_BUTTON(m_widget), image);
218                 }
219                 else // image presence or absence didn't change
220                 {
221                     // don't invalidate best size below
222                     break;
223                 }
224 
225                 InvalidateBestSize();
226             }
227             break;
228 
229         case State_Pressed:
230             if ( bitmap.IsOk() )
231             {
232                 if ( !m_bitmaps[which].IsOk() )
233                 {
234                     // we need to install the callbacks to be notified about
235                     // the button pressed state change
236                     g_signal_connect
237                     (
238                         m_widget,
239                         "pressed",
240                         G_CALLBACK(wxgtk_button_press_callback),
241                         this
242                     );
243 
244                     g_signal_connect
245                     (
246                         m_widget,
247                         "released",
248                         G_CALLBACK(wxgtk_button_released_callback),
249                         this
250                     );
251                 }
252             }
253             else // no valid bitmap
254             {
255                 if ( m_bitmaps[which].IsOk() )
256                 {
257                     // we don't need to be notified about the button pressed
258                     // state changes any more
259                     g_signal_handlers_disconnect_by_func
260                     (
261                         m_widget,
262                         (gpointer)wxgtk_button_press_callback,
263                         this
264                     );
265 
266                     g_signal_handlers_disconnect_by_func
267                     (
268                         m_widget,
269                         (gpointer)wxgtk_button_released_callback,
270                         this
271                     );
272 
273                     // also make sure we don't remain stuck in pressed state
274                     if ( m_isPressed )
275                     {
276                         m_isPressed = false;
277                         GTKUpdateBitmap();
278                     }
279                 }
280             }
281             break;
282 
283         case State_Current:
284             // the logic here is the same as above for State_Pressed: we need
285             // to connect the handlers if we must be notified about the changes
286             // in the button current state and we disconnect them when/if we
287             // don't need them any more
288             if ( bitmap.IsOk() )
289             {
290                 if ( !m_bitmaps[which].IsOk() )
291                 {
292                     g_signal_connect
293                     (
294                         m_widget,
295                         "enter",
296                         G_CALLBACK(wxgtk_button_enter_callback),
297                         this
298                     );
299 
300                     g_signal_connect
301                     (
302                         m_widget,
303                         "leave",
304                         G_CALLBACK(wxgtk_button_leave_callback),
305                         this
306                     );
307                 }
308             }
309             else // no valid bitmap
310             {
311                 if ( m_bitmaps[which].IsOk() )
312                 {
313                     g_signal_handlers_disconnect_by_func
314                     (
315                         m_widget,
316                         (gpointer)wxgtk_button_enter_callback,
317                         this
318                     );
319 
320                     g_signal_handlers_disconnect_by_func
321                     (
322                         m_widget,
323                         (gpointer)wxgtk_button_leave_callback,
324                         this
325                     );
326 
327                     if ( m_isCurrent )
328                     {
329                         m_isCurrent = false;
330                         GTKUpdateBitmap();
331                     }
332                 }
333             }
334             break;
335 
336         case State_Focused:
337             if ( bitmap.IsOk() )
338             {
339                 Connect(wxEVT_SET_FOCUS,
340                         wxFocusEventHandler(wxAnyButton::GTKOnFocus));
341                 Connect(wxEVT_KILL_FOCUS,
342                         wxFocusEventHandler(wxAnyButton::GTKOnFocus));
343             }
344             else // no valid focused bitmap
345             {
346                 Disconnect(wxEVT_SET_FOCUS,
347                            wxFocusEventHandler(wxAnyButton::GTKOnFocus));
348                 Disconnect(wxEVT_KILL_FOCUS,
349                            wxFocusEventHandler(wxAnyButton::GTKOnFocus));
350             }
351             break;
352 
353         default:
354             // no callbacks to connect/disconnect
355             ;
356     }
357 
358     m_bitmaps[which] = bitmap;
359 
360     // update the bitmap immediately if necessary, otherwise it will be done
361     // when the bitmap for the corresponding state is needed the next time by
362     // GTKUpdateBitmap()
363     if ( bitmap.IsOk() && which == GTKGetCurrentState() )
364     {
365         GTKDoShowBitmap(bitmap);
366     }
367 }
368 
DoSetBitmapPosition(wxDirection dir)369 void wxAnyButton::DoSetBitmapPosition(wxDirection dir)
370 {
371 #ifdef __WXGTK210__
372 #ifndef __WXGTK3__
373     if ( !gtk_check_version(2,10,0) )
374 #endif
375     {
376         GtkPositionType gtkpos;
377         switch ( dir )
378         {
379             default:
380                 wxFAIL_MSG( "invalid position" );
381                 // fall through
382 
383             case wxLEFT:
384                 gtkpos = GTK_POS_LEFT;
385                 break;
386 
387             case wxRIGHT:
388                 gtkpos = GTK_POS_RIGHT;
389                 break;
390 
391             case wxTOP:
392                 gtkpos = GTK_POS_TOP;
393                 break;
394 
395             case wxBOTTOM:
396                 gtkpos = GTK_POS_BOTTOM;
397                 break;
398         }
399 
400         gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
401         InvalidateBestSize();
402     }
403 #endif // GTK+ 2.10+
404 }
405 
406 #endif // wxHAS_ANY_BUTTON
407