]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/button.cpp
0a6a838f90a42fb0f1f8728df4724ce2453ead60
[wxWidgets.git] / src / gtk / button.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/button.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
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 #if wxUSE_BUTTON
14
15 #ifndef WX_PRECOMP
16 #include "wx/button.h"
17 #endif
18
19 #include "wx/stockitem.h"
20
21 #include "wx/gtk/private.h"
22
23 // ----------------------------------------------------------------------------
24 // GTK callbacks
25 // ----------------------------------------------------------------------------
26
27 extern "C"
28 {
29
30 static void
31 wxgtk_button_clicked_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
32 {
33 if ( button->GTKShouldIgnoreEvent() )
34 return;
35
36 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, button->GetId());
37 event.SetEventObject(button);
38 button->HandleWindowEvent(event);
39 }
40
41 static void
42 wxgtk_button_enter_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
43 {
44 if ( button->GTKShouldIgnoreEvent() )
45 return;
46
47 button->GTKMouseEnters();
48 }
49
50 static void
51 wxgtk_button_leave_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
52 {
53 if ( button->GTKShouldIgnoreEvent() )
54 return;
55
56 button->GTKMouseLeaves();
57 }
58
59 static void
60 wxgtk_button_press_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
61 {
62 if ( button->GTKShouldIgnoreEvent() )
63 return;
64
65 button->GTKPressed();
66 }
67
68 static void
69 wxgtk_button_released_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
70 {
71 if ( button->GTKShouldIgnoreEvent() )
72 return;
73
74 button->GTKReleased();
75 }
76
77 //-----------------------------------------------------------------------------
78 // "style_set" from m_widget
79 //-----------------------------------------------------------------------------
80
81 static void
82 wxgtk_button_style_set_callback(GtkWidget* widget, GtkStyle*, wxButton* win)
83 {
84 /* the default button has a border around it */
85 wxWindow* parent = win->GetParent();
86 if (parent && parent->m_wxwindow && GTK_WIDGET_CAN_DEFAULT(widget))
87 {
88 GtkBorder* border = NULL;
89 gtk_widget_style_get(widget, "default_border", &border, NULL);
90 if (border)
91 {
92 win->MoveWindow(
93 win->m_x - border->left,
94 win->m_y - border->top,
95 win->m_width + border->left + border->right,
96 win->m_height + border->top + border->bottom);
97 gtk_border_free(border);
98 }
99 }
100 }
101
102 } // extern "C"
103
104 //-----------------------------------------------------------------------------
105 // wxButton
106 //-----------------------------------------------------------------------------
107
108 IMPLEMENT_DYNAMIC_CLASS(wxButton,wxControl)
109
110 bool wxButton::Create(wxWindow *parent,
111 wxWindowID id,
112 const wxString &label,
113 const wxPoint& pos,
114 const wxSize& size,
115 long style,
116 const wxValidator& validator,
117 const wxString& name)
118 {
119 if (!PreCreation( parent, pos, size ) ||
120 !CreateBase( parent, id, pos, size, style, validator, name ))
121 {
122 wxFAIL_MSG( wxT("wxButton creation failed") );
123 return false;
124 }
125
126 // create either a standard button with text label (which may still contain
127 // an image under GTK+ 2.6+) or a bitmap-only button if we don't have any
128 // label
129 const bool
130 useLabel = !(style & wxBU_NOTEXT) && (!label.empty() || wxIsStockID(id));
131 if ( useLabel )
132 {
133 m_widget = gtk_button_new_with_mnemonic("");
134 }
135 else // no label, suppose we will have a bitmap
136 {
137 m_widget = gtk_button_new();
138
139 GtkWidget *image = gtk_image_new();
140 gtk_widget_show(image);
141 gtk_container_add(GTK_CONTAINER(m_widget), image);
142 }
143
144 g_object_ref(m_widget);
145
146 float x_alignment = 0.5;
147 if (HasFlag(wxBU_LEFT))
148 x_alignment = 0.0;
149 else if (HasFlag(wxBU_RIGHT))
150 x_alignment = 1.0;
151
152 float y_alignment = 0.5;
153 if (HasFlag(wxBU_TOP))
154 y_alignment = 0.0;
155 else if (HasFlag(wxBU_BOTTOM))
156 y_alignment = 1.0;
157
158 gtk_button_set_alignment(GTK_BUTTON(m_widget), x_alignment, y_alignment);
159
160 if ( useLabel )
161 SetLabel(label);
162
163 if (style & wxNO_BORDER)
164 gtk_button_set_relief( GTK_BUTTON(m_widget), GTK_RELIEF_NONE );
165
166 g_signal_connect_after (m_widget, "clicked",
167 G_CALLBACK (wxgtk_button_clicked_callback),
168 this);
169
170 g_signal_connect_after (m_widget, "style_set",
171 G_CALLBACK (wxgtk_button_style_set_callback),
172 this);
173
174 m_parent->DoAddChild( this );
175
176 PostCreation(size);
177
178 return true;
179 }
180
181
182 wxWindow *wxButton::SetDefault()
183 {
184 wxWindow *oldDefault = wxButtonBase::SetDefault();
185
186 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
187 gtk_widget_grab_default( m_widget );
188
189 // resize for default border
190 wxgtk_button_style_set_callback( m_widget, NULL, this );
191
192 return oldDefault;
193 }
194
195 /* static */
196 wxSize wxButtonBase::GetDefaultSize()
197 {
198 static wxSize size = wxDefaultSize;
199 if (size == wxDefaultSize)
200 {
201 // NB: Default size of buttons should be same as size of stock
202 // buttons as used in most GTK+ apps. Unfortunately it's a little
203 // tricky to obtain this size: stock button's size may be smaller
204 // than size of button in GtkButtonBox and vice versa,
205 // GtkButtonBox's minimal button size may be smaller than stock
206 // button's size. We have to retrieve both values and combine them.
207
208 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
209 GtkWidget *box = gtk_hbutton_box_new();
210 GtkWidget *btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
211 gtk_container_add(GTK_CONTAINER(box), btn);
212 gtk_container_add(GTK_CONTAINER(wnd), box);
213 GtkRequisition req;
214 gtk_widget_size_request(btn, &req);
215
216 gint minwidth, minheight;
217 gtk_widget_style_get(box,
218 "child-min-width", &minwidth,
219 "child-min-height", &minheight,
220 NULL);
221
222 size.x = wxMax(minwidth, req.width);
223 size.y = wxMax(minheight, req.height);
224
225 gtk_widget_destroy(wnd);
226 }
227 return size;
228 }
229
230 void wxButton::SetLabel( const wxString &lbl )
231 {
232 wxCHECK_RET( m_widget != NULL, wxT("invalid button") );
233
234 wxString label(lbl);
235
236 if (label.empty() && wxIsStockID(m_windowId))
237 label = wxGetStockLabel(m_windowId);
238
239 wxControl::SetLabel(label);
240
241 // don't use label if it was explicitly disabled
242 if ( HasFlag(wxBU_NOTEXT) )
243 return;
244
245 if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label))
246 {
247 const char *stock = wxGetStockGtkID(m_windowId);
248 if (stock)
249 {
250 gtk_button_set_label(GTK_BUTTON(m_widget), stock);
251 gtk_button_set_use_stock(GTK_BUTTON(m_widget), TRUE);
252 return;
253 }
254 }
255
256 const wxString labelGTK = GTKConvertMnemonics(label);
257 gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK));
258 gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE);
259
260 GTKApplyWidgetStyle( false );
261 }
262
263 bool wxButton::Enable( bool enable )
264 {
265 bool isEnabled = IsEnabled();
266
267 if ( !wxControl::Enable( enable ) )
268 return false;
269
270 gtk_widget_set_sensitive(GTK_BIN(m_widget)->child, enable);
271
272 if (!isEnabled && enable)
273 {
274 GTKFixSensitivity();
275 }
276
277 GTKUpdateBitmap();
278
279 return true;
280 }
281
282 GdkWindow *wxButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
283 {
284 return GTK_BUTTON(m_widget)->event_window;
285 }
286
287 void wxButton::DoApplyWidgetStyle(GtkRcStyle *style)
288 {
289 gtk_widget_modify_style(m_widget, style);
290 GtkWidget *child = GTK_BIN(m_widget)->child;
291 gtk_widget_modify_style(child, style);
292
293 // for buttons with images, the path to the label is (at least in 2.12)
294 // GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
295 if ( GTK_IS_ALIGNMENT(child) )
296 {
297 GtkWidget *box = GTK_BIN(child)->child;
298 if ( GTK_IS_BOX(box) )
299 {
300 for (GList* item = GTK_BOX(box)->children; item; item = item->next)
301 {
302 GtkBoxChild* boxChild = static_cast<GtkBoxChild*>(item->data);
303 gtk_widget_modify_style(boxChild->widget, style);
304 }
305 }
306 }
307 }
308
309 wxSize wxButton::DoGetBestSize() const
310 {
311 // the default button in wxGTK is bigger than the other ones because of an
312 // extra border around it, but we don't want to take it into account in
313 // our size calculations (otherwise the result is visually ugly), so
314 // always return the size of non default button from here
315 const bool isDefault = GTK_WIDGET_HAS_DEFAULT(m_widget);
316 if ( isDefault )
317 {
318 // temporarily unset default flag
319 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_DEFAULT );
320 }
321
322 wxSize ret( wxControl::DoGetBestSize() );
323
324 if ( isDefault )
325 {
326 // set it back again
327 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
328 }
329
330 if (!HasFlag(wxBU_EXACTFIT))
331 {
332 wxSize defaultSize = GetDefaultSize();
333 if (ret.x < defaultSize.x)
334 ret.x = defaultSize.x;
335 if (ret.y < defaultSize.y)
336 ret.y = defaultSize.y;
337 }
338
339 CacheBestSize(ret);
340 return ret;
341 }
342
343 // static
344 wxVisualAttributes
345 wxButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
346 {
347 return GetDefaultAttributesFromGTKWidget(gtk_button_new);
348 }
349
350 // ----------------------------------------------------------------------------
351 // bitmaps support
352 // ----------------------------------------------------------------------------
353
354 void wxButton::GTKMouseEnters()
355 {
356 m_isCurrent = true;
357
358 GTKUpdateBitmap();
359 }
360
361 void wxButton::GTKMouseLeaves()
362 {
363 m_isCurrent = false;
364
365 GTKUpdateBitmap();
366 }
367
368 void wxButton::GTKPressed()
369 {
370 m_isPressed = true;
371
372 GTKUpdateBitmap();
373 }
374
375 void wxButton::GTKReleased()
376 {
377 m_isPressed = false;
378
379 GTKUpdateBitmap();
380 }
381
382 void wxButton::GTKOnFocus(wxFocusEvent& event)
383 {
384 event.Skip();
385
386 GTKUpdateBitmap();
387 }
388
389 wxButton::State wxButton::GTKGetCurrentState() const
390 {
391 if ( !IsThisEnabled() )
392 return m_bitmaps[State_Disabled].IsOk() ? State_Disabled : State_Normal;
393
394 if ( m_isPressed && m_bitmaps[State_Pressed].IsOk() )
395 return State_Pressed;
396
397 if ( m_isCurrent && m_bitmaps[State_Current].IsOk() )
398 return State_Current;
399
400 if ( HasFocus() && m_bitmaps[State_Focused].IsOk() )
401 return State_Focused;
402
403 return State_Normal;
404 }
405
406 void wxButton::GTKUpdateBitmap()
407 {
408 State state = GTKGetCurrentState();
409
410 GTKDoShowBitmap(m_bitmaps[state]);
411 }
412
413 void wxButton::GTKDoShowBitmap(const wxBitmap& bitmap)
414 {
415 wxASSERT_MSG( bitmap.IsOk(), "invalid bitmap" );
416
417 GtkWidget *image;
418 if ( DontShowLabel() )
419 {
420 image = GTK_BIN(m_widget)->child;
421 }
422 else // have both label and bitmap
423 {
424 #ifdef __WXGTK26__
425 if ( !gtk_check_version(2,6,0) )
426 {
427 image = gtk_button_get_image(GTK_BUTTON(m_widget));
428 }
429 else
430 #endif // __WXGTK26__
431 {
432 // buttons with both label and bitmap are only supported with GTK+
433 // 2.6 so far
434 //
435 // it shouldn't be difficult to implement them ourselves for the
436 // previous GTK+ versions by stuffing a container with a label and
437 // an image inside GtkButton but there doesn't seem to be much
438 // point in doing this for ancient GTK+ versions
439 return;
440 }
441 }
442
443 wxCHECK_RET( image && GTK_IS_IMAGE(image), "must have image widget" );
444
445 gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
446 }
447
448 wxBitmap wxButton::DoGetBitmap(State which) const
449 {
450 return m_bitmaps[which];
451 }
452
453 void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
454 {
455 switch ( which )
456 {
457 case State_Normal:
458 if ( DontShowLabel() )
459 {
460 // we only have the bitmap in this button, never remove it but
461 // do invalidate the best size when the bitmap (and presumably
462 // its size) changes
463 InvalidateBestSize();
464 }
465 #ifdef __WXGTK26__
466 // normal image is special: setting it enables images for the button and
467 // resetting it to nothing disables all of them
468 else if ( !gtk_check_version(2,6,0) )
469 {
470 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
471 if ( image && !bitmap.IsOk() )
472 {
473 gtk_container_remove(GTK_CONTAINER(m_widget), image);
474 }
475 else if ( !image && bitmap.IsOk() )
476 {
477 image = gtk_image_new();
478 gtk_button_set_image(GTK_BUTTON(m_widget), image);
479 }
480 else // image presence or absence didn't change
481 {
482 // don't invalidate best size below
483 break;
484 }
485
486 InvalidateBestSize();
487 }
488 #endif // GTK+ 2.6+
489 break;
490
491 case State_Pressed:
492 if ( bitmap.IsOk() )
493 {
494 if ( !m_bitmaps[which].IsOk() )
495 {
496 // we need to install the callbacks to be notified about
497 // the button pressed state change
498 g_signal_connect
499 (
500 m_widget,
501 "pressed",
502 G_CALLBACK(wxgtk_button_press_callback),
503 this
504 );
505
506 g_signal_connect
507 (
508 m_widget,
509 "released",
510 G_CALLBACK(wxgtk_button_released_callback),
511 this
512 );
513 }
514 }
515 else // no valid bitmap
516 {
517 if ( m_bitmaps[which].IsOk() )
518 {
519 // we don't need to be notified about the button pressed
520 // state changes any more
521 g_signal_handlers_disconnect_by_func
522 (
523 m_widget,
524 (gpointer)wxgtk_button_press_callback,
525 this
526 );
527
528 g_signal_handlers_disconnect_by_func
529 (
530 m_widget,
531 (gpointer)wxgtk_button_released_callback,
532 this
533 );
534
535 // also make sure we don't remain stuck in pressed state
536 if ( m_isPressed )
537 {
538 m_isPressed = false;
539 GTKUpdateBitmap();
540 }
541 }
542 }
543 break;
544
545 case State_Current:
546 // the logic here is the same as above for State_Pressed: we need
547 // to connect the handlers if we must be notified about the changes
548 // in the button current state and we disconnect them when/if we
549 // don't need them any more
550 if ( bitmap.IsOk() )
551 {
552 if ( !m_bitmaps[which].IsOk() )
553 {
554 g_signal_connect
555 (
556 m_widget,
557 "enter",
558 G_CALLBACK(wxgtk_button_enter_callback),
559 this
560 );
561
562 g_signal_connect
563 (
564 m_widget,
565 "leave",
566 G_CALLBACK(wxgtk_button_leave_callback),
567 this
568 );
569 }
570 }
571 else // no valid bitmap
572 {
573 if ( m_bitmaps[which].IsOk() )
574 {
575 g_signal_handlers_disconnect_by_func
576 (
577 m_widget,
578 (gpointer)wxgtk_button_enter_callback,
579 this
580 );
581
582 g_signal_handlers_disconnect_by_func
583 (
584 m_widget,
585 (gpointer)wxgtk_button_leave_callback,
586 this
587 );
588
589 if ( m_isCurrent )
590 {
591 m_isCurrent = false;
592 GTKUpdateBitmap();
593 }
594 }
595 }
596 break;
597
598 case State_Focused:
599 if ( bitmap.IsOk() )
600 {
601 Connect(wxEVT_SET_FOCUS,
602 wxFocusEventHandler(wxButton::GTKOnFocus));
603 Connect(wxEVT_KILL_FOCUS,
604 wxFocusEventHandler(wxButton::GTKOnFocus));
605 }
606 else // no valid focused bitmap
607 {
608 Disconnect(wxEVT_SET_FOCUS,
609 wxFocusEventHandler(wxButton::GTKOnFocus));
610 Disconnect(wxEVT_KILL_FOCUS,
611 wxFocusEventHandler(wxButton::GTKOnFocus));
612 }
613 break;
614
615 default:
616 // no callbacks to connect/disconnect
617 ;
618 }
619
620 m_bitmaps[which] = bitmap;
621
622 // update the bitmap immediately if necessary, otherwise it will be done
623 // when the bitmap for the corresponding state is needed the next time by
624 // GTKUpdateBitmap()
625 if ( bitmap.IsOk() && which == GTKGetCurrentState() )
626 {
627 GTKDoShowBitmap(bitmap);
628 }
629 }
630
631 void wxButton::DoSetBitmapPosition(wxDirection dir)
632 {
633 #ifdef __WXGTK210__
634 if ( !gtk_check_version(2,10,0) )
635 {
636 GtkPositionType gtkpos;
637 switch ( dir )
638 {
639 default:
640 wxFAIL_MSG( "invalid position" );
641 // fall through
642
643 case wxLEFT:
644 gtkpos = GTK_POS_LEFT;
645 break;
646
647 case wxRIGHT:
648 gtkpos = GTK_POS_RIGHT;
649 break;
650
651 case wxTOP:
652 gtkpos = GTK_POS_TOP;
653 break;
654
655 case wxBOTTOM:
656 gtkpos = GTK_POS_BOTTOM;
657 break;
658 }
659
660 gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
661 }
662 #endif // GTK+ 2.10+
663 }
664
665 #endif // wxUSE_BUTTON