1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/button.cpp
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
16 #include "wx/button.h"
19 #include "wx/stockitem.h"
21 #include "wx/gtk/private.h"
23 // ----------------------------------------------------------------------------
25 // ----------------------------------------------------------------------------
31 wxgtk_button_clicked_callback(GtkWidget
*WXUNUSED(widget
), wxButton
*button
)
33 if ( button
->GTKShouldIgnoreEvent() )
36 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, button
->GetId());
37 event
.SetEventObject(button
);
38 button
->HandleWindowEvent(event
);
42 wxgtk_button_enter_callback(GtkWidget
*WXUNUSED(widget
), wxButton
*button
)
44 if ( button
->GTKShouldIgnoreEvent() )
47 button
->GTKMouseEnters();
51 wxgtk_button_leave_callback(GtkWidget
*WXUNUSED(widget
), wxButton
*button
)
53 if ( button
->GTKShouldIgnoreEvent() )
56 button
->GTKMouseLeaves();
60 wxgtk_button_press_callback(GtkWidget
*WXUNUSED(widget
), wxButton
*button
)
62 if ( button
->GTKShouldIgnoreEvent() )
69 wxgtk_button_released_callback(GtkWidget
*WXUNUSED(widget
), wxButton
*button
)
71 if ( button
->GTKShouldIgnoreEvent() )
74 button
->GTKReleased();
77 //-----------------------------------------------------------------------------
78 // "style_set" from m_widget
79 //-----------------------------------------------------------------------------
82 wxgtk_button_style_set_callback(GtkWidget
* widget
, GtkStyle
*, wxButton
* win
)
84 /* the default button has a border around it */
85 wxWindow
* parent
= win
->GetParent();
86 if (parent
&& parent
->m_wxwindow
&& gtk_widget_get_can_default(widget
))
88 GtkBorder
* border
= NULL
;
89 gtk_widget_style_get(widget
, "default_border", &border
, NULL
);
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
);
104 //-----------------------------------------------------------------------------
106 //-----------------------------------------------------------------------------
108 bool wxButton::Create(wxWindow
*parent
,
110 const wxString
&label
,
114 const wxValidator
& validator
,
115 const wxString
& name
)
117 if (!PreCreation( parent
, pos
, size
) ||
118 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
120 wxFAIL_MSG( wxT("wxButton creation failed") );
124 // create either a standard button with text label (which may still contain
125 // an image under GTK+ 2.6+) or a bitmap-only button if we don't have any
128 useLabel
= !(style
& wxBU_NOTEXT
) && (!label
.empty() || wxIsStockID(id
));
131 m_widget
= gtk_button_new_with_mnemonic("");
133 else // no label, suppose we will have a bitmap
135 m_widget
= gtk_button_new();
137 GtkWidget
*image
= gtk_image_new();
138 gtk_widget_show(image
);
139 gtk_container_add(GTK_CONTAINER(m_widget
), image
);
142 g_object_ref(m_widget
);
144 float x_alignment
= 0.5;
145 if (HasFlag(wxBU_LEFT
))
147 else if (HasFlag(wxBU_RIGHT
))
150 float y_alignment
= 0.5;
151 if (HasFlag(wxBU_TOP
))
153 else if (HasFlag(wxBU_BOTTOM
))
156 gtk_button_set_alignment(GTK_BUTTON(m_widget
), x_alignment
, y_alignment
);
161 if (style
& wxNO_BORDER
)
162 gtk_button_set_relief( GTK_BUTTON(m_widget
), GTK_RELIEF_NONE
);
164 g_signal_connect_after (m_widget
, "clicked",
165 G_CALLBACK (wxgtk_button_clicked_callback
),
168 g_signal_connect_after (m_widget
, "style_set",
169 G_CALLBACK (wxgtk_button_style_set_callback
),
172 m_parent
->DoAddChild( this );
180 wxWindow
*wxButton::SetDefault()
182 wxWindow
*oldDefault
= wxButtonBase::SetDefault();
184 gtk_widget_set_can_default(m_widget
, TRUE
);
185 gtk_widget_grab_default( m_widget
);
187 // resize for default border
188 wxgtk_button_style_set_callback( m_widget
, NULL
, this );
194 wxSize
wxButtonBase::GetDefaultSize()
196 static wxSize size
= wxDefaultSize
;
197 if (size
== wxDefaultSize
)
199 // NB: Default size of buttons should be same as size of stock
200 // buttons as used in most GTK+ apps. Unfortunately it's a little
201 // tricky to obtain this size: stock button's size may be smaller
202 // than size of button in GtkButtonBox and vice versa,
203 // GtkButtonBox's minimal button size may be smaller than stock
204 // button's size. We have to retrieve both values and combine them.
206 GtkWidget
*wnd
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
207 GtkWidget
*box
= gtk_hbutton_box_new();
208 GtkWidget
*btn
= gtk_button_new_from_stock(GTK_STOCK_CANCEL
);
209 gtk_container_add(GTK_CONTAINER(box
), btn
);
210 gtk_container_add(GTK_CONTAINER(wnd
), box
);
212 gtk_widget_size_request(btn
, &req
);
214 gint minwidth
, minheight
;
215 gtk_widget_style_get(box
,
216 "child-min-width", &minwidth
,
217 "child-min-height", &minheight
,
220 size
.x
= wxMax(minwidth
, req
.width
);
221 size
.y
= wxMax(minheight
, req
.height
);
223 gtk_widget_destroy(wnd
);
228 void wxButton::SetLabel( const wxString
&lbl
)
230 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid button") );
234 if (label
.empty() && wxIsStockID(m_windowId
))
235 label
= wxGetStockLabel(m_windowId
);
237 wxControl::SetLabel(label
);
239 // don't use label if it was explicitly disabled
240 if ( HasFlag(wxBU_NOTEXT
) )
243 if (wxIsStockID(m_windowId
) && wxIsStockLabel(m_windowId
, label
))
245 const char *stock
= wxGetStockGtkID(m_windowId
);
248 gtk_button_set_label(GTK_BUTTON(m_widget
), stock
);
249 gtk_button_set_use_stock(GTK_BUTTON(m_widget
), TRUE
);
254 // this call is necessary if the button had been initially created without
255 // a (text) label -- then we didn't use gtk_button_new_with_mnemonic() and
256 // so "use-underline" GtkButton property remained unset
257 gtk_button_set_use_underline(GTK_BUTTON(m_widget
), TRUE
);
258 const wxString labelGTK
= GTKConvertMnemonics(label
);
259 gtk_button_set_label(GTK_BUTTON(m_widget
), wxGTK_CONV(labelGTK
));
260 gtk_button_set_use_stock(GTK_BUTTON(m_widget
), FALSE
);
262 GTKApplyWidgetStyle( false );
266 bool wxButton::DoSetLabelMarkup(const wxString
& markup
)
268 wxCHECK_MSG( m_widget
!= NULL
, false, "invalid button" );
270 const wxString stripped
= RemoveMarkup(markup
);
271 if ( stripped
.empty() && !markup
.empty() )
274 wxControl::SetLabel(stripped
);
276 GtkLabel
* const label
= GTKGetLabel();
277 wxCHECK_MSG( label
, false, "no label in this button?" );
279 GTKSetLabelWithMarkupForLabel(label
, markup
);
283 #endif // wxUSE_MARKUP
285 bool wxButton::Enable( bool enable
)
287 if (!base_type::Enable(enable
))
290 gtk_widget_set_sensitive(gtk_bin_get_child(GTK_BIN(m_widget
)), enable
);
300 GdkWindow
*wxButton::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
302 return GTK_BUTTON(m_widget
)->event_window
;
305 GtkLabel
*wxButton::GTKGetLabel() const
307 GtkWidget
* child
= gtk_bin_get_child(GTK_BIN(m_widget
));
308 if ( GTK_IS_ALIGNMENT(child
) )
310 GtkWidget
* box
= gtk_bin_get_child(GTK_BIN(child
));
311 GtkLabel
* label
= NULL
;
312 GList
* list
= gtk_container_get_children(GTK_CONTAINER(box
));
313 for (GList
* item
= list
; item
; item
= item
->next
)
315 GtkBoxChild
* boxChild
= static_cast<GtkBoxChild
*>(item
->data
);
316 if ( GTK_IS_LABEL(boxChild
->widget
) )
317 label
= GTK_LABEL(boxChild
->widget
);
324 return GTK_LABEL(child
);
327 void wxButton::DoApplyWidgetStyle(GtkRcStyle
*style
)
329 gtk_widget_modify_style(m_widget
, style
);
330 GtkWidget
* child
= gtk_bin_get_child(GTK_BIN(m_widget
));
331 gtk_widget_modify_style(child
, style
);
333 // for buttons with images, the path to the label is (at least in 2.12)
334 // GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
335 if ( GTK_IS_ALIGNMENT(child
) )
337 GtkWidget
* box
= gtk_bin_get_child(GTK_BIN(child
));
338 if ( GTK_IS_BOX(box
) )
340 GList
* list
= gtk_container_get_children(GTK_CONTAINER(box
));
341 for (GList
* item
= list
; item
; item
= item
->next
)
343 GtkBoxChild
* boxChild
= static_cast<GtkBoxChild
*>(item
->data
);
344 gtk_widget_modify_style(boxChild
->widget
, style
);
351 wxSize
wxButton::DoGetBestSize() const
353 // the default button in wxGTK is bigger than the other ones because of an
354 // extra border around it, but we don't want to take it into account in
355 // our size calculations (otherwise the result is visually ugly), so
356 // always return the size of non default button from here
357 const bool isDefault
= gtk_widget_has_default(m_widget
);
360 // temporarily unset default flag
361 gtk_widget_set_can_default(m_widget
, FALSE
);
364 wxSize
ret( wxControl::DoGetBestSize() );
369 gtk_widget_set_can_default(m_widget
, TRUE
);
372 if (!HasFlag(wxBU_EXACTFIT
))
374 wxSize defaultSize
= GetDefaultSize();
375 if (ret
.x
< defaultSize
.x
)
376 ret
.x
= defaultSize
.x
;
377 if (ret
.y
< defaultSize
.y
)
378 ret
.y
= defaultSize
.y
;
387 wxButton::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
389 return GetDefaultAttributesFromGTKWidget(gtk_button_new
);
392 // ----------------------------------------------------------------------------
394 // ----------------------------------------------------------------------------
396 void wxButton::GTKMouseEnters()
403 void wxButton::GTKMouseLeaves()
410 void wxButton::GTKPressed()
417 void wxButton::GTKReleased()
424 void wxButton::GTKOnFocus(wxFocusEvent
& event
)
431 wxButton::State
wxButton::GTKGetCurrentState() const
433 if ( !IsThisEnabled() )
434 return m_bitmaps
[State_Disabled
].IsOk() ? State_Disabled
: State_Normal
;
436 if ( m_isPressed
&& m_bitmaps
[State_Pressed
].IsOk() )
437 return State_Pressed
;
439 if ( m_isCurrent
&& m_bitmaps
[State_Current
].IsOk() )
440 return State_Current
;
442 if ( HasFocus() && m_bitmaps
[State_Focused
].IsOk() )
443 return State_Focused
;
448 void wxButton::GTKUpdateBitmap()
450 // if we don't show bitmaps at all, there is nothing to update
451 if ( m_bitmaps
[State_Normal
].IsOk() )
453 // if we do show them, this will return a state for which we do have a
455 State state
= GTKGetCurrentState();
457 GTKDoShowBitmap(m_bitmaps
[state
]);
461 void wxButton::GTKDoShowBitmap(const wxBitmap
& bitmap
)
463 wxASSERT_MSG( bitmap
.IsOk(), "invalid bitmap" );
466 if ( DontShowLabel() )
468 image
= gtk_bin_get_child(GTK_BIN(m_widget
));
470 else // have both label and bitmap
473 if ( !gtk_check_version(2,6,0) )
475 image
= gtk_button_get_image(GTK_BUTTON(m_widget
));
478 #endif // __WXGTK26__
480 // buttons with both label and bitmap are only supported with GTK+
483 // it shouldn't be difficult to implement them ourselves for the
484 // previous GTK+ versions by stuffing a container with a label and
485 // an image inside GtkButton but there doesn't seem to be much
486 // point in doing this for ancient GTK+ versions
491 wxCHECK_RET( image
&& GTK_IS_IMAGE(image
), "must have image widget" );
493 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), bitmap
.GetPixbuf());
496 wxBitmap
wxButton::DoGetBitmap(State which
) const
498 return m_bitmaps
[which
];
501 void wxButton::DoSetBitmap(const wxBitmap
& bitmap
, State which
)
506 if ( DontShowLabel() )
508 // we only have the bitmap in this button, never remove it but
509 // do invalidate the best size when the bitmap (and presumably
511 InvalidateBestSize();
514 // normal image is special: setting it enables images for the button and
515 // resetting it to nothing disables all of them
516 else if ( !gtk_check_version(2,6,0) )
518 GtkWidget
*image
= gtk_button_get_image(GTK_BUTTON(m_widget
));
519 if ( image
&& !bitmap
.IsOk() )
521 gtk_container_remove(GTK_CONTAINER(m_widget
), image
);
523 else if ( !image
&& bitmap
.IsOk() )
525 image
= gtk_image_new();
526 gtk_button_set_image(GTK_BUTTON(m_widget
), image
);
528 else // image presence or absence didn't change
530 // don't invalidate best size below
534 InvalidateBestSize();
542 if ( !m_bitmaps
[which
].IsOk() )
544 // we need to install the callbacks to be notified about
545 // the button pressed state change
550 G_CALLBACK(wxgtk_button_press_callback
),
558 G_CALLBACK(wxgtk_button_released_callback
),
563 else // no valid bitmap
565 if ( m_bitmaps
[which
].IsOk() )
567 // we don't need to be notified about the button pressed
568 // state changes any more
569 g_signal_handlers_disconnect_by_func
572 (gpointer
)wxgtk_button_press_callback
,
576 g_signal_handlers_disconnect_by_func
579 (gpointer
)wxgtk_button_released_callback
,
583 // also make sure we don't remain stuck in pressed state
594 // the logic here is the same as above for State_Pressed: we need
595 // to connect the handlers if we must be notified about the changes
596 // in the button current state and we disconnect them when/if we
597 // don't need them any more
600 if ( !m_bitmaps
[which
].IsOk() )
606 G_CALLBACK(wxgtk_button_enter_callback
),
614 G_CALLBACK(wxgtk_button_leave_callback
),
619 else // no valid bitmap
621 if ( m_bitmaps
[which
].IsOk() )
623 g_signal_handlers_disconnect_by_func
626 (gpointer
)wxgtk_button_enter_callback
,
630 g_signal_handlers_disconnect_by_func
633 (gpointer
)wxgtk_button_leave_callback
,
649 Connect(wxEVT_SET_FOCUS
,
650 wxFocusEventHandler(wxButton::GTKOnFocus
));
651 Connect(wxEVT_KILL_FOCUS
,
652 wxFocusEventHandler(wxButton::GTKOnFocus
));
654 else // no valid focused bitmap
656 Disconnect(wxEVT_SET_FOCUS
,
657 wxFocusEventHandler(wxButton::GTKOnFocus
));
658 Disconnect(wxEVT_KILL_FOCUS
,
659 wxFocusEventHandler(wxButton::GTKOnFocus
));
664 // no callbacks to connect/disconnect
668 m_bitmaps
[which
] = bitmap
;
670 // update the bitmap immediately if necessary, otherwise it will be done
671 // when the bitmap for the corresponding state is needed the next time by
673 if ( bitmap
.IsOk() && which
== GTKGetCurrentState() )
675 GTKDoShowBitmap(bitmap
);
679 void wxButton::DoSetBitmapPosition(wxDirection dir
)
682 if ( !gtk_check_version(2,10,0) )
684 GtkPositionType gtkpos
;
688 wxFAIL_MSG( "invalid position" );
692 gtkpos
= GTK_POS_LEFT
;
696 gtkpos
= GTK_POS_RIGHT
;
700 gtkpos
= GTK_POS_TOP
;
704 gtkpos
= GTK_POS_BOTTOM
;
708 gtk_button_set_image_position(GTK_BUTTON(m_widget
), gtkpos
);
709 InvalidateBestSize();
714 #endif // wxUSE_BUTTON