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