gtkbutton.c 80 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
20 21 22 23 24
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

25 26
/**
 * SECTION:gtkbutton
27
 * @Short_description: A widget that emits a signal when clicked on
28 29
 * @Title: GtkButton
 *
30
 * The #GtkButton widget is generally used to trigger a callback function that is
31 32 33
 * called when the button is pressed.  The various signals and how to use them
 * are outlined below.
 *
34 35
 * The #GtkButton widget can hold any valid child widget.  That is, it can hold
 * almost any other standard #GtkWidget.  The most commonly used child is the
36 37 38
 * #GtkLabel.
 */

39
#include "config.h"
40 41 42 43

#include "gtkbutton.h"
#include "gtkbuttonprivate.h"

44
#include <string.h>
45
#include "gtkalignment.h"
Elliot Lee's avatar
Elliot Lee committed
46 47
#include "gtklabel.h"
#include "gtkmain.h"
48
#include "gtkmarshalers.h"
49
#include "gtkimage.h"
Matthias Clasen's avatar
Matthias Clasen committed
50
#include "gtkbox.h"
51 52
#include "gtkstock.h"
#include "gtkiconfactory.h"
53
#include "gtkactivatable.h"
54
#include "gtksizerequest.h"
55
#include "gtktypebuiltins.h"
56
#include "gtkprivate.h"
57
#include "gtkintl.h"
58
#include "a11y/gtkbuttonaccessible.h"
59
#include "gtkapplicationprivate.h"
60
#include "gtkactionhelper.h"
Elliot Lee's avatar
Elliot Lee committed
61

62 63
static const GtkBorder default_default_border = { 1, 1, 1, 1 };
static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
Elliot Lee's avatar
Elliot Lee committed
64

Matthias Clasen's avatar
Matthias Clasen committed
65
/* Time out before giving up on getting a key release when animating
66 67 68
 * the close button.
 */
#define ACTIVATE_TIMEOUT 250
Elliot Lee's avatar
Elliot Lee committed
69

70

Elliot Lee's avatar
Elliot Lee committed
71 72 73 74 75 76
enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
77
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
78 79
  LAST_SIGNAL
};
80

81
enum {
82 83
  PROP_0,
  PROP_LABEL,
Matthias Clasen's avatar
Matthias Clasen committed
84
  PROP_IMAGE,
85 86
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
87
  PROP_USE_STOCK,
88 89
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
90
  PROP_YALIGN,
91
  PROP_IMAGE_POSITION,
92 93
  PROP_ACTION_NAME,
  PROP_ACTION_TARGET,
94
  PROP_ALWAYS_SHOW_IMAGE,
95 96 97 98

  /* activatable properties */
  PROP_ACTIVATABLE_RELATED_ACTION,
  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
99 100
};

101

102
static void gtk_button_destroy        (GtkWidget          *widget);
103
static void gtk_button_dispose        (GObject            *object);
104 105 106 107 108 109 110 111 112 113
static void gtk_button_set_property   (GObject            *object,
                                       guint               prop_id,
                                       const GValue       *value,
                                       GParamSpec         *pspec);
static void gtk_button_get_property   (GObject            *object,
                                       guint               prop_id,
                                       GValue             *value,
                                       GParamSpec         *pspec);
static void gtk_button_screen_changed (GtkWidget          *widget,
				       GdkScreen          *previous_screen);
114 115 116 117
static void gtk_button_realize (GtkWidget * widget);
static void gtk_button_unrealize (GtkWidget * widget);
static void gtk_button_map (GtkWidget * widget);
static void gtk_button_unmap (GtkWidget * widget);
118
static void gtk_button_style_updated (GtkWidget * widget);
119 120
static void gtk_button_size_allocate (GtkWidget * widget,
				      GtkAllocation * allocation);
Benjamin Otte's avatar
Benjamin Otte committed
121
static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr);
122 123 124 125
static gint gtk_button_button_press (GtkWidget * widget,
				     GdkEventButton * event);
static gint gtk_button_button_release (GtkWidget * widget,
				       GdkEventButton * event);
126 127
static gboolean gtk_button_touch (GtkWidget     *widget,
                                  GdkEventTouch *event);
128 129 130 131 132 133 134 135 136 137
static gint gtk_button_grab_broken (GtkWidget * widget,
				    GdkEventGrabBroken * event);
static gint gtk_button_key_release (GtkWidget * widget, GdkEventKey * event);
static gint gtk_button_enter_notify (GtkWidget * widget,
				     GdkEventCrossing * event);
static gint gtk_button_leave_notify (GtkWidget * widget,
				     GdkEventCrossing * event);
static void gtk_real_button_pressed (GtkButton * button);
static void gtk_real_button_released (GtkButton * button);
static void gtk_real_button_clicked (GtkButton * button);
138 139 140 141 142 143 144
static void gtk_real_button_activate  (GtkButton          *button);
static void gtk_button_update_state   (GtkButton          *button);
static void gtk_button_add            (GtkContainer       *container,
			               GtkWidget          *widget);
static GType gtk_button_child_type    (GtkContainer       *container);
static void gtk_button_finish_activate (GtkButton         *button,
					gboolean           do_it);
Elliot Lee's avatar
Elliot Lee committed
145

146 147 148
static GObject*	gtk_button_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
149
static void gtk_button_construct_child (GtkButton             *button);
150 151 152 153
static void gtk_button_state_changed   (GtkWidget             *widget,
					GtkStateType           previous_state);
static void gtk_button_grab_notify     (GtkWidget             *widget,
					gboolean               was_grabbed);
154

155
static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
156
static void gtk_button_activatable_interface_init(GtkActivatableIface  *iface);
157 158 159 160 161 162 163 164 165
static void gtk_button_update                    (GtkActivatable       *activatable,
				                  GtkAction            *action,
			                          const gchar          *property_name);
static void gtk_button_sync_action_properties    (GtkActivatable       *activatable,
                                                  GtkAction            *action);
static void gtk_button_set_related_action        (GtkButton            *button,
					          GtkAction            *action);
static void gtk_button_set_use_action_appearance (GtkButton            *button,
						  gboolean              use_appearance);
166

167 168 169 170 171 172 173 174 175 176 177 178 179 180
static void gtk_button_get_preferred_width             (GtkWidget           *widget,
                                                        gint                *minimum_size,
                                                        gint                *natural_size);
static void gtk_button_get_preferred_height            (GtkWidget           *widget,
                                                        gint                *minimum_size,
                                                        gint                *natural_size);
static void gtk_button_get_preferred_width_for_height  (GtkWidget           *widget,
                                                        gint                 for_size,
                                                        gint                *minimum_size,
                                                        gint                *natural_size);
static void gtk_button_get_preferred_height_for_width  (GtkWidget           *widget,
                                                        gint                 for_size,
                                                        gint                *minimum_size,
                                                        gint                *natural_size);
181
  
182
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
183

184
G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
185
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
186
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
187
						gtk_button_activatable_interface_init))
Elliot Lee's avatar
Elliot Lee committed
188 189 190 191

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
192
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
193 194 195
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
196
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
197 198
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
199
  
200 201
  gobject_class->constructor  = gtk_button_constructor;
  gobject_class->dispose      = gtk_button_dispose;
Manish Singh's avatar
Manish Singh committed
202 203
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
204

205
  widget_class->get_preferred_width = gtk_button_get_preferred_width;
206
  widget_class->get_preferred_height = gtk_button_get_preferred_height;
207 208
  widget_class->get_preferred_width_for_height = gtk_button_get_preferred_width_for_height;
  widget_class->get_preferred_height_for_width = gtk_button_get_preferred_height_for_width;
209
  widget_class->destroy = gtk_button_destroy;
Matthias Clasen's avatar
Matthias Clasen committed
210
  widget_class->screen_changed = gtk_button_screen_changed;
211
  widget_class->realize = gtk_button_realize;
212
  widget_class->unrealize = gtk_button_unrealize;
213 214
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
215
  widget_class->style_updated = gtk_button_style_updated;
216
  widget_class->size_allocate = gtk_button_size_allocate;
Benjamin Otte's avatar
Benjamin Otte committed
217
  widget_class->draw = gtk_button_draw;
218 219
  widget_class->button_press_event = gtk_button_button_press;
  widget_class->button_release_event = gtk_button_button_release;
220
  widget_class->touch_event = gtk_button_touch;
Matthias Clasen's avatar
Matthias Clasen committed
221
  widget_class->grab_broken_event = gtk_button_grab_broken;
222
  widget_class->key_release_event = gtk_button_key_release;
223 224
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
225 226
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
227 228

  container_class->child_type = gtk_button_child_type;
229
  container_class->add = gtk_button_add;
230
  gtk_container_class_handle_border_width (container_class);
231 232 233

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
234
  klass->clicked = NULL;
235 236
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
237
  klass->activate = gtk_real_button_activate;
238

Manish Singh's avatar
Manish Singh committed
239
  g_object_class_install_property (gobject_class,
240 241
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
242 243
                                                        P_("Label"),
                                                        P_("Text of the label widget inside the button, if the button contains a label widget"),
244
                                                        NULL,
245
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
246
  
Manish Singh's avatar
Manish Singh committed
247
  g_object_class_install_property (gobject_class,
248
                                   PROP_USE_UNDERLINE,
Matthias Clasen's avatar
x  
Matthias Clasen committed
249
                                   g_param_spec_boolean ("use-underline",
250 251
							 P_("Use underline"),
							 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
252
                                                        FALSE,
253
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
254
  
Manish Singh's avatar
Manish Singh committed
255
  g_object_class_install_property (gobject_class,
256
                                   PROP_USE_STOCK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
257
                                   g_param_spec_boolean ("use-stock",
258 259
							 P_("Use stock"),
							 P_("If set, the label is used to pick a stock item instead of being displayed"),
260
                                                        FALSE,
261
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
262
  
263 264
  g_object_class_install_property (gobject_class,
                                   PROP_FOCUS_ON_CLICK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
265
                                   g_param_spec_boolean ("focus-on-click",
266 267
							 P_("Focus on click"),
							 P_("Whether the button grabs focus when it is clicked with the mouse"),
268
							 TRUE,
269
							 GTK_PARAM_READWRITE));
270
  
Manish Singh's avatar
Manish Singh committed
271
  g_object_class_install_property (gobject_class,
272 273
                                   PROP_RELIEF,
                                   g_param_spec_enum ("relief",
274 275
                                                      P_("Border relief"),
                                                      P_("The border relief style"),
276 277
                                                      GTK_TYPE_RELIEF_STYLE,
                                                      GTK_RELIEF_NORMAL,
278
                                                      GTK_PARAM_READWRITE));
279 280 281 282 283
  
  /**
   * GtkButton:xalign:
   *
   * If the child of the button is a #GtkMisc or #GtkAlignment, this property 
284
   * can be used to control its horizontal alignment. 0.0 is left aligned, 
285
   * 1.0 is right aligned.
286
   *
287 288 289 290 291
   * Since: 2.4
   */
  g_object_class_install_property (gobject_class,
                                   PROP_XALIGN,
                                   g_param_spec_float("xalign",
292 293
                                                      P_("Horizontal alignment for child"),
                                                      P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"),
294 295 296
                                                      0.0,
                                                      1.0,
                                                      0.5,
297
                                                      GTK_PARAM_READWRITE));
298 299 300 301 302

  /**
   * GtkButton:yalign:
   *
   * If the child of the button is a #GtkMisc or #GtkAlignment, this property 
303
   * can be used to control its vertical alignment. 0.0 is top aligned, 
304
   * 1.0 is bottom aligned.
305
   *
306 307 308 309 310
   * Since: 2.4
   */
  g_object_class_install_property (gobject_class,
                                   PROP_YALIGN,
                                   g_param_spec_float("yalign",
311 312
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
313 314 315
                                                      0.0,
                                                      1.0,
                                                      0.5,
316
                                                      GTK_PARAM_READWRITE));
Elliot Lee's avatar
Elliot Lee committed
317

Matthias Clasen's avatar
Matthias Clasen committed
318
  /**
319
   * GtkButton:image:
320
   *
Matthias Clasen's avatar
Matthias Clasen committed
321
   * The child widget to appear next to the button text.
322
   *
Matthias Clasen's avatar
Matthias Clasen committed
323 324 325 326 327 328 329 330
   * Since: 2.6
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE,
                                   g_param_spec_object ("image",
                                                        P_("Image widget"),
                                                        P_("Child widget to appear next to the button text"),
                                                        GTK_TYPE_WIDGET,
331
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
332

333 334 335
  /**
   * GtkButton:image-position:
   *
336
   * The position of the image relative to the text inside the button.
337
   *
338 339 340 341 342
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE_POSITION,
                                   g_param_spec_enum ("image-position",
343
                                            P_("Image position"),
344 345 346 347 348
                                                      P_("The position of the image relative to the text"),
                                                      GTK_TYPE_POSITION_TYPE,
                                                      GTK_POS_LEFT,
                                                      GTK_PARAM_READWRITE));

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
  /**
   * GtkButton:always-show-image:
   *
   * If %TRUE, the button will ignore the #GtkSettings:gtk-button-images
   * setting and always show the image, if available.
   *
   * Use this property if the button would be useless or hard to use
   * without the image.
   *
   * Since: 3.6
   */
  g_object_class_install_property (gobject_class,
                                   PROP_ALWAYS_SHOW_IMAGE,
                                   g_param_spec_boolean ("always-show-image",
                                                         P_("Always show image"),
                                                         P_("Whether the image will always be shown"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));

368 369 370
  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");

371 372 373
  g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
  g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");

374 375 376 377 378
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
379
   *
380
   * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal.
381
   */ 
Elliot Lee's avatar
Elliot Lee committed
382
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
383
    g_signal_new (I_("pressed"),
384
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
385 386 387 388 389
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, pressed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
390 391 392 393 394 395

  /**
   * GtkButton::released:
   * @button: the object that received the signal
   *
   * Emitted when the button is released.
396
   *
397
   * Deprecated: 2.8: Use the #GtkWidget::button-release-event signal.
398
   */ 
Elliot Lee's avatar
Elliot Lee committed
399
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
400
    g_signal_new (I_("released"),
401
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
402 403 404 405 406
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, released),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
407 408 409 410 411 412 413

  /**
   * GtkButton::clicked:
   * @button: the object that received the signal
   *
   * Emitted when the button has been activated (pressed and released).
   */ 
Elliot Lee's avatar
Elliot Lee committed
414
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
415
    g_signal_new (I_("clicked"),
416
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
417 418 419 420 421
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, clicked),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
422 423 424 425 426 427

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
428
   *
429
   * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal.
430
   */ 
Elliot Lee's avatar
Elliot Lee committed
431
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
432
    g_signal_new (I_("enter"),
433
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
434 435 436 437 438
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, enter),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
439 440 441 442 443 444

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
445
   *
446
   * Deprecated: 2.8: Use the #GtkWidget::leave-notify-event signal.
447
   */ 
Elliot Lee's avatar
Elliot Lee committed
448
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
449
    g_signal_new (I_("leave"),
450
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
451 452 453 454 455
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, leave),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
456 457 458

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
459
   * @widget: the object which received the signal.
460
   *
461
   * The ::activate signal on GtkButton is an action signal and
462 463
   * emitting it causes the button to animate press then release. 
   * Applications should never connect to this signal, but use the
464
   * #GtkButton::clicked signal.
465
   */
466
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
467
    g_signal_new (I_("activate"),
468
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
469 470 471 472 473
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, activate),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
474
  widget_class->activate_signal = button_signals[ACTIVATE];
475

476 477 478 479 480 481 482 483
  /**
   * GtkButton:default-border:
   *
   * The "default-border" style property defines the extra space to add
   * around a button that can become the default widget of its window.
   * For more information about default widgets, see gtk_widget_grab_default().
   */

484
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
485
					   g_param_spec_boxed ("default-border",
486
							       P_("Default Spacing"),
487
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons"),
488
							       GTK_TYPE_BORDER,
489
							       GTK_PARAM_READABLE));
490

491 492 493 494 495 496 497 498
  /**
   * GtkButton:default-outside-border:
   *
   * The "default-outside-border" style property defines the extra outside
   * space to add around a button that can become the default widget of its
   * window. Extra outside space is always drawn outside the button border.
   * For more information about default widgets, see gtk_widget_grab_default().
   */
499
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
500
					   g_param_spec_boxed ("default-outside-border",
501
							       P_("Default Outside Spacing"),
502
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons that is always drawn outside the border"),
503
							       GTK_TYPE_BORDER,
504
							       GTK_PARAM_READABLE));
505
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
506
					   g_param_spec_int ("child-displacement-x",
507 508
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
509 510 511
							     G_MININT,
							     G_MAXINT,
							     0,
512
							     GTK_PARAM_READABLE));
513
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
514
					   g_param_spec_int ("child-displacement-y",
515 516
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
517 518 519
							     G_MININT,
							     G_MAXINT,
							     0,
520
							     GTK_PARAM_READABLE));
521

522 523 524
  /**
   * GtkButton:displace-focus:
   *
525 526
   * Whether the child_displacement_x/child_displacement_y properties 
   * should also affect the focus rectangle.
527 528 529 530 531 532 533
   *
   * Since: 2.6
   */
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_boolean ("displace-focus",
								 P_("Displace focus"),
								 P_("Whether the child_displacement_x/_y properties should also affect the focus rectangle"),
Johan Dahlin's avatar
Johan Dahlin committed
534 535
								 FALSE,
								 GTK_PARAM_READABLE));
536

537
  /**
538
   * GtkButton:inner-border:
539
   *
540
   * Sets the border between the button edges and child.
541 542
   *
   * Since: 2.10
543 544 545
   *
   * Deprecated: 3.4: Use the standard border and padding CSS properties;
   *   the value of this style property is ignored.
546 547
   */
  gtk_widget_class_install_style_property (widget_class,
548 549 550 551 552
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
553

554 555
  /**
   * GtkButton::image-spacing:
556
   *
557
   * Spacing in pixels between the image and label.
558
   *
559 560 561 562 563 564 565 566 567 568 569
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("image-spacing",
							     P_("Image spacing"),
							     P_("Spacing in pixels between the image and label"),
							     0,
							     G_MAXINT,
							     2,
							     GTK_PARAM_READABLE));

570
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
571 572

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BUTTON_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
573 574 575 576 577
}

static void
gtk_button_init (GtkButton *button)
{
578
  GtkButtonPrivate *priv;
579
  GtkStyleContext *context;
580 581 582 583 584

  button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
                                              GTK_TYPE_BUTTON,
                                              GtkButtonPrivate);
  priv = button->priv;
585

586
  gtk_widget_set_can_focus (GTK_WIDGET (button), TRUE);
587
  gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE);
588
  gtk_widget_set_has_window (GTK_WIDGET (button), FALSE);
Elliot Lee's avatar
Elliot Lee committed
589

590 591 592 593 594 595 596 597 598 599 600
  priv->label_text = NULL;

  priv->constructed = FALSE;
  priv->in_button = FALSE;
  priv->button_down = FALSE;
  priv->relief = GTK_RELIEF_NORMAL;
  priv->use_stock = FALSE;
  priv->use_underline = FALSE;
  priv->depressed = FALSE;
  priv->depress_on_activate = TRUE;
  priv->focus_on_click = TRUE;
601 602 603

  priv->xalign = 0.5;
  priv->yalign = 0.5;
604
  priv->align_set = 0;
605
  priv->image_is_stock = TRUE;
606
  priv->image_position = GTK_POS_LEFT;
607
  priv->use_action_appearance = TRUE;
608 609 610

  context = gtk_widget_get_style_context (GTK_WIDGET (button));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
Elliot Lee's avatar
Elliot Lee committed
611 612
}

613
static void
614
gtk_button_destroy (GtkWidget *widget)
615
{
616
  GtkButton *button = GTK_BUTTON (widget);
617
  GtkButtonPrivate *priv = button->priv;
618

619
  if (priv->label_text)
620
    {
621 622
      g_free (priv->label_text);
      priv->label_text = NULL;
623
    }
624

625
  GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
626 627
}

628 629 630 631 632 633 634
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;
635
  GtkButtonPrivate *priv;
636

637 638 639
  object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type,
                                                                  n_construct_properties,
                                                                  construct_params);
640 641

  button = GTK_BUTTON (object);
642 643 644
  priv = button->priv;

  priv->constructed = TRUE;
645

646
  if (priv->label_text != NULL)
647 648 649 650 651 652
    gtk_button_construct_child (button);
  
  return object;
}


Manish Singh's avatar
Manish Singh committed
653
static GType
654 655
gtk_button_child_type  (GtkContainer     *container)
{
Javier Jardón's avatar
Javier Jardón committed
656
  if (!gtk_bin_get_child (GTK_BIN (container)))
657 658
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
659
    return G_TYPE_NONE;
660 661
}

662
static void
663 664
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
665
{
666
  GtkButtonPrivate *priv = button->priv;
667

668 669 670
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
671 672 673
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
674 675 676 677
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
678 679 680 681 682 683
      gfloat xscale, yscale;

      g_object_get (alignment,
                    "xscale", &xscale,
                    "yscale", &yscale,
                    NULL);
684 685

      if (priv->align_set)
686 687 688
        gtk_alignment_set (alignment,
                           priv->xalign, priv->yalign,
                           xscale, yscale);
689 690 691 692 693 694 695
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
696
  maybe_set_alignment (GTK_BUTTON (container), widget);
697

Matthias Clasen's avatar
Matthias Clasen committed
698
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
699 700
}

701 702 703 704
static void 
gtk_button_dispose (GObject *object)
{
  GtkButton *button = GTK_BUTTON (object);
705
  GtkButtonPrivate *priv = button->priv;
706

707
  g_clear_object (&priv->action_helper);
708

709 710 711 712 713 714 715 716
  if (priv->action)
    {
      gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
      priv->action = NULL;
    }
  G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
}

717 718 719 720 721 722
static void
gtk_button_set_action_name (GtkActionable *actionable,
                            const gchar   *action_name)
{
  GtkButton *button = GTK_BUTTON (actionable);

723
  g_return_if_fail (button->priv->action == NULL);
724

725 726
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
727

728 729 730
  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
  if (action_name)
    g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
731

732
  gtk_action_helper_set_action_name (button->priv->action_helper, action_name);
733 734 735 736 737 738 739 740
}

static void
gtk_button_set_action_target_value (GtkActionable *actionable,
                                    GVariant      *action_target)
{
  GtkButton *button = GTK_BUTTON (actionable);

741 742
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
743

744
  gtk_action_helper_set_action_target_value (button->priv->action_helper, action_target);
745 746
}

Elliot Lee's avatar
Elliot Lee committed
747
static void
748 749 750 751
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
752
{
753
  GtkButton *button = GTK_BUTTON (object);
754
  GtkButtonPrivate *priv = button->priv;
Elliot Lee's avatar
Elliot Lee committed
755

756
  switch (prop_id)
757
    {
758
    case PROP_LABEL:
759
      gtk_button_set_label (button, g_value_get_string (value));
760
      break;
Matthias Clasen's avatar
Matthias Clasen committed
761 762 763
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
764 765 766
    case PROP_ALWAYS_SHOW_IMAGE:
      gtk_button_set_always_show_image (button, g_value_get_boolean (value));
      break;
767 768
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
769
      break;
770 771 772 773 774 775
    case PROP_USE_UNDERLINE:
      gtk_button_set_use_underline (button, g_value_get_boolean (value));
      break;
    case PROP_USE_STOCK:
      gtk_button_set_use_stock (button, g_value_get_boolean (value));
      break;
776 777 778
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
779 780 781 782 783 784
    case PROP_XALIGN:
      gtk_button_set_alignment (button, g_value_get_float (value), priv->yalign);
      break;
    case PROP_YALIGN:
      gtk_button_set_alignment (button, priv->xalign, g_value_get_float (value));
      break;
785 786 787
    case PROP_IMAGE_POSITION:
      gtk_button_set_image_position (button, g_value_get_enum (value));
      break;
788 789 790 791 792 793
    case PROP_ACTIVATABLE_RELATED_ACTION:
      gtk_button_set_related_action (button, g_value_get_object (value));
      break;
    case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
      gtk_button_set_use_action_appearance (button, g_value_get_boolean (value));
      break;
794 795 796 797 798 799
    case PROP_ACTION_NAME:
      gtk_button_set_action_name (GTK_ACTIONABLE (button), g_value_get_string (value));
      break;
    case PROP_ACTION_TARGET:
      gtk_button_set_action_target_value (GTK_ACTIONABLE (button), g_value_get_variant (value));
      break;
800
    default:
801
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
802 803 804 805 806
      break;
    }
}

static void
807 808 809 810
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
811
{
812
  GtkButton *button = GTK_BUTTON (object);
813
  GtkButtonPrivate *priv = button->priv;
814

815
  switch (prop_id)
816
    {
817
    case PROP_LABEL:
818
      g_value_set_string (value, priv->label_text);
819
      break;
Matthias Clasen's avatar
Matthias Clasen committed
820 821 822
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
823 824 825
    case PROP_ALWAYS_SHOW_IMAGE:
      g_value_set_boolean (value, gtk_button_get_always_show_image (button));
      break;
826
    case PROP_RELIEF:
827
      g_value_set_enum (value, priv->relief);
828
      break;
829
    case PROP_USE_UNDERLINE:
830
      g_value_set_boolean (value, priv->use_underline);
831 832
      break;
    case PROP_USE_STOCK:
833
      g_value_set_boolean (value, priv->use_stock);
834
      break;
835
    case PROP_FOCUS_ON_CLICK:
836
      g_value_set_boolean (value, priv->focus_on_click);
837
      break;
838 839 840 841 842 843
    case PROP_XALIGN:
      g_value_set_float (value, priv->xalign);
      break;
    case PROP_YALIGN:
      g_value_set_float (value, priv->yalign);
      break;
844 845 846
    case PROP_IMAGE_POSITION:
      g_value_set_enum (value, priv->image_position);
      break;
847 848 849 850 851 852
    case PROP_ACTIVATABLE_RELATED_ACTION:
      g_value_set_object (value, priv->action);
      break;
    case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
      g_value_set_boolean (value, priv->use_action_appearance);
      break;
853
    case PROP_ACTION_NAME:
854
      g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper));
855 856
      break;
    case PROP_ACTION_TARGET:
857
      g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
858
      break;
Tim Janik's avatar
Tim Janik committed
859
    default:
860
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
861
      break;
Elliot Lee's avatar
Elliot Lee committed
862 863 864
    }
}

865 866 867 868 869
static const gchar *
gtk_button_get_action_name (GtkActionable *actionable)
{
  GtkButton *button = GTK_BUTTON (actionable);

870
  return gtk_action_helper_get_action_name (button->priv->action_helper);
871 872 873 874 875 876 877
}

static GVariant *
gtk_button_get_action_target_value (GtkActionable *actionable)
{
  GtkButton *button = GTK_BUTTON (actionable);

878
  return gtk_action_helper_get_action_target_value (button->priv->action_helper);
879 880 881 882 883 884 885 886 887 888 889
}

static void
gtk_button_actionable_iface_init (GtkActionableInterface *iface)
{
  iface->get_action_name = gtk_button_get_action_name;
  iface->set_action_name = gtk_button_set_action_name;
  iface->get_action_target_value = gtk_button_get_action_target_value;
  iface->set_action_target_value = gtk_button_set_action_target_value;
}

890 891 892
static void 
gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
{
893 894
  iface->update = gtk_button_update;
  iface->sync_action_properties = gtk_button_sync_action_properties;
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
}

static void
activatable_update_stock_id (GtkButton *button,
			     GtkAction *action)
{
  if (!gtk_button_get_use_stock (button))
    return;

  gtk_button_set_label (button, gtk_action_get_stock_id (action));
}

static void
activatable_update_short_label (GtkButton *button,
				GtkAction *action)
{
Javier Jardón's avatar
Javier Jardón committed
911
  GtkWidget *child;
912 913 914 915 916 917 918 919
  GtkWidget *image;

  if (gtk_button_get_use_stock (button))
    return;

  image = gtk_button_get_image (button);

  /* Dont touch custom child... */
Javier Jardón's avatar
Javier Jardón committed
920
  child = gtk_bin_get_child (GTK_BIN (button));
921
  if (GTK_IS_IMAGE (image) ||
Javier Jardón's avatar
Javier Jardón committed
922 923
      child == NULL ||
      GTK_IS_LABEL (child))
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
    {
      gtk_button_set_label (button, gtk_action_get_short_label (action));
      gtk_button_set_use_underline (button, TRUE);
    }
}

static void
activatable_update_icon_name (GtkButton *button,
			      GtkAction *action)
{
  GtkWidget *image;
	      
  if (gtk_button_get_use_stock (button))
    return;

  image = gtk_button_get_image (button);

  if (GTK_IS_IMAGE (image) &&
      (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY ||
       gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME))
    gtk_image_set_from_icon_name (GTK_IMAGE (image),
				  gtk_action_get_icon_name (action), GTK_ICON_SIZE_MENU);
}

static void
activatable_update_gicon (GtkButton *button,
			  GtkAction *action)
{
  GtkWidget *image = gtk_button_get_image (button);
  GIcon *icon = gtk_action_get_gicon (action);
  
  if (GTK_IS_IMAGE (image) &&
      (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY ||
       gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_GICON))
    gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON);
}

static void 
962 963 964
gtk_button_update (GtkActivatable *activatable,
		   GtkAction      *action,
	           const gchar    *property_name)
965
{
966 967
  GtkButton *button = GTK_BUTTON (activatable);
  GtkButtonPrivate *priv = button->priv;
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

  if (strcmp (property_name, "visible") == 0)
    {
      if (gtk_action_is_visible (action))
	gtk_widget_show (GTK_WIDGET (activatable));
      else
	gtk_widget_hide (GTK_WIDGET (activatable));
    }
  else if (strcmp (property_name, "sensitive") == 0)
    gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));

  if (!priv->use_action_appearance)
    return;

  if (strcmp (property_name, "stock-id") == 0)
    activatable_update_stock_id (GTK_BUTTON (activatable), action);
  else if (strcmp (property_name, "gicon") == 0)
    activatable_update_gicon (GTK_BUTTON (activatable), action);
  else if (strcmp (property_name, "short-label") == 0)
    activatable_update_short_label (GTK_BUTTON (activatable), action);
  else if (strcmp (property_name, "icon-name") == 0)
    activatable_update_icon_name (GTK_BUTTON (activatable), action);
}

992 993 994
static void
gtk_button_sync_action_properties (GtkActivatable *activatable,
			           GtkAction      *action)
995
{
996 997
  GtkButton *button = GTK_BUTTON (activatable);
  GtkButtonPrivate *priv = button->priv;
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015

  if (!action)
    return;

  if (gtk_action_is_visible (action))
    gtk_widget_show (GTK_WIDGET (activatable));
  else
    gtk_widget_hide (GTK_WIDGET (activatable));
  
  gtk_widget_set_sensitive (GTK_WIDGET (activatable), gtk_action_is_sensitive (action));
  
  if (priv->use_action_appearance)
    {
      activatable_update_stock_id (GTK_BUTTON (activatable), action);
      activatable_update_short_label (GTK_BUTTON (activatable), action);
      activatable_update_gicon (GTK_BUTTON (activatable), action);
      activatable_update_icon_name (GTK_BUTTON (activatable), action);
    }
1016 1017 1018

  gtk_button_set_always_show_image (button,
                                    gtk_action_get_always_show_image (action));
1019 1020 1021
}

static void
1022 1023
gtk_button_set_related_action (GtkButton *button,
			       GtkAction *action)
1024
{
1025
  GtkButtonPrivate *priv = button->priv;
1026

1027
  g_return_if_fail (gtk_action_helper_get_action_name (button->priv->action_helper) == NULL);
1028

1029 1030 1031
  if (priv->action == action)
    return;

1032 1033 1034 1035 1036 1037
  /* This should be a default handler, but for compatibility reasons
   * we need to support derived classes that don't chain up their
   * clicked handler.
   */
  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
  if (action)
1038
    g_signal_connect_after (button, "clicked",
1039 1040
                            G_CALLBACK (gtk_real_button_clicked), NULL);

1041 1042 1043 1044 1045 1046
  gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);

  priv->action = action;
}

static void
1047 1048
gtk_button_set_use_action_appearance (GtkButton *button,
				      gboolean   use_appearance)
1049
{
1050
  GtkButtonPrivate *priv = button->priv;
1051 1052 1053 1054

  if (priv->use_action_appearance != use_appearance)
    {
      priv->use_action_appearance = use_appearance;
1055 1056

      gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (button), priv->action);
1057 1058 1059
    }
}

1060 1061 1062 1063 1064 1065 1066 1067
/**
 * gtk_button_new:
 *
 * Creates a new #GtkButton widget. To add a child widget to the button,
 * use gtk_container_add().
 *
 * Returns: The newly created #GtkButton widget.
 */
Elliot Lee's avatar
Elliot Lee committed
1068
GtkWidget*
1069
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
1070
{
Manish Singh's avatar
Manish Singh committed
1071
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
1072 1073
}

Matthias Clasen's avatar
Matthias Clasen committed
1074 1075 1076
static gboolean
show_image (GtkButton *button)
{
1077
  GtkButtonPrivate *priv = button->priv;
Matthias Clasen's avatar
Matthias Clasen committed
1078
  gboolean show;
1079

1080
  if (priv->label_text && !priv->always_show_image)
1081 1082
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
1083

1084 1085 1086 1087 1088
      settings = gtk_widget_get_settings (GTK_WIDGET (button));        
      g_object_get (settings, "gtk-button-images", &show, NULL);
    }
  else
    show = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
1089 1090 1091 1092

  return show;
}