]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/button.cpp
implement new wxBG_STYLE_XXX semantics for wxMSW too
[wxWidgets.git] / src / gtk / button.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
93763ad5 2// Name: src/gtk/button.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
dbf858b5 5// Id: $Id$
01111366 6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
1e6feb95
VZ
13#if wxUSE_BUTTON
14
b84aec03 15#ifndef WX_PRECOMP
94aff5ff 16 #include "wx/button.h"
b84aec03
WS
17#endif
18
5f7bcb48 19#include "wx/stockitem.h"
c801d85f 20
9e691f46 21#include "wx/gtk/private.h"
83624f79 22
b4a4eafb
VZ
23// ----------------------------------------------------------------------------
24// GTK callbacks
25// ----------------------------------------------------------------------------
66bd6b93 26
b4a4eafb
VZ
27extern "C"
28{
c801d85f 29
b4a4eafb
VZ
30static void
31wxgtk_button_clicked_callback(GtkWidget *WXUNUSED(widget), wxButton *button)
c801d85f 32{
b4a4eafb
VZ
33 if ( button->GTKShouldIgnoreEvent() )
34 return;
9e691f46 35
acfd422a
RR
36 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, button->GetId());
37 event.SetEventObject(button);
937013e0 38 button->HandleWindowEvent(event);
6de97a3b 39}
b4a4eafb
VZ
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();
865bb325 75}
c801d85f 76
a90c0600
RR
77//-----------------------------------------------------------------------------
78// "style_set" from m_widget
79//-----------------------------------------------------------------------------
80
4cdf71be 81static void
b4a4eafb 82wxgtk_button_style_set_callback(GtkWidget* widget, GtkStyle*, wxButton* win)
a90c0600 83{
f893066b 84 /* the default button has a border around it */
4cdf71be
PC
85 wxWindow* parent = win->GetParent();
86 if (parent && parent->m_wxwindow && GTK_WIDGET_CAN_DEFAULT(widget))
f893066b 87 {
4cdf71be
PC
88 GtkBorder* border = NULL;
89 gtk_widget_style_get(widget, "default_border", &border, NULL);
90 if (border)
f893066b 91 {
4cdf71be
PC
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);
f893066b 98 }
b2ff89d6 99 }
4cdf71be 100}
b4a4eafb
VZ
101
102} // extern "C"
a90c0600 103
c801d85f 104//-----------------------------------------------------------------------------
e1e955e1
RR
105// wxButton
106//-----------------------------------------------------------------------------
107
108IMPLEMENT_DYNAMIC_CLASS(wxButton,wxControl)
c801d85f 109
e8375af8
VZ
110bool wxButton::Create(wxWindow *parent,
111 wxWindowID id,
112 const wxString &label,
113 const wxPoint& pos,
114 const wxSize& size,
115 long style,
116 const wxValidator& validator,
117 const wxString& name)
c801d85f 118{
4dcaf11a
RR
119 if (!PreCreation( parent, pos, size ) ||
120 !CreateBase( parent, id, pos, size, style, validator, name ))
121 {
223d09f6 122 wxFAIL_MSG( wxT("wxButton creation failed") );
93763ad5 123 return false;
4dcaf11a 124 }
c801d85f 125
c37dd6da
VZ
126 // create either a standard button with text label (which may still contain
127 // an image under GTK+ 2.6+) or a bitmap-only button if we don't have any
128 // label
129 const bool useLabel = !label.empty() || wxIsStockID(id);
130 if ( useLabel )
131 {
132 m_widget = gtk_button_new_with_mnemonic("");
133 }
134 else // no label, suppose we will have a bitmap
135 {
136 m_widget = gtk_button_new();
137
138 GtkWidget *image = gtk_image_new();
139 gtk_widget_show(image);
140 gtk_container_add(GTK_CONTAINER(m_widget), image);
141 }
142
9ff9d30c 143 g_object_ref(m_widget);
354aa1e3 144
2e8613b7
RR
145 float x_alignment = 0.5;
146 if (HasFlag(wxBU_LEFT))
147 x_alignment = 0.0;
148 else if (HasFlag(wxBU_RIGHT))
149 x_alignment = 1.0;
150
151 float y_alignment = 0.5;
152 if (HasFlag(wxBU_TOP))
153 y_alignment = 0.0;
154 else if (HasFlag(wxBU_BOTTOM))
155 y_alignment = 1.0;
156
593ac8df 157 gtk_button_set_alignment(GTK_BUTTON(m_widget), x_alignment, y_alignment);
a696db45 158
c37dd6da
VZ
159 if ( useLabel )
160 SetLabel(label);
354aa1e3 161
de1c750f
RR
162 if (style & wxNO_BORDER)
163 gtk_button_set_relief( GTK_BUTTON(m_widget), GTK_RELIEF_NONE );
de1c750f 164
9fa72bd2 165 g_signal_connect_after (m_widget, "clicked",
b4a4eafb 166 G_CALLBACK (wxgtk_button_clicked_callback),
9fa72bd2 167 this);
c801d85f 168
9fa72bd2 169 g_signal_connect_after (m_widget, "style_set",
b4a4eafb 170 G_CALLBACK (wxgtk_button_style_set_callback),
9fa72bd2 171 this);
b2ff89d6 172
f03fc89f 173 m_parent->DoAddChild( this );
9e691f46 174
abdeb9e7 175 PostCreation(size);
db434467 176
4fa87bd9
VS
177 return true;
178}
b2ff89d6 179
32c77a71 180
94aff5ff 181wxWindow *wxButton::SetDefault()
c801d85f 182{
94aff5ff 183 wxWindow *oldDefault = wxButtonBase::SetDefault();
b2ff89d6 184
3502e687
RR
185 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
186 gtk_widget_grab_default( m_widget );
b2ff89d6 187
f893066b 188 // resize for default border
b4a4eafb 189 wxgtk_button_style_set_callback( m_widget, NULL, this );
94aff5ff
VZ
190
191 return oldDefault;
6de97a3b 192}
c801d85f 193
ebea0891 194/* static */
4fa87bd9 195wxSize wxButtonBase::GetDefaultSize()
8dbf4589 196{
4fa87bd9
VS
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_hbutton_box_new();
209 GtkWidget *btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
210 gtk_container_add(GTK_CONTAINER(box), btn);
211 gtk_container_add(GTK_CONTAINER(wnd), box);
212 GtkRequisition req;
213 gtk_widget_size_request(btn, &req);
214
215 gint minwidth, minheight;
216 gtk_widget_style_get(box,
217 "child-min-width", &minwidth,
218 "child-min-height", &minheight,
219 NULL);
220
221 size.x = wxMax(minwidth, req.width);
222 size.y = wxMax(minheight, req.height);
b2ff89d6 223
4fa87bd9
VS
224 gtk_widget_destroy(wnd);
225 }
226 return size;
8dbf4589
RR
227}
228
5f7bcb48 229void wxButton::SetLabel( const wxString &lbl )
c801d85f 230{
223d09f6 231 wxCHECK_RET( m_widget != NULL, wxT("invalid button") );
9e691f46 232
5f7bcb48
VS
233 wxString label(lbl);
234
5f7bcb48
VS
235 if (label.empty() && wxIsStockID(m_windowId))
236 label = wxGetStockLabel(m_windowId);
5f7bcb48
VS
237
238 wxControl::SetLabel(label);
9e691f46 239
5f7bcb48
VS
240 if (wxIsStockID(m_windowId) && wxIsStockLabel(m_windowId, label))
241 {
242 const char *stock = wxGetStockGtkID(m_windowId);
243 if (stock)
244 {
245 gtk_button_set_label(GTK_BUTTON(m_widget), stock);
246 gtk_button_set_use_stock(GTK_BUTTON(m_widget), TRUE);
b04683b1 247 return;
5f7bcb48 248 }
5f7bcb48
VS
249 }
250
4cdf71be 251 const wxString labelGTK = GTKConvertMnemonics(label);
b2ff89d6 252 gtk_button_set_label(GTK_BUTTON(m_widget), wxGTK_CONV(labelGTK));
5f7bcb48 253 gtk_button_set_use_stock(GTK_BUTTON(m_widget), FALSE);
b2ff89d6 254
496e7ec6 255 GTKApplyWidgetStyle( false );
6de97a3b 256}
c801d85f 257
f03fc89f 258bool wxButton::Enable( bool enable )
a9c96bcc 259{
ad60f9e7
JS
260 bool isEnabled = IsEnabled();
261
f03fc89f 262 if ( !wxControl::Enable( enable ) )
93763ad5 263 return false;
9e691f46 264
afa7bd1e 265 gtk_widget_set_sensitive(GTK_BIN(m_widget)->child, enable);
f03fc89f 266
ad60f9e7
JS
267 if (!isEnabled && enable)
268 {
269 GTKFixSensitivity();
270 }
271
b4a4eafb
VZ
272 GTKUpdateBitmap();
273
93763ad5 274 return true;
a9c96bcc
RR
275}
276
ef5c70f9 277GdkWindow *wxButton::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
2b5f62a0 278{
2b5f62a0 279 return GTK_BUTTON(m_widget)->event_window;
2b5f62a0
VZ
280}
281
f40fdaa3 282void wxButton::DoApplyWidgetStyle(GtkRcStyle *style)
868a2826 283{
f40fdaa3 284 gtk_widget_modify_style(m_widget, style);
dfc22083
VZ
285 GtkWidget *child = GTK_BIN(m_widget)->child;
286 gtk_widget_modify_style(child, style);
287
288 // for buttons with images, the path to the label is (at least in 2.12)
289 // GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
290 if ( GTK_IS_ALIGNMENT(child) )
291 {
292 GtkWidget *box = GTK_BIN(child)->child;
293 if ( GTK_IS_BOX(box) )
294 {
f4b0832d
PC
295 for (GList* item = GTK_BOX(box)->children; item; item = item->next)
296 {
297 GtkBoxChild* boxChild = static_cast<GtkBoxChild*>(item->data);
298 gtk_widget_modify_style(boxChild->widget, style);
299 }
dfc22083
VZ
300 }
301 }
a81258be 302}
db434467
RR
303
304wxSize wxButton::DoGetBestSize() const
305{
4f819fe4
VZ
306 // the default button in wxGTK is bigger than the other ones because of an
307 // extra border around it, but we don't want to take it into account in
7be740a3 308 // our size calculations (otherwise the result is visually ugly), so
4f819fe4
VZ
309 // always return the size of non default button from here
310 const bool isDefault = GTK_WIDGET_HAS_DEFAULT(m_widget);
311 if ( isDefault )
312 {
313 // temporarily unset default flag
314 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_DEFAULT );
315 }
316
db434467 317 wxSize ret( wxControl::DoGetBestSize() );
9e691f46 318
4f819fe4
VZ
319 if ( isDefault )
320 {
321 // set it back again
322 GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
323 }
324
8ab696e0
RR
325 if (!HasFlag(wxBU_EXACTFIT))
326 {
4fa87bd9 327 wxSize defaultSize = GetDefaultSize();
7be740a3
VZ
328 if (ret.x < defaultSize.x)
329 ret.x = defaultSize.x;
330 if (ret.y < defaultSize.y)
331 ret.y = defaultSize.y;
8ab696e0 332 }
9e691f46 333
9f884528 334 CacheBestSize(ret);
db434467
RR
335 return ret;
336}
337
9d522606
RD
338// static
339wxVisualAttributes
340wxButton::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
341{
342 return GetDefaultAttributesFromGTKWidget(gtk_button_new);
343}
344
7be740a3
VZ
345// ----------------------------------------------------------------------------
346// bitmaps support
347// ----------------------------------------------------------------------------
348
b4a4eafb
VZ
349void wxButton::GTKMouseEnters()
350{
351 m_isCurrent = true;
352
353 GTKUpdateBitmap();
354}
355
356void wxButton::GTKMouseLeaves()
357{
358 m_isCurrent = false;
359
360 GTKUpdateBitmap();
361}
362
363void wxButton::GTKPressed()
364{
365 m_isPressed = true;
366
367 GTKUpdateBitmap();
368}
369
370void wxButton::GTKReleased()
371{
372 m_isPressed = false;
373
374 GTKUpdateBitmap();
375}
376
377void wxButton::GTKOnFocus(wxFocusEvent& event)
378{
379 event.Skip();
380
381 GTKUpdateBitmap();
382}
383
384wxButton::State wxButton::GTKGetCurrentState() const
385{
386 if ( !IsThisEnabled() )
387 return m_bitmaps[State_Disabled].IsOk() ? State_Disabled : State_Normal;
388
389 if ( m_isPressed && m_bitmaps[State_Pressed].IsOk() )
390 return State_Pressed;
391
392 if ( m_isCurrent && m_bitmaps[State_Current].IsOk() )
393 return State_Current;
394
395 if ( HasFocus() && m_bitmaps[State_Focused].IsOk() )
396 return State_Focused;
397
398 return State_Normal;
399}
400
401void wxButton::GTKUpdateBitmap()
402{
403 State state = GTKGetCurrentState();
404
405 GTKDoShowBitmap(m_bitmaps[state]);
406}
407
408void wxButton::GTKDoShowBitmap(const wxBitmap& bitmap)
409{
410 wxASSERT_MSG( bitmap.IsOk(), "invalid bitmap" );
411
c37dd6da
VZ
412 GtkWidget *image;
413 if ( GetLabel().empty() )
b4a4eafb 414 {
c37dd6da 415 image = GTK_BIN(m_widget)->child;
b4a4eafb 416 }
c37dd6da
VZ
417 else // have both label and bitmap
418 {
419#ifdef __WXGTK26__
420 if ( !gtk_check_version(2,6,0) )
421 {
422 image = gtk_button_get_image(GTK_BUTTON(m_widget));
423 }
424 else
b4a4eafb 425#endif // __WXGTK26__
c37dd6da
VZ
426 {
427 // buttons with both label and bitmap are only supported with GTK+
428 // 2.6 so far
429 //
430 // it shouldn't be difficult to implement them ourselves for the
431 // previous GTK+ versions by stuffing a container with a label and
432 // an image inside GtkButton but there doesn't seem to be much
433 // point in doing this for ancient GTK+ versions
434 return;
435 }
436 }
437
438 wxCHECK_RET( image && GTK_IS_IMAGE(image), "must have image widget" );
439
440 gtk_image_set_from_pixbuf(GTK_IMAGE(image), bitmap.GetPixbuf());
b4a4eafb
VZ
441}
442
7be740a3
VZ
443wxBitmap wxButton::DoGetBitmap(State which) const
444{
445 return m_bitmaps[which];
446}
447
448void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
449{
b4a4eafb 450 switch ( which )
7be740a3 451 {
b4a4eafb 452 case State_Normal:
c37dd6da
VZ
453 if ( GetLabel().empty() )
454 {
455 // we only have the bitmap in this button, never remove it but
456 // do invalidate the best size when the bitmap (and presumably
457 // its size) changes
458 InvalidateBestSize();
459 }
b4a4eafb
VZ
460#ifdef __WXGTK26__
461 // normal image is special: setting it enables images for the button and
462 // resetting it to nothing disables all of them
c37dd6da 463 else if ( !gtk_check_version(2,6,0) )
7be740a3 464 {
b4a4eafb
VZ
465 GtkWidget *image = gtk_button_get_image(GTK_BUTTON(m_widget));
466 if ( image && !bitmap.IsOk() )
467 {
468 gtk_container_remove(GTK_CONTAINER(m_widget), image);
469 }
470 else if ( !image && bitmap.IsOk() )
471 {
472 image = gtk_image_new();
473 gtk_button_set_image(GTK_BUTTON(m_widget), image);
474 }
475 else // image presence or absence didn't change
476 {
477 // don't invalidate best size below
478 break;
479 }
480
7be740a3
VZ
481 InvalidateBestSize();
482 }
b4a4eafb
VZ
483#endif // GTK+ 2.6+
484 break;
485
486 case State_Pressed:
487 if ( bitmap.IsOk() )
488 {
489 if ( !m_bitmaps[which].IsOk() )
490 {
491 // we need to install the callbacks to be notified about
492 // the button pressed state change
493 g_signal_connect
494 (
495 m_widget,
496 "pressed",
497 G_CALLBACK(wxgtk_button_press_callback),
498 this
499 );
500
501 g_signal_connect
502 (
503 m_widget,
504 "released",
505 G_CALLBACK(wxgtk_button_released_callback),
506 this
507 );
508 }
509 }
510 else // no valid bitmap
7be740a3 511 {
b4a4eafb
VZ
512 if ( m_bitmaps[which].IsOk() )
513 {
514 // we don't need to be notified about the button pressed
515 // state changes any more
516 g_signal_handlers_disconnect_by_func
517 (
518 m_widget,
519 (gpointer)wxgtk_button_press_callback,
520 this
521 );
522
523 g_signal_handlers_disconnect_by_func
524 (
525 m_widget,
526 (gpointer)wxgtk_button_released_callback,
527 this
528 );
529
530 // also make sure we don't remain stuck in pressed state
531 if ( m_isPressed )
532 {
533 m_isPressed = false;
534 GTKUpdateBitmap();
535 }
536 }
7be740a3 537 }
b4a4eafb 538 break;
7be740a3 539
b4a4eafb
VZ
540 case State_Current:
541 // the logic here is the same as above for State_Pressed: we need
542 // to connect the handlers if we must be notified about the changes
543 // in the button current state and we disconnect them when/if we
544 // don't need them any more
7be740a3
VZ
545 if ( bitmap.IsOk() )
546 {
b4a4eafb
VZ
547 if ( !m_bitmaps[which].IsOk() )
548 {
549 g_signal_connect
550 (
551 m_widget,
552 "enter",
553 G_CALLBACK(wxgtk_button_enter_callback),
554 this
555 );
556
557 g_signal_connect
558 (
559 m_widget,
560 "leave",
561 G_CALLBACK(wxgtk_button_leave_callback),
562 this
563 );
564 }
7be740a3 565 }
b4a4eafb
VZ
566 else // no valid bitmap
567 {
568 if ( m_bitmaps[which].IsOk() )
569 {
570 g_signal_handlers_disconnect_by_func
571 (
572 m_widget,
573 (gpointer)wxgtk_button_enter_callback,
574 this
575 );
576
577 g_signal_handlers_disconnect_by_func
578 (
579 m_widget,
580 (gpointer)wxgtk_button_leave_callback,
581 this
582 );
583
584 if ( m_isCurrent )
585 {
586 m_isCurrent = false;
587 GTKUpdateBitmap();
588 }
589 }
590 }
591 break;
592
593 case State_Focused:
594 if ( bitmap.IsOk() )
595 {
596 Connect(wxEVT_SET_FOCUS,
597 wxFocusEventHandler(wxButton::GTKOnFocus));
598 Connect(wxEVT_KILL_FOCUS,
599 wxFocusEventHandler(wxButton::GTKOnFocus));
600 }
601 else // no valid focused bitmap
602 {
603 Disconnect(wxEVT_SET_FOCUS,
604 wxFocusEventHandler(wxButton::GTKOnFocus));
605 Disconnect(wxEVT_KILL_FOCUS,
606 wxFocusEventHandler(wxButton::GTKOnFocus));
607 }
608 break;
609
610 default:
611 // no callbacks to connect/disconnect
612 ;
7be740a3 613 }
7be740a3
VZ
614
615 m_bitmaps[which] = bitmap;
b4a4eafb
VZ
616
617 // update the bitmap immediately if necessary, otherwise it will be done
618 // when the bitmap for the corresponding state is needed the next time by
619 // GTKUpdateBitmap()
620 if ( bitmap.IsOk() && which == GTKGetCurrentState() )
621 {
622 GTKDoShowBitmap(bitmap);
623 }
7be740a3
VZ
624}
625
626void wxButton::DoSetBitmapPosition(wxDirection dir)
627{
628#ifdef __WXGTK210__
629 if ( !gtk_check_version(2,10,0) )
630 {
631 GtkPositionType gtkpos;
632 switch ( dir )
633 {
634 default:
635 wxFAIL_MSG( "invalid position" );
636 // fall through
637
638 case wxLEFT:
639 gtkpos = GTK_POS_LEFT;
640 break;
641
642 case wxRIGHT:
643 gtkpos = GTK_POS_RIGHT;
644 break;
645
646 case wxTOP:
647 gtkpos = GTK_POS_TOP;
648 break;
649
650 case wxBOTTOM:
651 gtkpos = GTK_POS_BOTTOM;
652 break;
653 }
654
655 gtk_button_set_image_position(GTK_BUTTON(m_widget), gtkpos);
656 }
657#endif // GTK+ 2.10+
658}
659
1e6feb95 660#endif // wxUSE_BUTTON