gtkbutton.c 70.2 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
15
16
17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19
20

/*
21
 * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
22
23
24
25
26
 * 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/. 
 */

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

41
#include "config.h"
42
#include <string.h>
43
#include "gtkalignment.h"
Elliot Lee's avatar
Elliot Lee committed
44
45
46
#include "gtkbutton.h"
#include "gtklabel.h"
#include "gtkmain.h"
47
#include "gtkmarshalers.h"
48
49
#include "gtkimage.h"
#include "gtkhbox.h"
50
#include "gtkvbox.h"
51
52
#include "gtkstock.h"
#include "gtkiconfactory.h"
53
#include "gtkactivatable.h"
54
#include "gtksizerequest.h"
55
#include "gtkprivate.h"
56
#include "gtkintl.h"
57

Elliot Lee's avatar
Elliot Lee committed
58

59
60
static const GtkBorder default_default_border = { 1, 1, 1, 1 };
static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
61
static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
Elliot Lee's avatar
Elliot Lee committed
62

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

enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
74
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
75
76
  LAST_SIGNAL
};
77

78
enum {
79
80
  PROP_0,
  PROP_LABEL,
Matthias Clasen's avatar
Matthias Clasen committed
81
  PROP_IMAGE,
82
83
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
84
  PROP_USE_STOCK,
85
86
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
87
  PROP_YALIGN,
88
89
90
91
92
  PROP_IMAGE_POSITION,

  /* activatable properties */
  PROP_ACTIVATABLE_RELATED_ACTION,
  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
93
94
95
96
97
98
99
};

#define GTK_BUTTON_GET_PRIVATE(o)       (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_BUTTON, GtkButtonPrivate))
typedef struct _GtkButtonPrivate GtkButtonPrivate;

struct _GtkButtonPrivate
{
100
101
102
  gfloat          xalign;
  gfloat          yalign;
  GtkWidget      *image;
103
104
105
  guint           align_set             : 1;
  guint           image_is_stock        : 1;
  guint           use_action_appearance : 1;
106
  guint32         grab_time;
107
  GdkDevice      *grab_keyboard;
108
  GtkPositionType image_position;
109
  GtkAction      *action;
110
};
111

112
static void gtk_button_destroy        (GtkWidget          *widget);
113
static void gtk_button_dispose        (GObject            *object);
114
115
116
117
118
119
120
121
122
123
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);
124
125
126
127
128
129
130
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);
static void gtk_button_style_set (GtkWidget * widget, GtkStyle * prev_style);
static void gtk_button_size_allocate (GtkWidget * widget,
				      GtkAllocation * allocation);
Benjamin Otte's avatar
Benjamin Otte committed
131
static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
static gint gtk_button_button_press (GtkWidget * widget,
				     GdkEventButton * event);
static gint gtk_button_button_release (GtkWidget * widget,
				       GdkEventButton * event);
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);
146
147
148
149
150
151
152
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
153

154
155
156
static GObject*	gtk_button_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
157
static void gtk_button_construct_child (GtkButton             *button);
158
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_activatable_interface_init(GtkActivatableIface  *iface);
165
166
167
168
169
170
171
172
173
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);
174

175
static void gtk_button_get_preferred_width       (GtkWidget           *widget,
176
177
						  gint                *minimum_size,
						  gint                *natural_size);
178
static void gtk_button_get_preferred_height      (GtkWidget           *widget,
179
180
						  gint                *minimum_size,
						  gint                *natural_size);
181
  
182
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
183

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

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

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

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

  container_class->child_type = gtk_button_child_type;
225
  container_class->add = gtk_button_add;
226
  gtk_container_class_handle_border_width (container_class);
227
228
229

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
230
  klass->clicked = NULL;
231
232
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
233
  klass->activate = gtk_real_button_activate;
234

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

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

Matthias Clasen's avatar
Matthias Clasen committed
314
315
  /**
   * GtkButton::image:
316
   *
Matthias Clasen's avatar
Matthias Clasen committed
317
   * The child widget to appear next to the button text.
318
   *
Matthias Clasen's avatar
Matthias Clasen committed
319
320
321
322
323
324
325
326
   * 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,
327
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
328

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

345
346
347
  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");

348
349
350
351
352
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
353
   *
354
   * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal.
355
   */ 
Elliot Lee's avatar
Elliot Lee committed
356
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
357
    g_signal_new (I_("pressed"),
358
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
359
360
361
362
363
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, pressed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
364
365
366
367
368
369

  /**
   * GtkButton::released:
   * @button: the object that received the signal
   *
   * Emitted when the button is released.
370
   *
371
   * Deprecated: 2.8: Use the #GtkWidget::button-release-event signal.
372
   */ 
Elliot Lee's avatar
Elliot Lee committed
373
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
374
    g_signal_new (I_("released"),
375
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
376
377
378
379
380
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, released),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
381
382
383
384
385
386
387

  /**
   * 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
388
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
389
    g_signal_new (I_("clicked"),
390
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
391
392
393
394
395
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, clicked),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
396
397
398
399
400
401

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
402
   *
403
   * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal.
404
   */ 
Elliot Lee's avatar
Elliot Lee committed
405
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
406
    g_signal_new (I_("enter"),
407
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
408
409
410
411
412
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, enter),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
413
414
415
416
417
418

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
419
   *
420
   * Deprecated: 2.8: Use the #GtkWidget::leave-notify-event signal.
421
   */ 
Elliot Lee's avatar
Elliot Lee committed
422
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
423
    g_signal_new (I_("leave"),
424
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
425
426
427
428
429
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, leave),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
430
431
432

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
433
   * @widget: the object which received the signal.
434
   *
435
   * The ::activate signal on GtkButton is an action signal and
436
437
   * emitting it causes the button to animate press then release. 
   * Applications should never connect to this signal, but use the
438
   * #GtkButton::clicked signal.
439
   */
440
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
441
    g_signal_new (I_("activate"),
442
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
443
444
445
446
447
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, activate),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
448
  widget_class->activate_signal = button_signals[ACTIVATE];
449

450
451
452
453
454
455
456
457
  /**
   * 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().
   */

458
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
459
					   g_param_spec_boxed ("default-border",
460
							       P_("Default Spacing"),
461
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons"),
462
							       GTK_TYPE_BORDER,
463
							       GTK_PARAM_READABLE));
464

465
466
467
468
469
470
471
472
  /**
   * 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().
   */
473
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
474
					   g_param_spec_boxed ("default-outside-border",
475
							       P_("Default Outside Spacing"),
476
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons that is always drawn outside the border"),
477
							       GTK_TYPE_BORDER,
478
							       GTK_PARAM_READABLE));
479
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
480
					   g_param_spec_int ("child-displacement-x",
481
482
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
483
484
485
							     G_MININT,
							     G_MAXINT,
							     0,
486
							     GTK_PARAM_READABLE));
487
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x    
Matthias Clasen committed
488
					   g_param_spec_int ("child-displacement-y",
489
490
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
491
492
493
							     G_MININT,
							     G_MAXINT,
							     0,
494
							     GTK_PARAM_READABLE));
495

496
497
498
  /**
   * GtkButton:displace-focus:
   *
499
500
   * Whether the child_displacement_x/child_displacement_y properties 
   * should also affect the focus rectangle.
501
502
503
504
505
506
507
   *
   * 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
508
509
								 FALSE,
								 GTK_PARAM_READABLE));
510

511
  /**
512
   * GtkButton:inner-border:
513
   *
514
   * Sets the border between the button edges and child.
515
516
517
518
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
519
520
521
522
523
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
524

525
526
  /**
   * GtkButton::image-spacing:
527
   *
528
   * Spacing in pixels between the image and label.
529
   *
530
531
532
533
534
535
536
537
538
539
540
   * 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));

541
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
Elliot Lee's avatar
Elliot Lee committed
542
543
544
545
546
}

static void
gtk_button_init (GtkButton *button)
{
547
548
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

549
  gtk_widget_set_can_focus (GTK_WIDGET (button), TRUE);
550
  gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE);
551
  gtk_widget_set_has_window (GTK_WIDGET (button), FALSE);
Elliot Lee's avatar
Elliot Lee committed
552

553
554
555
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
556
557
  button->in_button = FALSE;
  button->button_down = FALSE;
558
  button->relief = GTK_RELIEF_NORMAL;
559
560
  button->use_stock = FALSE;
  button->use_underline = FALSE;
561
  button->depressed = FALSE;
562
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
563
  button->focus_on_click = TRUE;
564
565
566

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
567
  priv->align_set = 0;
568
  priv->image_is_stock = TRUE;
569
  priv->image_position = GTK_POS_LEFT;
570
  priv->use_action_appearance = TRUE;
Elliot Lee's avatar
Elliot Lee committed
571
572
}

573
static void
574
gtk_button_destroy (GtkWidget *widget)
575
{
576
577
  GtkButton *button = GTK_BUTTON (widget);

578
579
580
581
582
  if (button->label_text)
    {
      g_free (button->label_text);
      button->label_text = NULL;
    }
583

584
  GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
585
586
}

587
588
589
590
591
592
593
594
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;

595
596
597
  object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type,
                                                                  n_construct_properties,
                                                                  construct_params);
598
599
600
601
602
603
604
605
606
607
608

  button = GTK_BUTTON (object);
  button->constructed = TRUE;

  if (button->label_text != NULL)
    gtk_button_construct_child (button);
  
  return object;
}


Manish Singh's avatar
Manish Singh committed
609
static GType
610
611
gtk_button_child_type  (GtkContainer     *container)
{
Javier Jardón's avatar
Javier Jardón committed
612
  if (!gtk_bin_get_child (GTK_BIN (container)))
613
614
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
615
    return G_TYPE_NONE;
616
617
}

618
static void
Matthias Clasen's avatar
Matthias Clasen committed
619
620
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
621
{
Matthias Clasen's avatar
Matthias Clasen committed
622
623
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

624
625
626
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
627
628
629
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
630
631
632
633
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
634
635
636
637
638
639
      gfloat xscale, yscale;

      g_object_get (alignment,
                    "xscale", &xscale,
                    "yscale", &yscale,
                    NULL);
Matthias Clasen's avatar
Matthias Clasen committed
640
641

      if (priv->align_set)
642
643
644
        gtk_alignment_set (alignment,
                           priv->xalign, priv->yalign,
                           xscale, yscale);
645
646
647
648
649
650
651
    }
}

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

Matthias Clasen's avatar
Matthias Clasen committed
654
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
655
656
}

657
658
659
660
661
662
663
664
665
666
667
668
669
670
static void 
gtk_button_dispose (GObject *object)
{
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

  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);
}

Elliot Lee's avatar
Elliot Lee committed
671
static void
672
673
674
675
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
676
{
677
678
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
679

680
  switch (prop_id)
681
    {
682
    case PROP_LABEL:
683
      gtk_button_set_label (button, g_value_get_string (value));
684
      break;
Matthias Clasen's avatar
Matthias Clasen committed
685
686
687
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
688
689
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
690
      break;
691
692
693
694
695
696
    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;
Soeren Sandmann's avatar
Soeren Sandmann committed
697
698
699
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
700
701
702
703
704
705
    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;
706
707
708
    case PROP_IMAGE_POSITION:
      gtk_button_set_image_position (button, g_value_get_enum (value));
      break;
709
710
711
712
713
714
    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;
715
    default:
716
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
717
718
719
720
721
      break;
    }
}

static void
722
723
724
725
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
726
{
727
728
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
729

730
  switch (prop_id)
731
    {
732
    case PROP_LABEL:
733
      g_value_set_string (value, button->label_text);
734
      break;
Matthias Clasen's avatar
Matthias Clasen committed
735
736
737
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
738
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
739
      g_value_set_enum (value, gtk_button_get_relief (button));
740
      break;
741
742
743
744
745
746
    case PROP_USE_UNDERLINE:
      g_value_set_boolean (value, button->use_underline);
      break;
    case PROP_USE_STOCK:
      g_value_set_boolean (value, button->use_stock);
      break;
Soeren Sandmann's avatar
Soeren Sandmann committed
747
748
749
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
750
751
752
753
754
755
    case PROP_XALIGN:
      g_value_set_float (value, priv->xalign);
      break;
    case PROP_YALIGN:
      g_value_set_float (value, priv->yalign);
      break;
756
757
758
    case PROP_IMAGE_POSITION:
      g_value_set_enum (value, priv->image_position);
      break;
759
760
761
762
763
764
    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;
Tim Janik's avatar
Tim Janik committed
765
    default:
766
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
767
      break;
Elliot Lee's avatar
Elliot Lee committed
768
769
770
    }
}

771
772
773
static void 
gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
{
774
775
  iface->update = gtk_button_update;
  iface->sync_action_properties = gtk_button_sync_action_properties;
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
}

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
792
  GtkWidget *child;
793
794
795
796
797
798
799
800
  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
801
  child = gtk_bin_get_child (GTK_BIN (button));
802
  if (GTK_IS_IMAGE (image) ||
Javier Jardón's avatar
Javier Jardón committed
803
804
      child == NULL ||
      GTK_IS_LABEL (child))
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
    {
      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 
843
844
845
gtk_button_update (GtkActivatable *activatable,
		   GtkAction      *action,
	           const gchar    *property_name)
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable);

  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);
}

872
873
874
static void
gtk_button_sync_action_properties (GtkActivatable *activatable,
			           GtkAction      *action)
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (activatable);

  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);
    }
}

static void
898
899
gtk_button_set_related_action (GtkButton *button,
			       GtkAction *action)
900
901
902
903
904
905
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

  if (priv->action == action)
    return;

906
907
908
909
910
911
  /* 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)
912
    g_signal_connect_after (button, "clicked",
913
914
                            G_CALLBACK (gtk_real_button_clicked), NULL);

915
916
917
918
919
920
  gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);

  priv->action = action;
}

static void
921
922
gtk_button_set_use_action_appearance (GtkButton *button,
				      gboolean   use_appearance)
923
924
925
926
927
928
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

  if (priv->use_action_appearance != use_appearance)
    {
      priv->use_action_appearance = use_appearance;
929
930

      gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (button), priv->action);
931
932
933
    }
}

934
935
936
937
938
939
940
941
/**
 * 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
942
GtkWidget*
943
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
944
{
Manish Singh's avatar
Manish Singh committed
945
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
946
947
}

Matthias Clasen's avatar
Matthias Clasen committed
948
949
950
951
static gboolean
show_image (GtkButton *button)
{
  gboolean show;
952
953
954
955
  
  if (button->label_text)
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
956

957
958
959
960
961
      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
962
963
964
965

  return show;
}

966
967
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
968
{
Matthias Clasen's avatar
Matthias Clasen committed
969
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
970
  GtkStockItem item;
Javier Jardón's avatar
Javier Jardón committed
971
  GtkWidget *child;
972
  GtkWidget *label;
973
  GtkWidget *box;
974
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
975
976
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
977
  gint image_spacing;
978

979
980
  if (!button->constructed)
    return;
981

982
  if (!button->label_text && !priv->image)
983
    return;
984
985
986

  gtk_widget_style_get (GTK_WIDGET (button),
			"image-spacing", &image_spacing,
987
988
			NULL);

989
990
  if (priv->image && !priv->image_is_stock)
    {
991
992
      GtkWidget *parent;

993
      image = g_object_ref (priv->image);
994
995
996
997

      parent = gtk_widget_get_parent (image);
      if (parent)
	gtk_container_remove (GTK_CONTAINER (parent), image);
998
    }
999

1000
1001
  priv->image = NULL;

Javier Jardón's avatar
Javier Jardón committed
1002
1003
1004
  child = gtk_bin_get_child (GTK_BIN (button));
  if (child)
    gtk_container_remove (GTK_CONTAINER (button), child);
1005

1006
  if (button->use_stock &&
1007
      button->label_text &&
1008
      gtk_stock_lookup (button->label_text, &item))
1009
    {
Matthias Clasen's avatar
Matthias Clasen committed
1010
1011
1012
1013
1014
1015
1016
      if (!image)
	image = g_object_ref (gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON));

      label_text = item.label;
    }
  else
    label_text = button->label_text;
1017

Matthias Clasen's avatar
Matthias Clasen committed
1018
1019
1020
  if (image)
    {
      priv->image = image;
1021
      g_object_set (priv->image,
Matthias Clasen's avatar
Matthias Clasen committed
1022
		    "visible", show_image (button),
1023
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
1024
		    NULL);
1025
1026
1027

      if (priv->image_position == GTK_POS_LEFT ||
	  priv->image_position == GTK_POS_RIGHT)
1028
	box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, image_spacing);
1029
      else
1030
	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, image_spacing);
1031

Matthias Clasen's avatar
Matthias Clasen committed
1032
1033
1034
1035
      if (priv->align_set)
	align = gtk_alignment_new (priv->xalign, priv->yalign, 0.0, 0.0);
      else
	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1036
1037
1038
1039
1040
1041

      if (priv->image_position == GTK_POS_LEFT ||
	  priv->image_position == GTK_POS_TOP)
	gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
      else
	gtk_box_pack_end (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
1042
1043
1044

      if (label_text)
	{
1045
          if (button->use_underline || button->use_stock)
1046
1047
1048
1049
1050
1051
1052
            {
	      label = gtk_label_new_with_mnemonic (label_text);
	      gtk_label_set_mnemonic_widget (GTK_LABEL (label),
                                             GTK_WIDGET (button));
            }
          else
            label = gtk_label_new (label_text);
1053

1054
1055
1056
1057
1058
	  if (priv->image_position == GTK_POS_RIGHT ||
	      priv->image_position == GTK_POS_BOTTOM)
	    gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
	  else
	    gtk_box_pack_end (GTK_BOX (box), label, FALSE, FALSE, 0);
1059
	}
1060

1061
      gtk_container_add (GTK_CONTAINER (button), align);
1062
      gtk_container_add (GTK_CONTAINER (align), box);
1063
      gtk_widget_show_all (align);
1064

Matthias Clasen's avatar
Matthias Clasen committed
1065
1066
      g_object_unref (image);

1067
      return;