1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/button.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_BUTTON
13
14 #ifndef WX_PRECOMP
15 #include "wx/button.h"
16 #endif
17
18 #include "wx/stockitem.h"
19
20 #include "wx/gtk/private.h"
21 #include "wx/gtk/private/list.h"
22 #include "wx/gtk/private/image.h"
23
24 // ----------------------------------------------------------------------------
25 // GTK callbacks
26 // ----------------------------------------------------------------------------
27
28 extern "C"
29 {
30
31 static void
wxgtk_button_clicked_callback(GtkWidget * WXUNUSED (widget),wxButton * button)32 wxgtk_button_clicked_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
33 {
34 if ( button->GTKShouldIgnoreEvent() )
35 return;
36
37 wxCommandEvent event(wxEVT_BUTTON, button->GetId());
38 event.SetEventObject(button);
39 button->HandleWindowEvent(event);
40 }
41
42 //-----------------------------------------------------------------------------
43 // "style_set" from m_widget
44 //-----------------------------------------------------------------------------
45
46 static void
wxgtk_button_style_set_callback(GtkWidget * widget,GtkStyle *,wxButton * win)47 wxgtk_button_style_set_callback(GtkWidget* widget, GtkStyle*, wxButton* win)
48 {
49 /* the default button has a border around it */
50 wxWindow* parent = win->GetParent();
51 if (parent && parent->m_wxwindow && gtk_widget_get_can_default(widget))
52 {
53 GtkBorder* border = NULL;
54 gtk_widget_style_get(widget, "default_border", &border, NULL);
55 if (border)
56 {
57 win->MoveWindow(
58 win->m_x - border->left,
59 win->m_y - border->top,
60 win->m_width + border->left + border->right,
61 win->m_height + border->top + border->bottom);
62 gtk_border_free(border);
63 }
64 }
65 }
66
67 } // extern "C"
68
69 //-----------------------------------------------------------------------------
70 // wxButton
71 //-----------------------------------------------------------------------------
72
73 #ifndef __WXGTK3__
74 bool wxButton::m_exactFitStyleDefined = false;
75 #endif // !__WXGTK3__
76
Create(wxWindow * parent,wxWindowID id,const wxString & label,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)77 bool wxButton::Create(wxWindow *parent,
78 wxWindowID id,
79 const wxString &label,
80 const wxPoint& pos,
81 const wxSize& size,
82 long style,
83 const wxValidator& validator,
84 const wxString& name)
85 {
86 if (!PreCreation( parent, pos, size ) ||
87 !CreateBase( parent, id, pos, size, style, validator, name ))
88 {
89 wxFAIL_MSG( wxT("wxButton creation failed") );
90 return false;
91 }
92
93 // create either a standard button with text label (which may still contain
94 // an image under GTK+ 2.6+) or a bitmap-only button if we don't have any
95 // label
96 const bool
97 useLabel = !(style & wxBU_NOTEXT) && (!label.empty() || wxIsStockID(id));
98 if ( useLabel )
99 {
100 m_widget = gtk_button_new_with_mnemonic("");
101 }
102 else // no label, suppose we will have a bitmap
103 {
104 m_widget = gtk_button_new();
105
106 GtkWidget* image = wxGtkImage::New(this);
107 gtk_widget_show(image);
108 gtk_container_add(GTK_CONTAINER(m_widget), image);
109 }
110
111 g_object_ref(m_widget);
112
113 float x_alignment = 0.5f;
114 if (HasFlag(wxBU_LEFT))
115 x_alignment = 0;
116 else if (HasFlag(wxBU_RIGHT))
117 x_alignment = 1;
118
119 float y_alignment = 0.5f;
120 if (HasFlag(wxBU_TOP))
121 y_alignment = 0;
122 else if (HasFlag(wxBU_BOTTOM))
123 y_alignment = 1;
124
125 #ifdef __WXGTK4__
126 if (useLabel)
127 {
128 g_object_set(gtk_bin_get_child(GTK_BIN(m_widget)),
129 "xalign", x_alignment, "yalign", y_alignment, NULL);
130 }
131 #else
132 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
133 gtk_button_set_alignment(GTK_BUTTON(m_widget), x_alignment, y_alignment);
134 wxGCC_WARNING_RESTORE()
135 #endif
136
137 if ( useLabel )
138 SetLabel(label);
139
140 if (style & wxNO_BORDER)
141 gtk_button_set_relief( GTK_BUTTON(m_widget), GTK_RELIEF_NONE );
142
143 if ( useLabel && (style & wxBU_EXACTFIT) )
144 {
145 #ifdef __WXGTK3__
146 GTKApplyCssStyle("* { padding:0 }");
147 #else
148 // Define a special button style without inner border
149 // if it's not yet done.
150 if ( !m_exactFitStyleDefined )
151 {
152 gtk_rc_parse_string(
153 "style \"wxButton_wxBU_EXACTFIT_style\"\n"
154 "{ GtkButton::inner-border = { 0, 0, 0, 0 } }\n"
155 "widget \"*wxButton_wxBU_EXACTFIT*\" style \"wxButton_wxBU_EXACTFIT_style\"\n"
156 );
157 m_exactFitStyleDefined = true;
158 }
159
160 // Assign the button to the GTK style without inner border.
161 gtk_widget_set_name(m_widget, "wxButton_wxBU_EXACTFIT");
162 #endif // __WXGTK3__ / !__WXGTK3__
163 }
164
165 g_signal_connect_after (m_widget, "clicked",
166 G_CALLBACK (wxgtk_button_clicked_callback),
167 this);
168
169 g_signal_connect_after (m_widget, "style_set",
170 G_CALLBACK (wxgtk_button_style_set_callback),
171 this);
172
173 m_parent->DoAddChild( this );
174
175 PostCreation(size);
176
177 return true;
178 }
179
180
SetDefault()181 wxWindow *wxButton::SetDefault()
182 {
183 wxWindow *oldDefault = wxButtonBase::SetDefault();
184
185 gtk_widget_set_can_default(m_widget, TRUE);
186 gtk_widget_grab_default( m_widget );
187
188 // resize for default border
189 wxgtk_button_style_set_callback( m_widget, NULL, this );
190
191 return oldDefault;
192 }
193
194 /* static */
GetDefaultSize(wxWindow * WXUNUSED (win))195 wxSize wxButtonBase::GetDefaultSize(wxWindow* WXUNUSED(win))
196 {
197 static wxSize size = wxDefaultSize;
198 if (size == wxDefaultSize)
199 {
200 // NB: Default size of buttons should be same as size of stock
201 // buttons as used in most GTK+ apps. Unfortunately it's a little
202 // tricky to obtain this size: stock button's size may be smaller
203 // than size of button in GtkButtonBox and vice versa,
204 // GtkButtonBox's minimal button size may be smaller than stock
205 // button's size. We have to retrieve both values and combine them.
206
207 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
208 GtkWidget *box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
209 #ifdef __WXGTK4__
210 wxString labelGTK = GTKConvertMnemonics(wxGetStockLabel(wxID_CANCEL));
211 GtkWidget *btn = gtk_button_new_with_mnemonic(labelGTK.utf8_str());
212 #else
213 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
214 GtkWidget* btn = gtk_button_new_from_stock("gtk-cancel");
215 wxGCC_WARNING_RESTORE()
216 #endif
217 gtk_container_add(GTK_CONTAINER(box), btn);
218 gtk_container_add(GTK_CONTAINER(wnd), box);
219 GtkRequisition req;
220 gtk_widget_get_preferred_size(btn, NULL, &req);
221
222 gint minwidth, minheight;
223 gtk_widget_style_get(box,
224 "child-min-width", &minwidth,
225 "child-min-height", &minheight,
226 NULL);
227
228 size.x = wxMax(minwidth, req.width);
229 size.y = wxMax(minheight, req.height);
230
231 gtk_widget_destroy(wnd);
232 }
233 return size;
234 }
235
SetLabel(const wxString & lbl)236 void wxButton::SetLabel( const wxString &lbl )
237 {
238 wxCHECK_RET( m_widget != NULL, wxT("invalid button") );
239
240 wxString label(lbl);
241
242 if (label.empty() && wxIsStockID(m_windowId))
243 label = wxGetStockLabel(m_windowId);
244
245 wxAnyButton::SetLabel(label);
246
247 // don't use label if it was explicitly disabled
248 if ( HasFlag(wxBU_NOTEXT) )
249 return;
250
251 #ifndef __WXGTK4__
252 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
253 if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label))
254 {
255 const char *stock = wxGetStockGtkID(m_windowId);
256 if (stock)
257 {
258 gtk_button_set_label(GTK_BUTTON(m_widget), stock);
259 gtk_button_set_use_stock(GTK_BUTTON(m_widget), TRUE);
260 return;
261 }
262 }
263 wxGCC_WARNING_RESTORE()
264 #endif
265
266 // this call is necessary if the button had been initially created without
267 // a (text) label -- then we didn't use gtk_button_new_with_mnemonic() and
268 // so "use-underline" GtkButton property remained unset
269 gtk_button_set_use_underline(GTK_BUTTON(m_widget), TRUE);
270 const wxString labelGTK = GTKConvertMnemonics(label);
271 gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK));
272 #ifndef __WXGTK4__
273 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
274 gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE);
275 wxGCC_WARNING_RESTORE()
276 #endif
277
278 GTKApplyWidgetStyle( false );
279 }
280
281 #if wxUSE_MARKUP
DoSetLabelMarkup(const wxString & markup)282 bool wxButton::DoSetLabelMarkup(const wxString& markup)
283 {
284 wxCHECK_MSG( m_widget != NULL, false, "invalid button" );
285
286 const wxString stripped = RemoveMarkup(markup);
287 if ( stripped.empty() && !markup.empty() )
288 return false;
289
290 SetLabel(stripped);
291
292 GtkLabel * const label = GTKGetLabel();
293 wxCHECK_MSG( label, false, "no label in this button?" );
294
295 GTKSetLabelWithMarkupForLabel(label, markup);
296
297 return true;
298 }
299
GTKGetLabel() const300 GtkLabel *wxButton::GTKGetLabel() const
301 {
302 GtkWidget* child = gtk_bin_get_child(GTK_BIN(m_widget));
303 #ifdef __WXGTK4__
304 if (GTK_IS_LABEL(child))
305 return GTK_LABEL(child);
306
307 return NULL;
308 #else
309 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
310 if ( GTK_IS_ALIGNMENT(child) )
311 {
312 GtkWidget* box = gtk_bin_get_child(GTK_BIN(child));
313 GtkLabel* label = NULL;
314 wxGtkList list(gtk_container_get_children(GTK_CONTAINER(box)));
315 for (GList* item = list; item; item = item->next)
316 {
317 if (GTK_IS_LABEL(item->data))
318 label = GTK_LABEL(item->data);
319 }
320
321 return label;
322 }
323
324 return GTK_LABEL(child);
325 wxGCC_WARNING_RESTORE()
326 #endif
327 }
328 #endif // wxUSE_MARKUP
329
DoApplyWidgetStyle(GtkRcStyle * style)330 void wxButton::DoApplyWidgetStyle(GtkRcStyle *style)
331 {
332 GTKApplyStyle(m_widget, style);
333 GtkWidget* child = gtk_bin_get_child(GTK_BIN(m_widget));
334 GTKApplyStyle(child, style);
335
336 #ifndef __WXGTK4__
337 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
338 // for buttons with images, the path to the label is (at least in 2.12)
339 // GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
340 if ( GTK_IS_ALIGNMENT(child) )
341 {
342 GtkWidget* box = gtk_bin_get_child(GTK_BIN(child));
343 if ( GTK_IS_BOX(box) )
344 {
345 wxGtkList list(gtk_container_get_children(GTK_CONTAINER(box)));
346 for (GList* item = list; item; item = item->next)
347 {
348 GTKApplyStyle(GTK_WIDGET(item->data), style);
349 }
350 }
351 }
352 wxGCC_WARNING_RESTORE()
353 #endif
354 }
355
DoGetBestSize() const356 wxSize wxButton::DoGetBestSize() const
357 {
358 // the default button in wxGTK is bigger than the other ones because of an
359 // extra border around it, but we don't want to take it into account in
360 // our size calculations (otherwise the result is visually ugly), so
361 // always return the size of non default button from here
362 const bool isDefault = gtk_widget_has_default(m_widget) != 0;
363 if ( isDefault )
364 {
365 // temporarily unset default flag
366 gtk_widget_set_can_default(m_widget, FALSE);
367 }
368
369 wxSize ret( wxAnyButton::DoGetBestSize() );
370
371 if ( isDefault )
372 {
373 // set it back again
374 gtk_widget_set_can_default(m_widget, TRUE);
375 }
376
377 if (!HasFlag(wxBU_EXACTFIT))
378 {
379 wxSize defaultSize = GetDefaultSize();
380 if (ret.x < defaultSize.x)
381 ret.x = defaultSize.x;
382 if (ret.y < defaultSize.y)
383 ret.y = defaultSize.y;
384 }
385
386 return ret;
387 }
388
389 // static
390 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))391 wxButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
392 {
393 return GetDefaultAttributesFromGTKWidget(gtk_button_new());
394 }
395 #endif // wxUSE_BUTTON
396