]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/anybutton.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[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 image = gtk_button_get_image(GTK_BUTTON(m_widget));
182 }
183
184 wxCHECK_RET( image && GTK_IS_IMAGE(image), "must have image widget" );
185
186 gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
187 }
188
189 wxBitmap wxAnyButton::DoGetBitmap(State which) const
190 {
191 return m_bitmaps[which];
192 }
193
194 void wxAnyButton::DoSetBitmap(const wxBitmap& bitmap, State which)
195 {
196 switch ( which )
197 {
198 case State_Normal:
199 if ( DontShowLabel() )
200 {
201 // we only have the bitmap in this button, never remove it but
202 // do invalidate the best size when the bitmap (and presumably
203 // its size) changes
204 InvalidateBestSize();
205 }
206 // normal image is special: setting it enables images for the button and
207 // resetting it to nothing disables all of them
208 else
209 {
210 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
211 if ( image && !bitmap.IsOk() )
212 {
213 gtk_container_remove(GTK_CONTAINER(m_widget), image);
214 }
215 else if ( !image && bitmap.IsOk() )
216 {
217 image = gtk_image_new();
218 gtk_button_set_image(GTK_BUTTON(m_widget), image);
219 }
220 else // image presence or absence didn't change
221 {
222 // don't invalidate best size below
223 break;
224 }
225
226 InvalidateBestSize();
227 }
228 break;
229
230 case State_Pressed:
231 if ( bitmap.IsOk() )
232 {
233 if ( !m_bitmaps[which].IsOk() )
234 {
235 // we need to install the callbacks to be notified about
236 // the button pressed state change
237 g_signal_connect
238 (
239 m_widget,
240 "pressed",
241 G_CALLBACK(wxgtk_button_press_callback),
242 this
243 );
244
245 g_signal_connect
246 (
247 m_widget,
248 "released",
249 G_CALLBACK(wxgtk_button_released_callback),
250 this
251 );
252 }
253 }
254 else // no valid bitmap
255 {
256 if ( m_bitmaps[which].IsOk() )
257 {
258 // we don't need to be notified about the button pressed
259 // state changes any more
260 g_signal_handlers_disconnect_by_func
261 (
262 m_widget,
263 (gpointer)wxgtk_button_press_callback,
264 this
265 );
266
267 g_signal_handlers_disconnect_by_func
268 (
269 m_widget,
270 (gpointer)wxgtk_button_released_callback,
271 this
272 );
273
274 // also make sure we don't remain stuck in pressed state
275 if ( m_isPressed )
276 {
277 m_isPressed = false;
278 GTKUpdateBitmap();
279 }
280 }
281 }
282 break;
283
284 case State_Current:
285 // the logic here is the same as above for State_Pressed: we need
286 // to connect the handlers if we must be notified about the changes
287 // in the button current state and we disconnect them when/if we
288 // don't need them any more
289 if ( bitmap.IsOk() )
290 {
291 if ( !m_bitmaps[which].IsOk() )
292 {
293 g_signal_connect
294 (
295 m_widget,
296 "enter",
297 G_CALLBACK(wxgtk_button_enter_callback),
298 this
299 );
300
301 g_signal_connect
302 (
303 m_widget,
304 "leave",
305 G_CALLBACK(wxgtk_button_leave_callback),
306 this
307 );
308 }
309 }
310 else // no valid bitmap
311 {
312 if ( m_bitmaps[which].IsOk() )
313 {
314 g_signal_handlers_disconnect_by_func
315 (
316 m_widget,
317 (gpointer)wxgtk_button_enter_callback,
318 this
319 );
320
321 g_signal_handlers_disconnect_by_func
322 (
323 m_widget,
324 (gpointer)wxgtk_button_leave_callback,
325 this
326 );
327
328 if ( m_isCurrent )
329 {
330 m_isCurrent = false;
331 GTKUpdateBitmap();
332 }
333 }
334 }
335 break;
336
337 case State_Focused:
338 if ( bitmap.IsOk() )
339 {
340 Connect(wxEVT_SET_FOCUS,
341 wxFocusEventHandler(wxAnyButton::GTKOnFocus));
342 Connect(wxEVT_KILL_FOCUS,
343 wxFocusEventHandler(wxAnyButton::GTKOnFocus));
344 }
345 else // no valid focused bitmap
346 {
347 Disconnect(wxEVT_SET_FOCUS,
348 wxFocusEventHandler(wxAnyButton::GTKOnFocus));
349 Disconnect(wxEVT_KILL_FOCUS,
350 wxFocusEventHandler(wxAnyButton::GTKOnFocus));
351 }
352 break;
353
354 default:
355 // no callbacks to connect/disconnect
356 ;
357 }
358
359 m_bitmaps[which] = bitmap;
360
361 // update the bitmap immediately if necessary, otherwise it will be done
362 // when the bitmap for the corresponding state is needed the next time by
363 // GTKUpdateBitmap()
364 if ( bitmap.IsOk() && which == GTKGetCurrentState() )
365 {
366 GTKDoShowBitmap(bitmap);
367 }
368 }
369
370 void wxAnyButton::DoSetBitmapPosition(wxDirection dir)
371 {
372 #ifdef __WXGTK210__
373 if ( !gtk_check_version(2,10,0) )
374 {
375 GtkPositionType gtkpos;
376 switch ( dir )
377 {
378 default:
379 wxFAIL_MSG( "invalid position" );
380 // fall through
381
382 case wxLEFT:
383 gtkpos = GTK_POS_LEFT;
384 break;
385
386 case wxRIGHT:
387 gtkpos = GTK_POS_RIGHT;
388 break;
389
390 case wxTOP:
391 gtkpos = GTK_POS_TOP;
392 break;
393
394 case wxBOTTOM:
395 gtkpos = GTK_POS_BOTTOM;
396 break;
397 }
398
399 gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
400 InvalidateBestSize();
401 }
402 #endif // GTK+ 2.10+
403 }
404
405 #endif // wxHAS_ANY_BUTTON