gtkbutton.c 79.9 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
 * #GtkLabel.
37
38
39
40
41
42
43
44
 *
 * # CSS nodes
 *
 * GtkButton has a single CSS node with name button. The node will get the
 * style classes .image-button or .text-button, if the content is just an
 * image or label, respectively. It may also receive the .flat style class.
 *
 * Other style classes that are commonly used with GtkButton include
45
46
 * .suggested-action and .destructive-action. In special cases, buttons
 * can be made round by adding the .circular style class.
47
48
49
50
51
 *
 * Button-like widgets like #GtkToggleButton, #GtkMenuButton, #GtkVolumeButton,
 * #GtkLockButton, #GtkColorButton, #GtkFontButton or #GtkFileChooserButton use
 * style classes such as .toggle, .popup, .scale, .lock, .color, .font, .file
 * to differentiate themselves from a plain GtkButton.
52
53
 */

54
#include "config.h"
55
56
57
58

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

59
#include <string.h>
Matthias Clasen's avatar
Matthias Clasen committed
60
#include "deprecated/gtkalignment.h"
Elliot Lee's avatar
Elliot Lee committed
61
62
#include "gtklabel.h"
#include "gtkmain.h"
63
#include "gtkmarshalers.h"
64
#include "gtkimage.h"
Matthias Clasen's avatar
Matthias Clasen committed
65
#include "gtkbox.h"
66
67
#include "deprecated/gtkstock.h"
#include "deprecated/gtkactivatable.h"
68
#include "gtksizerequest.h"
69
#include "gtktypebuiltins.h"
70
#include "gtkwidgetprivate.h"
71
#include "gtkprivate.h"
72
#include "gtkintl.h"
73
#include "a11y/gtkbuttonaccessible.h"
74
#include "gtkapplicationprivate.h"
75
#include "gtkactionhelper.h"
Matthias Clasen's avatar
Matthias Clasen committed
76
77
#include "gtkcsscustomgadgetprivate.h"
#include "gtkcontainerprivate.h"
Elliot Lee's avatar
Elliot Lee committed
78

Matthias Clasen's avatar
Matthias Clasen committed
79
/* Time out before giving up on getting a key release when animating
80
81
82
 * the close button.
 */
#define ACTIVATE_TIMEOUT 250
Elliot Lee's avatar
Elliot Lee committed
83

84

Elliot Lee's avatar
Elliot Lee committed
85
86
87
88
89
90
enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
91
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
92
93
  LAST_SIGNAL
};
94

95
enum {
96
97
  PROP_0,
  PROP_LABEL,
Matthias Clasen's avatar
Matthias Clasen committed
98
  PROP_IMAGE,
99
100
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
101
  PROP_USE_STOCK,
102
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
103
  PROP_YALIGN,
104
  PROP_IMAGE_POSITION,
105
106
107
  PROP_ALWAYS_SHOW_IMAGE,

  /* actionable properties */
108
109
  PROP_ACTION_NAME,
  PROP_ACTION_TARGET,
110
111
112

  /* activatable properties */
  PROP_ACTIVATABLE_RELATED_ACTION,
113
114
  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE,
  LAST_PROP = PROP_ACTION_NAME
115
116
};

117

118
static void gtk_button_finalize       (GObject            *object);
119
static void gtk_button_dispose        (GObject            *object);
120
121
122
123
124
125
126
127
128
129
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);
130
131
132
133
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);
134
static void gtk_button_style_updated (GtkWidget * widget);
135
136
static void gtk_button_size_allocate (GtkWidget * widget,
				      GtkAllocation * allocation);
Benjamin Otte's avatar
Benjamin Otte committed
137
static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr);
138
139
140
141
142
143
144
145
146
147
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);
148
149
static void gtk_real_button_activate  (GtkButton          *button);
static void gtk_button_update_state   (GtkButton          *button);
150
static void gtk_button_enter_leave    (GtkButton          *button);
151
152
153
154
155
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
156

157
static void gtk_button_constructed (GObject *object);
Matthias Clasen's avatar
Matthias Clasen committed
158
static void gtk_button_construct_child (GtkButton             *button);
159
160
161
162
static void gtk_button_state_changed   (GtkWidget             *widget,
					GtkStateType           previous_state);
static void gtk_button_grab_notify     (GtkWidget             *widget,
					gboolean               was_grabbed);
163
164
static void gtk_button_do_release      (GtkButton             *button,
                                        gboolean               emit_clicked);
165

166
static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
167
static void gtk_button_activatable_interface_init(GtkActivatableIface  *iface);
168
169
170
171
172
173
174
175
176
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);
177

178
179
180
181
182
183
184
185
186
187
188
189
190
191
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);
192
193
194
195
196
197
static void gtk_button_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
								    gint       width,
								    gint      *minimum_size,
								    gint      *natural_size,
								    gint      *minimum_baseline,
								    gint      *natural_baseline);
Matthias Clasen's avatar
Matthias Clasen committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

static void     gtk_button_measure  (GtkCssGadget        *gadget,
                                     GtkOrientation       orientation,
                                     int                  for_size,
                                     int                 *minimum_size,
                                     int                 *natural_size,
                                     int                 *minimum_baseline,
                                     int                 *natural_baseline,
                                     gpointer             data);
static void     gtk_button_allocate (GtkCssGadget        *gadget,
                                     const GtkAllocation *allocation,
                                     int                  baseline,
                                     GtkAllocation       *out_clip,
                                     gpointer             data);
static gboolean gtk_button_render   (GtkCssGadget        *gadget,
                                     cairo_t             *cr,
                                     int                  x,
                                     int                  y,
                                     int                  width,
                                     int                  height,
                                     gpointer             data);

220
static GParamSpec *props[LAST_PROP] = { NULL, };
221
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
222

223
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
224
G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
225
                         G_ADD_PRIVATE (GtkButton)
226
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
227
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
228
						gtk_button_activatable_interface_init))
229
G_GNUC_END_IGNORE_DEPRECATIONS;
Elliot Lee's avatar
Elliot Lee committed
230
231
232
233

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
234
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
235
236
237
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
238
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
239
240
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
241
  
242
  gobject_class->constructed  = gtk_button_constructed;
243
  gobject_class->dispose      = gtk_button_dispose;
244
  gobject_class->finalize     = gtk_button_finalize;
Manish Singh's avatar
Manish Singh committed
245
246
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
247

248
  widget_class->get_preferred_width = gtk_button_get_preferred_width;
249
  widget_class->get_preferred_height = gtk_button_get_preferred_height;
250
251
  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;
252
  widget_class->get_preferred_height_and_baseline_for_width = gtk_button_get_preferred_height_and_baseline_for_width;
Matthias Clasen's avatar
Matthias Clasen committed
253
  widget_class->screen_changed = gtk_button_screen_changed;
254
  widget_class->realize = gtk_button_realize;
255
  widget_class->unrealize = gtk_button_unrealize;
256
257
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
258
  widget_class->style_updated = gtk_button_style_updated;
259
  widget_class->size_allocate = gtk_button_size_allocate;
Benjamin Otte's avatar
Benjamin Otte committed
260
  widget_class->draw = gtk_button_draw;
Matthias Clasen's avatar
Matthias Clasen committed
261
  widget_class->grab_broken_event = gtk_button_grab_broken;
262
  widget_class->key_release_event = gtk_button_key_release;
263
264
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
265
266
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
267
268

  container_class->child_type = gtk_button_child_type;
269
  container_class->add = gtk_button_add;
270
  gtk_container_class_handle_border_width (container_class);
271
272
273

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
274
  klass->clicked = NULL;
275
276
  klass->enter = gtk_button_enter_leave;
  klass->leave = gtk_button_enter_leave;
277
  klass->activate = gtk_real_button_activate;
278

279
280
281
282
283
  props[PROP_LABEL] =
    g_param_spec_string ("label",
                         P_("Label"),
                         P_("Text of the label widget inside the button, if the button contains a label widget"),
                         NULL,
284
                         GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
285
  
286
287
288
289
290
  props[PROP_USE_UNDERLINE] =
    g_param_spec_boolean ("use-underline",
                          P_("Use underline"),
                          P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
                          FALSE,
291
                          GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
292
  
293
294
295
296
297
  /**
   * GtkButton:use-stock:
   *
   * Deprecated: 3.10
   */
298
299
300
301
302
  props[PROP_USE_STOCK] =
    g_param_spec_boolean ("use-stock",
                          P_("Use stock"),
                          P_("If set, the label is used to pick a stock item instead of being displayed"),
                          FALSE,
303
                          GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
304
  
305
306
307
308
309
310
  props[PROP_RELIEF] =
    g_param_spec_enum ("relief",
                       P_("Border relief"),
                       P_("The border relief style"),
                       GTK_TYPE_RELIEF_STYLE,
                       GTK_RELIEF_NORMAL,
311
                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
312
313
314
315
316
  
  /**
   * GtkButton:xalign:
   *
   * If the child of the button is a #GtkMisc or #GtkAlignment, this property 
317
   * can be used to control its horizontal alignment. 0.0 is left aligned, 
318
   * 1.0 is right aligned.
319
   *
320
   * Since: 2.4
321
322
323
   *
   * Deprecated: 3.14: Access the child widget directly if you need to control
   * its alignment.
324
   */
325
326
327
328
329
  props[PROP_XALIGN] =
    g_param_spec_float ("xalign",
                        P_("Horizontal alignment for child"),
                        P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"),
                        0.0, 1.0, 0.5,
330
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
331
332
333
334
335

  /**
   * GtkButton:yalign:
   *
   * If the child of the button is a #GtkMisc or #GtkAlignment, this property 
336
   * can be used to control its vertical alignment. 0.0 is top aligned, 
337
   * 1.0 is bottom aligned.
338
   *
339
   * Since: 2.4
340
341
342
   *
   * Deprecated: 3.14: Access the child widget directly if you need to control
   * its alignment.
343
   */
344
345
346
347
348
  props[PROP_YALIGN] =
    g_param_spec_float ("yalign",
                        P_("Vertical alignment for child"),
                        P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
                        0.0, 1.0, 0.5,
349
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
Elliot Lee's avatar
Elliot Lee committed
350

Matthias Clasen's avatar
Matthias Clasen committed
351
  /**
352
   * GtkButton:image:
353
   *
Matthias Clasen's avatar
Matthias Clasen committed
354
   * The child widget to appear next to the button text.
355
   *
Matthias Clasen's avatar
Matthias Clasen committed
356
357
   * Since: 2.6
   */
358
359
360
361
362
  props[PROP_IMAGE] =
    g_param_spec_object ("image",
                         P_("Image widget"),
                         P_("Child widget to appear next to the button text"),
                         GTK_TYPE_WIDGET,
363
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
Matthias Clasen's avatar
Matthias Clasen committed
364

365
366
367
  /**
   * GtkButton:image-position:
   *
368
   * The position of the image relative to the text inside the button.
369
   *
370
371
   * Since: 2.10
   */
372
373
374
375
376
377
  props[PROP_IMAGE_POSITION] =
    g_param_spec_enum ("image-position",
                       P_("Image position"),
                       P_("The position of the image relative to the text"),
                       GTK_TYPE_POSITION_TYPE,
                       GTK_POS_LEFT,
378
                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
379

380
381
382
  /**
   * GtkButton:always-show-image:
   *
383
384
   * If %TRUE, the button will ignore the #GtkSettings:gtk-button-images
   * setting and always show the image, if available.
385
386
387
388
389
390
   *
   * Use this property if the button would be useless or hard to use
   * without the image.
   *
   * Since: 3.6
   */
391
392
393
394
395
  props[PROP_ALWAYS_SHOW_IMAGE] =
     g_param_spec_boolean ("always-show-image",
                           P_("Always show image"),
                           P_("Whether the image will always be shown"),
                           FALSE,
396
                           GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY);
397
398

  g_object_class_install_properties (gobject_class, LAST_PROP, props);
399

400
401
402
  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");

403
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
404
405
  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");
406
  G_GNUC_END_IGNORE_DEPRECATIONS;
407

408
409
410
411
412
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
413
   *
414
   * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal.
415
   */ 
Elliot Lee's avatar
Elliot Lee committed
416
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
417
    g_signal_new (I_("pressed"),
418
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
419
420
421
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, pressed),
		  NULL, NULL,
422
		  NULL,
Manish Singh's avatar
Manish Singh committed
423
		  G_TYPE_NONE, 0);
424
425
426
427
428
429

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

  /**
   * 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
448
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
449
    g_signal_new (I_("clicked"),
450
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
451
452
453
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, clicked),
		  NULL, NULL,
454
		  NULL,
Manish Singh's avatar
Manish Singh committed
455
		  G_TYPE_NONE, 0);
456
457
458
459
460
461

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
462
   *
463
   * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal.
464
   */ 
Elliot Lee's avatar
Elliot Lee committed
465
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
466
    g_signal_new (I_("enter"),
467
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
468
469
470
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, enter),
		  NULL, NULL,
471
		  NULL,
Manish Singh's avatar
Manish Singh committed
472
		  G_TYPE_NONE, 0);
473
474
475
476
477
478

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
479
   *
480
   * Deprecated: 2.8: Use the #GtkWidget::leave-notify-event signal.
481
   */ 
Elliot Lee's avatar
Elliot Lee committed
482
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
483
    g_signal_new (I_("leave"),
484
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
485
486
487
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, leave),
		  NULL, NULL,
488
		  NULL,
Manish Singh's avatar
Manish Singh committed
489
		  G_TYPE_NONE, 0);
490
491
492

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
493
   * @widget: the object which received the signal.
494
   *
495
   * The ::activate signal on GtkButton is an action signal and
496
497
   * emitting it causes the button to animate press then release. 
   * Applications should never connect to this signal, but use the
498
   * #GtkButton::clicked signal.
499
   */
500
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
501
    g_signal_new (I_("activate"),
502
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
503
504
505
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, activate),
		  NULL, NULL,
506
		  NULL,
Manish Singh's avatar
Manish Singh committed
507
		  G_TYPE_NONE, 0);
508
  widget_class->activate_signal = button_signals[ACTIVATE];
509

510
511
512
513
514
515
  /**
   * 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().
516
   *
Matthias Clasen's avatar
Matthias Clasen committed
517
518
   * Deprecated: 3.14: Use CSS margins and padding instead;
   *     the value of this style property is ignored.
519
520
   */

521
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
522
					   g_param_spec_boxed ("default-border",
523
							       P_("Default Spacing"),
524
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons"),
525
							       GTK_TYPE_BORDER,
526
							       GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
527

528
529
530
531
532
533
534
  /**
   * 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().
535
   *
Matthias Clasen's avatar
Matthias Clasen committed
536
537
   * Deprecated: 3.14: Use CSS margins and padding instead;
   *     the value of this style property is ignored.
538
   */
539
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
540
					   g_param_spec_boxed ("default-outside-border",
541
							       P_("Default Outside Spacing"),
542
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons that is always drawn outside the border"),
543
							       GTK_TYPE_BORDER,
544
							       GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
545
546
547
548
549
550
551
552
553

  /**
   * GtkButton:child-displacement-x:
   *
   * How far in the x direction to move the child when the button is depressed.
   *
   * Deprecated: 3.20: Use CSS margins and padding instead;
   *     the value of this style property is ignored.
   */
554
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
555
					   g_param_spec_int ("child-displacement-x",
556
557
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
558
559
560
							     G_MININT,
							     G_MAXINT,
							     0,
561
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
562
563
564
565
566
567
568
569
570

  /**
   * GtkButton:child-displacement-y:
   *
   * How far in the y direction to move the child when the button is depressed.
   *
   * Deprecated: 3.20: Use CSS margins and padding instead;
   *     the value of this style property is ignored.
   */
571
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
572
					   g_param_spec_int ("child-displacement-y",
573
574
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
575
576
577
							     G_MININT,
							     G_MAXINT,
							     0,
578
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
579

580
581
582
  /**
   * GtkButton:displace-focus:
   *
583
   * Whether the child_displacement_x/child_displacement_y properties
584
   * should also affect the focus rectangle.
585
586
   *
   * Since: 2.6
587
588
589
   *
   * Deprecated: 3.20: Use CSS margins and padding instead;
   *     the value of this style property is ignored.
590
591
592
593
594
   */
  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
595
								 FALSE,
596
								 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
597

598
  /**
599
   * GtkButton:inner-border:
600
   *
601
   * Sets the border between the button edges and child.
602
603
   *
   * Since: 2.10
604
605
606
   *
   * Deprecated: 3.4: Use the standard border and padding CSS properties;
   *   the value of this style property is ignored.
607
608
   */
  gtk_widget_class_install_style_property (widget_class,
609
610
611
612
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
613
                                                               GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
614

615
616
  /**
   * GtkButton::image-spacing:
617
   *
618
   * Spacing in pixels between the image and label.
619
   *
620
   * Since: 2.10
621
622
   *
   * Deprecated: 3.20: Use CSS margins and padding instead.
623
624
625
626
627
628
629
630
   */
  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,
631
							     GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
632

633
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BUTTON_ACCESSIBLE);
634
  gtk_widget_class_set_css_name (widget_class, "button");
Elliot Lee's avatar
Elliot Lee committed
635
636
}

637
static void
638
639
640
641
642
multipress_pressed_cb (GtkGestureMultiPress *gesture,
                       guint                 n_press,
                       gdouble               x,
                       gdouble               y,
                       GtkWidget            *widget)
643
644
645
646
{
  GtkButton *button = GTK_BUTTON (widget);
  GtkButtonPrivate *priv = button->priv;

647
  if (gtk_widget_get_focus_on_click (widget) && !gtk_widget_has_focus (widget))
648
    gtk_widget_grab_focus (widget);
649

650
  priv->in_button = TRUE;
651
  g_signal_emit (button, button_signals[PRESSED], 0);
652
  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
653
654
655
}

static void
656
657
658
659
660
multipress_released_cb (GtkGestureMultiPress *gesture,
                        guint                 n_press,
                        gdouble               x,
                        gdouble               y,
                        GtkWidget            *widget)
661
662
{
  GtkButton *button = GTK_BUTTON (widget);
663
664
  GtkButtonPrivate *priv = button->priv;
  GdkEventSequence *sequence;
665
666

  g_signal_emit (button, button_signals[RELEASED], 0);
667
668

  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
669
670

  if (sequence)
671
    {
672
673
      priv->in_button = FALSE;
      gtk_button_update_state (button);
674
    }
675
676
}

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
static void
multipress_gesture_update_cb (GtkGesture       *gesture,
                              GdkEventSequence *sequence,
                              GtkButton        *button)
{
  GtkButtonPrivate *priv = button->priv;
  GtkAllocation allocation;
  gboolean in_button;
  gdouble x, y;

  if (sequence != gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)))
    return;

  gtk_widget_get_allocation (GTK_WIDGET (button), &allocation);
  gtk_gesture_get_point (gesture, sequence, &x, &y);

  in_button = (x >= 0 && y >= 0 && x < allocation.width && y < allocation.height);

  if (priv->in_button != in_button)
    {
      priv->in_button = in_button;
      gtk_button_update_state (button);
    }
}

702
703
704
705
706
707
708
709
static void
multipress_gesture_cancel_cb (GtkGesture       *gesture,
                              GdkEventSequence *sequence,
                              GtkButton        *button)
{
  gtk_button_do_release (button, FALSE);
}

Elliot Lee's avatar
Elliot Lee committed
710
711
712
static void
gtk_button_init (GtkButton *button)
{
713
714
  GtkButtonPrivate *priv;

715
  button->priv = gtk_button_get_instance_private (button);
716
  priv = button->priv;
717

718
  gtk_widget_set_can_focus (GTK_WIDGET (button), TRUE);
719
  gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE);
720
  gtk_widget_set_has_window (GTK_WIDGET (button), FALSE);
Elliot Lee's avatar
Elliot Lee committed
721

722
723
724
725
726
727
728
  priv->label_text = NULL;

  priv->constructed = FALSE;
  priv->in_button = FALSE;
  priv->button_down = FALSE;
  priv->use_stock = FALSE;
  priv->use_underline = FALSE;
729
730
731

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
732
  priv->align_set = 0;
733
  priv->image_is_stock = TRUE;
734
  priv->image_position = GTK_POS_LEFT;
735
  priv->use_action_appearance = TRUE;
736

737
  priv->gesture = gtk_gesture_multi_press_new (GTK_WIDGET (button));
738
  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->gesture), FALSE);
739
  gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (priv->gesture), TRUE);
740
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->gesture), GDK_BUTTON_PRIMARY);
741
742
  g_signal_connect (priv->gesture, "pressed", G_CALLBACK (multipress_pressed_cb), button);
  g_signal_connect (priv->gesture, "released", G_CALLBACK (multipress_released_cb), button);
743
  g_signal_connect (priv->gesture, "update", G_CALLBACK (multipress_gesture_update_cb), button);
744
  g_signal_connect (priv->gesture, "cancel", G_CALLBACK (multipress_gesture_cancel_cb), button);
745
  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->gesture), GTK_PHASE_BUBBLE);
Matthias Clasen's avatar
Matthias Clasen committed
746
747
748
749
750
751
752
753
754

  priv->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (button)),
                                                     GTK_WIDGET (button),
                                                     gtk_button_measure,
                                                     gtk_button_allocate,
                                                     gtk_button_render,
                                                     NULL,
                                                     NULL);

Elliot Lee's avatar
Elliot Lee committed
755
756
}

757
static void
758
gtk_button_finalize (GObject *object)
759
{
760
  GtkButton *button = GTK_BUTTON (object);
761
  GtkButtonPrivate *priv = button->priv;
762

763
  g_clear_pointer (&priv->label_text, g_free);
764
  g_clear_object (&priv->gesture);
Matthias Clasen's avatar
Matthias Clasen committed
765
  g_clear_object (&priv->gadget);
766

767
  G_OBJECT_CLASS (gtk_button_parent_class)->finalize (object);
768
769
}

770
771
static void
gtk_button_constructed (GObject *object)
772
{
773
774
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = button->priv;
775

776
  G_OBJECT_CLASS (gtk_button_parent_class)->constructed (object);
777
778

  priv->constructed = TRUE;
779

780
  if (priv->label_text != NULL || priv->image != NULL)
781
782
783
784
    gtk_button_construct_child (button);
}


Manish Singh's avatar
Manish Singh committed
785
static GType
786
787
gtk_button_child_type  (GtkContainer     *container)
{
Javier Jardón's avatar
Javier Jardón committed
788
  if (!gtk_bin_get_child (GTK_BIN (container)))
789
790
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
791
    return G_TYPE_NONE;
792
793
}

794
static void
Matthias Clasen's avatar
Matthias Clasen committed
795
796
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
797
{
798
  GtkButtonPrivate *priv = button->priv;
Matthias Clasen's avatar
Matthias Clasen committed
799

800
801
  if (!priv->align_set)
    return;
Matthias Clasen's avatar
Matthias Clasen committed
802

803
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
804
805
806
  if (GTK_IS_LABEL (widget))
    g_object_set (widget, "xalign", priv->xalign, "yalign", priv->yalign, NULL);
  else if (GTK_IS_MISC (widget))
807
808
    gtk_misc_set_alignment (GTK_MISC (widget), priv->xalign, priv->yalign);
  else if (GTK_IS_ALIGNMENT (widget))
809
    g_object_set (widget, "xalign", priv->xalign, "yalign", priv->yalign, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
810
G_GNUC_END_IGNORE_DEPRECATIONS
811
812
813
814
815
816
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
817
  maybe_set_alignment (GTK_BUTTON (container), widget);
818

Matthias Clasen's avatar
Matthias Clasen committed
819
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
820
821
}

Matthias Clasen's avatar
Matthias Clasen committed
822
static void
823
824
825
gtk_button_dispose (GObject *object)
{
  GtkButton *button = GTK_BUTTON (object);
826
  GtkButtonPrivate *priv = button->priv;
827

828
  g_clear_object (&priv->action_helper);
829

830
831
  if (priv->action)
    {
832
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
833
      gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
834
      G_GNUC_END_IGNORE_DEPRECATIONS;
835
836
837
838
839
      priv->action = NULL;
    }
  G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
}

840
841
842
843
844
845
static void
gtk_button_set_action_name (GtkActionable *actionable,
                            const gchar   *action_name)
{
  GtkButton *button = GTK_BUTTON (actionable);

846
  g_return_if_fail (button->priv->action == NULL);
847

848
849
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
850

851
852
853
  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);
854

855
  gtk_action_helper_set_action_name (button->priv->action_helper, action_name);
856
857
858
859
860
861
862
863
}

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

864
865
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
866

867
  gtk_action_helper_set_action_target_value (button->priv->action_helper, action_target);
868
869
}

Elliot Lee's avatar
Elliot Lee committed
870
static void
871
872
873
874
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
875
{
876
  GtkButton *button = GTK_BUTTON (object);
877
  GtkButtonPrivate *priv = button->priv;
Elliot Lee's avatar
Elliot Lee committed
878

879
  switch (prop_id)
880
    {
881
    case PROP_LABEL:
882
      gtk_button_set_label (button, g_value_get_string (value));
883
      break;
Matthias Clasen's avatar
Matthias Clasen committed
884
885
886
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
887
888
889
    case PROP_ALWAYS_SHOW_IMAGE:
      gtk_button_set_always_show_image (button, g_value_get_boolean (value));
      break;
890
891
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
892
      break;
893
894
895
896
    case PROP_USE_UNDERLINE:
      gtk_button_set_use_underline (button, g_value_get_boolean (value));
      break;
    case PROP_USE_STOCK:
897
      G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
898
      gtk_button_set_use_stock (button, g_value_get_boolean (value));
899
      G_GNUC_END_IGNORE_DEPRECATIONS;
900
      break;
901
    case PROP_XALIGN:
902
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
903
      gtk_button_set_alignment (button, g_value_get_float (value), priv->yalign);
904
G_GNUC_END_IGNORE_DEPRECATIONS
905
906
      break;
    case PROP_YALIGN:
907
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
908
      gtk_button_set_alignment (button, priv->xalign, g_value_get_float (value));
909
G_GNUC_END_IGNORE_DEPRECATIONS
910
      break;
911
912
913
    case PROP_IMAGE_POSITION:
      gtk_button_set_image_position (button, g_value_get_enum (value));
      break;
914
915
916
917
918
919
    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;
920
921
922
923
924
925
    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;
926
    default: