implement support for button bitmaps (normal state only for now) for wxGTK
[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 // data
25 //-----------------------------------------------------------------------------
26
27 extern bool g_blockEventsOnDrag;
28
29 //-----------------------------------------------------------------------------
30 // "clicked"
31 //-----------------------------------------------------------------------------
32
33 extern "C" {
34 static void gtk_button_clicked_callback( GtkWidget *WXUNUSED(widget), wxButton *button )
35 {
36 if (!button->m_hasVMT) return;
37 if (g_blockEventsOnDrag) return;
38
39 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, button->GetId());
40 event.SetEventObject(button);
41 button->HandleWindowEvent(event);
42 }
43 }
44
45 //-----------------------------------------------------------------------------
46 // "style_set" from m_widget
47 //-----------------------------------------------------------------------------
48
49 extern "C" {
50 static void
51 gtk_button_style_set_callback(GtkWidget* widget, GtkStyle*, wxButton* win)
52 {
53 /* the default button has a border around it */
54 wxWindow* parent = win->GetParent();
55 if (parent && parent->m_wxwindow && GTK_WIDGET_CAN_DEFAULT(widget))
56 {
57 GtkBorder* border = NULL;
58 gtk_widget_style_get(widget, "default_border", &border, NULL);
59 if (border)
60 {
61 win->MoveWindow(
62 win->m_x - border->left,
63 win->m_y - border->top,
64 win->m_width + border->left + border->right,
65 win->m_height + border->top + border->bottom);
66 gtk_border_free(border);
67 }
68 }
69 }
70 }
71
72 //-----------------------------------------------------------------------------
73 // wxButton
74 //-----------------------------------------------------------------------------
75
76 IMPLEMENT_DYNAMIC_CLASS(wxButton,wxControl)
77
78 bool wxButton::Create(wxWindow *parent,
79 wxWindowID id,
80 const wxString &label,
81 const wxPoint& pos,
82 const wxSize& size,
83 long style,
84 const wxValidator& validator,
85 const wxString& name)
86 {
87 if (!PreCreation( parent, pos, size ) ||
88 !CreateBase( parent, id, pos, size, style, validator, name ))
89 {
90 wxFAIL_MSG( wxT("wxButton creation failed") );
91 return false;
92 }
93
94 m_widget = gtk_button_new_with_mnemonic("");
95 g_object_ref(m_widget);
96
97 float x_alignment = 0.5;
98 if (HasFlag(wxBU_LEFT))
99 x_alignment = 0.0;
100 else if (HasFlag(wxBU_RIGHT))
101 x_alignment = 1.0;
102
103 float y_alignment = 0.5;
104 if (HasFlag(wxBU_TOP))
105 y_alignment = 0.0;
106 else if (HasFlag(wxBU_BOTTOM))
107 y_alignment = 1.0;
108
109 gtk_button_set_alignment(GTK_BUTTON(m_widget), x_alignment, y_alignment);
110
111 SetLabel(label);
112
113 if (style & wxNO_BORDER)
114 gtk_button_set_relief( GTK_BUTTON(m_widget), GTK_RELIEF_NONE );
115
116 g_signal_connect_after (m_widget, "clicked",
117 G_CALLBACK (gtk_button_clicked_callback),
118 this);
119
120 g_signal_connect_after (m_widget, "style_set",
121 G_CALLBACK (gtk_button_style_set_callback),
122 this);
123
124 m_parent->DoAddChild( this );
125
126 PostCreation(size);
127
128 return true;
129 }
130
131
132 wxWindow *wxButton::SetDefault()
133 {
134 wxWindow *oldDefault = wxButtonBase::SetDefault();
135
136 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
137 gtk_widget_grab_default( m_widget );
138
139 // resize for default border
140 gtk_button_style_set_callback( m_widget, NULL, this );
141
142 return oldDefault;
143 }
144
145 /* static */
146 wxSize wxButtonBase::GetDefaultSize()
147 {
148 static wxSize size = wxDefaultSize;
149 if (size == wxDefaultSize)
150 {
151 // NB: Default size of buttons should be same as size of stock
152 // buttons as used in most GTK+ apps. Unfortunately it's a little
153 // tricky to obtain this size: stock button's size may be smaller
154 // than size of button in GtkButtonBox and vice versa,
155 // GtkButtonBox's minimal button size may be smaller than stock
156 // button's size. We have to retrieve both values and combine them.
157
158 GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
159 GtkWidget *box = gtk_hbutton_box_new();
160 GtkWidget *btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
161 gtk_container_add(GTK_CONTAINER(box), btn);
162 gtk_container_add(GTK_CONTAINER(wnd), box);
163 GtkRequisition req;
164 gtk_widget_size_request(btn, &req);
165
166 gint minwidth, minheight;
167 gtk_widget_style_get(box,
168 "child-min-width", &minwidth,
169 "child-min-height", &minheight,
170 NULL);
171
172 size.x = wxMax(minwidth, req.width);
173 size.y = wxMax(minheight, req.height);
174
175 gtk_widget_destroy(wnd);
176 }
177 return size;
178 }
179
180 void wxButton::SetLabel( const wxString &lbl )
181 {
182 wxCHECK_RET( m_widget != NULL, wxT("invalid button") );
183
184 wxString label(lbl);
185
186 if (label.empty() && wxIsStockID(m_windowId))
187 label = wxGetStockLabel(m_windowId);
188
189 wxControl::SetLabel(label);
190
191 if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label))
192 {
193 const char *stock = wxGetStockGtkID(m_windowId);
194 if (stock)
195 {
196 gtk_button_set_label(GTK_BUTTON(m_widget), stock);
197 gtk_button_set_use_stock(GTK_BUTTON(m_widget), TRUE);
198 return;
199 }
200 }
201
202 const wxString labelGTK = GTKConvertMnemonics(label);
203 gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK));
204 gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE);
205
206 GTKApplyWidgetStyle( false );
207 }
208
209 bool wxButton::Enable( bool enable )
210 {
211 bool isEnabled = IsEnabled();
212
213 if ( !wxControl::Enable( enable ) )
214 return false;
215
216 gtk_widget_set_sensitive(GTK_BIN(m_widget)->child, enable);
217
218 if (!isEnabled && enable)
219 {
220 GTKFixSensitivity();
221 }
222
223 return true;
224 }
225
226 GdkWindow *wxButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
227 {
228 return GTK_BUTTON(m_widget)->event_window;
229 }
230
231 void wxButton::DoApplyWidgetStyle(GtkRcStyle *style)
232 {
233 gtk_widget_modify_style(m_widget, style);
234 GtkWidget *child = GTK_BIN(m_widget)->child;
235 gtk_widget_modify_style(child, style);
236
237 // for buttons with images, the path to the label is (at least in 2.12)
238 // GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
239 if ( GTK_IS_ALIGNMENT(child) )
240 {
241 GtkWidget *box = GTK_BIN(child)->child;
242 if ( GTK_IS_BOX(box) )
243 {
244 for (GList* item = GTK_BOX(box)->children; item; item = item->next)
245 {
246 GtkBoxChild* boxChild = static_cast<GtkBoxChild*>(item->data);
247 gtk_widget_modify_style(boxChild->widget, style);
248 }
249 }
250 }
251 }
252
253 wxSize wxButton::DoGetBestSize() const
254 {
255 // the default button in wxGTK is bigger than the other ones because of an
256 // extra border around it, but we don't want to take it into account in
257 // our size calculations (otherwise the result is visually ugly), so
258 // always return the size of non default button from here
259 const bool isDefault = GTK_WIDGET_HAS_DEFAULT(m_widget);
260 if ( isDefault )
261 {
262 // temporarily unset default flag
263 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_DEFAULT );
264 }
265
266 wxSize ret( wxControl::DoGetBestSize() );
267
268 if ( isDefault )
269 {
270 // set it back again
271 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
272 }
273
274 if (!HasFlag(wxBU_EXACTFIT))
275 {
276 wxSize defaultSize = GetDefaultSize();
277 if (ret.x < defaultSize.x)
278 ret.x = defaultSize.x;
279 if (ret.y < defaultSize.y)
280 ret.y = defaultSize.y;
281 }
282
283 CacheBestSize(ret);
284 return ret;
285 }
286
287 // static
288 wxVisualAttributes
289 wxButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
290 {
291 return GetDefaultAttributesFromGTKWidget(gtk_button_new);
292 }
293
294 // ----------------------------------------------------------------------------
295 // bitmaps support
296 // ----------------------------------------------------------------------------
297
298 wxBitmap wxButton::DoGetBitmap(State which) const
299 {
300 return m_bitmaps[which];
301 }
302
303 void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
304 {
305 #ifdef __WXGTK26__
306 // normal image is special: setting it enables images for the button and
307 // resetting it to nothing disables all of them
308 if ( which == State_Normal )
309 {
310 if ( !gtk_check_version(2,6,0) )
311 {
312 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
313 if ( image && !bitmap.IsOk() )
314 {
315 gtk_container_remove(GTK_CONTAINER(m_widget), image);
316 InvalidateBestSize();
317 }
318 else if ( !image && bitmap.IsOk() )
319 {
320 image = gtk_image_new();
321 gtk_button_set_image(GTK_BUTTON(m_widget), image);
322 InvalidateBestSize();
323 }
324 //else: image presence or absence didn't change
325
326 if ( bitmap.IsOk() )
327 {
328 gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
329 }
330 }
331 }
332 #endif // GTK+ 2.6+
333
334 m_bitmaps[which] = bitmap;
335 }
336
337 void wxButton::DoSetBitmapPosition(wxDirection dir)
338 {
339 #ifdef __WXGTK210__
340 if ( !gtk_check_version(2,10,0) )
341 {
342 GtkPositionType gtkpos;
343 switch ( dir )
344 {
345 default:
346 wxFAIL_MSG( "invalid position" );
347 // fall through
348
349 case wxLEFT:
350 gtkpos = GTK_POS_LEFT;
351 break;
352
353 case wxRIGHT:
354 gtkpos = GTK_POS_RIGHT;
355 break;
356
357 case wxTOP:
358 gtkpos = GTK_POS_TOP;
359 break;
360
361 case wxBOTTOM:
362 gtkpos = GTK_POS_BOTTOM;
363 break;
364 }
365
366 gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
367 }
368 #endif // GTK+ 2.10+
369 }
370
371 #endif // wxUSE_BUTTON