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