gtkbutton.c 81.6 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"
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 "gtkwidgetprivate.h"
57
#include "gtkprivate.h"
58
#include "gtkintl.h"
59
#include "a11y/gtkbuttonaccessible.h"
60
#include "gtkapplicationprivate.h"
61
#include "gtkactionhelper.h"
Elliot Lee's avatar
Elliot Lee committed
62

63 64
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
65

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

71

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

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

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

102

103
static void gtk_button_destroy        (GtkWidget          *widget);
104
static void gtk_button_dispose        (GObject            *object);
105 106 107 108 109 110 111 112 113 114
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);
115 116 117 118
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);
119
static void gtk_button_style_updated (GtkWidget * widget);
120 121
static void gtk_button_size_allocate (GtkWidget * widget,
				      GtkAllocation * allocation);
122
static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr);
123 124 125 126
static gint gtk_button_button_press (GtkWidget * widget,
				     GdkEventButton * event);
static gint gtk_button_button_release (GtkWidget * widget,
				       GdkEventButton * event);
127 128
static gboolean gtk_button_touch (GtkWidget     *widget,
                                  GdkEventTouch *event);
129 130 131 132 133 134 135 136 137 138
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);
139 140 141 142 143 144 145
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
146

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

156
static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
157
static void gtk_button_activatable_interface_init(GtkActivatableIface  *iface);
158 159 160 161 162 163 164 165 166
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);
167

168 169 170 171 172 173 174 175 176 177 178 179 180 181
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);
182 183 184 185 186 187
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);
188
  
189
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
190

191
G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
192
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
193
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
194
						gtk_button_activatable_interface_init))
Elliot Lee's avatar
Elliot Lee committed
195 196 197 198

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
199
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
200 201 202
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
203
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
204 205
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
206
  
207 208
  gobject_class->constructor  = gtk_button_constructor;
  gobject_class->dispose      = gtk_button_dispose;
Manish Singh's avatar
Manish Singh committed
209 210
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
211

212
  widget_class->get_preferred_width = gtk_button_get_preferred_width;
213
  widget_class->get_preferred_height = gtk_button_get_preferred_height;
214 215
  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;
216
  widget_class->get_preferred_height_and_baseline_for_width = gtk_button_get_preferred_height_and_baseline_for_width;
217
  widget_class->destroy = gtk_button_destroy;
Matthias Clasen's avatar
Matthias Clasen committed
218
  widget_class->screen_changed = gtk_button_screen_changed;
219
  widget_class->realize = gtk_button_realize;
220
  widget_class->unrealize = gtk_button_unrealize;
221 222
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
223
  widget_class->style_updated = gtk_button_style_updated;
224
  widget_class->size_allocate = gtk_button_size_allocate;
225
  widget_class->draw = gtk_button_draw;
226 227
  widget_class->button_press_event = gtk_button_button_press;
  widget_class->button_release_event = gtk_button_button_release;
228
  widget_class->touch_event = gtk_button_touch;
Matthias Clasen's avatar
Matthias Clasen committed
229
  widget_class->grab_broken_event = gtk_button_grab_broken;
230
  widget_class->key_release_event = gtk_button_key_release;
231 232
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
233 234
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
235 236

  container_class->child_type = gtk_button_child_type;
237
  container_class->add = gtk_button_add;
238
  gtk_container_class_handle_border_width (container_class);
239 240 241

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
242
  klass->clicked = NULL;
243 244
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
245
  klass->activate = gtk_real_button_activate;
246

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

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

326
  /**
327
   * GtkButton:image:
328
   *
329
   * The child widget to appear next to the button text.
330
   *
331 332 333 334 335 336 337 338
   * 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,
339
                                                        GTK_PARAM_READWRITE));
340

341 342 343
  /**
   * GtkButton:image-position:
   *
344
   * The position of the image relative to the text inside the button.
345
   *
346 347 348 349 350
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE_POSITION,
                                   g_param_spec_enum ("image-position",
351
                                            P_("Image position"),
352 353 354 355 356
                                                      P_("The position of the image relative to the text"),
                                                      GTK_TYPE_POSITION_TYPE,
                                                      GTK_POS_LEFT,
                                                      GTK_PARAM_READWRITE));

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
  /**
   * 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));

376 377 378
  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");

379 380 381
  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");

382 383 384 385 386
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
387
   *
388
   * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal.
389
   */ 
Elliot Lee's avatar
Elliot Lee committed
390
  button_signals[PRESSED] =
391
    g_signal_new (I_("pressed"),
392
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
393 394 395 396 397
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, pressed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
398 399 400 401 402 403

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

  /**
   * 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
422
  button_signals[CLICKED] =
423
    g_signal_new (I_("clicked"),
424
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
425 426 427 428 429
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, clicked),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
430 431 432 433 434 435

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
436
   *
437
   * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal.
438
   */ 
Elliot Lee's avatar
Elliot Lee committed
439
  button_signals[ENTER] =
440
    g_signal_new (I_("enter"),
441
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
442 443 444 445 446
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, enter),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
447 448 449 450 451 452

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
453
   *
454
   * Deprecated: 2.8: Use the #GtkWidget::leave-notify-event signal.
455
   */ 
Elliot Lee's avatar
Elliot Lee committed
456
  button_signals[LEAVE] =
457
    g_signal_new (I_("leave"),
458
		  G_OBJECT_CLASS_TYPE (gobject_class),
Manish Singh's avatar
Manish Singh committed
459 460 461 462 463
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, leave),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
464 465 466

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

484 485 486 487 488 489 490 491
  /**
   * 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().
   */

492
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
493
					   g_param_spec_boxed ("default-border",
494
							       P_("Default Spacing"),
495
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons"),
496
							       GTK_TYPE_BORDER,
497
							       GTK_PARAM_READABLE));
498

499 500 501 502 503 504 505 506
  /**
   * 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().
   */
507
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
508
					   g_param_spec_boxed ("default-outside-border",
509
							       P_("Default Outside Spacing"),
510
							       P_("Extra space to add for GTK_CAN_DEFAULT buttons that is always drawn outside the border"),
511
							       GTK_TYPE_BORDER,
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-x",
515 516
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
517 518 519
							     G_MININT,
							     G_MAXINT,
							     0,
520
							     GTK_PARAM_READABLE));
521
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
522
					   g_param_spec_int ("child-displacement-y",
523 524
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
525 526 527
							     G_MININT,
							     G_MAXINT,
							     0,
528
							     GTK_PARAM_READABLE));
529

530 531 532
  /**
   * GtkButton:displace-focus:
   *
533 534
   * Whether the child_displacement_x/child_displacement_y properties 
   * should also affect the focus rectangle.
535 536 537 538 539 540 541
   *
   * 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
542 543
								 FALSE,
								 GTK_PARAM_READABLE));
544

545
  /**
546
   * GtkButton:inner-border:
547
   *
548
   * Sets the border between the button edges and child.
549 550
   *
   * Since: 2.10
551 552 553
   *
   * Deprecated: 3.4: Use the standard border and padding CSS properties;
   *   the value of this style property is ignored.
554 555
   */
  gtk_widget_class_install_style_property (widget_class,
556 557 558 559 560
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
561

562 563
  /**
   * GtkButton::image-spacing:
564
   *
565
   * Spacing in pixels between the image and label.
566
   *
567 568 569 570 571 572 573 574 575 576 577
   * 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));

578
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
579 580

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_BUTTON_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
581 582 583 584 585
}

static void
gtk_button_init (GtkButton *button)
{
586
  GtkButtonPrivate *priv;
587
  GtkStyleContext *context;
588 589 590 591 592

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

594
  gtk_widget_set_can_focus (GTK_WIDGET (button), TRUE);
595
  gtk_widget_set_receives_default (GTK_WIDGET (button), TRUE);
596
  gtk_widget_set_has_window (GTK_WIDGET (button), FALSE);
Elliot Lee's avatar
Elliot Lee committed
597

598 599 600 601 602 603 604 605 606 607 608
  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;
609 610 611

  priv->xalign = 0.5;
  priv->yalign = 0.5;
612
  priv->align_set = 0;
613
  priv->image_is_stock = TRUE;
614
  priv->image_position = GTK_POS_LEFT;
615
  priv->use_action_appearance = TRUE;
616 617 618

  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
619 620
}

621
static void
622
gtk_button_destroy (GtkWidget *widget)
623
{
624
  GtkButton *button = GTK_BUTTON (widget);
625
  GtkButtonPrivate *priv = button->priv;
626

627
  if (priv->label_text)
628
    {
629 630
      g_free (priv->label_text);
      priv->label_text = NULL;
631
    }
632

633
  GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
634 635
}

636 637 638 639 640 641 642
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;
643
  GtkButtonPrivate *priv;
644

645 646 647
  object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type,
                                                                  n_construct_properties,
                                                                  construct_params);
648 649

  button = GTK_BUTTON (object);
650 651 652
  priv = button->priv;

  priv->constructed = TRUE;
653

654
  if (priv->label_text != NULL)
655 656 657 658 659 660
    gtk_button_construct_child (button);
  
  return object;
}


Manish Singh's avatar
Manish Singh committed
661
static GType
662 663
gtk_button_child_type  (GtkContainer     *container)
{
Javier Jardón's avatar
Javier Jardón committed
664
  if (!gtk_bin_get_child (GTK_BIN (container)))
665 666
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
667
    return G_TYPE_NONE;
668 669
}

670
static void
671 672
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
673
{
674
  GtkButtonPrivate *priv = button->priv;
675

676 677 678
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
679 680 681
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
682 683 684 685
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
686 687 688 689 690 691
      gfloat xscale, yscale;

      g_object_get (alignment,
                    "xscale", &xscale,
                    "yscale", &yscale,
                    NULL);
692 693

      if (priv->align_set)
694 695 696
        gtk_alignment_set (alignment,
                           priv->xalign, priv->yalign,
                           xscale, yscale);
697 698 699 700 701 702 703
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
704
  maybe_set_alignment (GTK_BUTTON (container), widget);
705

Matthias Clasen's avatar
Matthias Clasen committed
706
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
707 708
}

709 710 711 712
static void 
gtk_button_dispose (GObject *object)
{
  GtkButton *button = GTK_BUTTON (object);
713
  GtkButtonPrivate *priv = button->priv;
714

715
  g_clear_object (&priv->action_helper);
716

717 718 719 720 721 722 723 724
  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);
}

725 726 727 728 729 730
static void
gtk_button_set_action_name (GtkActionable *actionable,
                            const gchar   *action_name)
{
  GtkButton *button = GTK_BUTTON (actionable);

731
  g_return_if_fail (button->priv->action == NULL);
732

733 734
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
735

736 737 738
  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);
739

740
  gtk_action_helper_set_action_name (button->priv->action_helper, action_name);
741 742 743 744 745 746 747 748
}

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

749 750
  if (!button->priv->action_helper)
    button->priv->action_helper = gtk_action_helper_new (actionable);
751

752
  gtk_action_helper_set_action_target_value (button->priv->action_helper, action_target);
753 754
}

Elliot Lee's avatar
Elliot Lee committed
755
static void
756 757 758 759
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
760
{
761
  GtkButton *button = GTK_BUTTON (object);
762
  GtkButtonPrivate *priv = button->priv;
Elliot Lee's avatar
Elliot Lee committed
763

764
  switch (prop_id)
765
    {
766
    case PROP_LABEL:
767
      gtk_button_set_label (button, g_value_get_string (value));
768
      break;
769 770 771
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
772 773 774
    case PROP_ALWAYS_SHOW_IMAGE:
      gtk_button_set_always_show_image (button, g_value_get_boolean (value));
      break;
775 776
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
777
      break;
778 779 780 781 782 783
    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;
784 785 786
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
787 788 789 790 791 792
    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;
793 794 795
    case PROP_IMAGE_POSITION:
      gtk_button_set_image_position (button, g_value_get_enum (value));
      break;
796 797 798 799 800 801
    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;
802 803 804 805 806 807
    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;
808
    default:
809
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
810 811 812 813 814
      break;
    }
}

static void
815 816 817 818
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
819
{
820
  GtkButton *button = GTK_BUTTON (object);
821
  GtkButtonPrivate *priv = button->priv;
822

823
  switch (prop_id)
824
    {
825
    case PROP_LABEL:
826
      g_value_set_string (value, priv->label_text);
827
      break;
828 829 830
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
831 832 833
    case PROP_ALWAYS_SHOW_IMAGE:
      g_value_set_boolean (value, gtk_button_get_always_show_image (button));
      break;
834
    case PROP_RELIEF:
835
      g_value_set_enum (value, priv->relief);
836
      break;
837
    case PROP_USE_UNDERLINE:
838
      g_value_set_boolean (value, priv->use_underline);
839 840
      break;
    case PROP_USE_STOCK:
841
      g_value_set_boolean (value, priv->use_stock);
842
      break;
843
    case PROP_FOCUS_ON_CLICK:
844
      g_value_set_boolean (value, priv->focus_on_click);
845
      break;
846 847 848 849 850 851
    case PROP_XALIGN:
      g_value_set_float (value, priv->xalign);
      break;
    case PROP_YALIGN:
      g_value_set_float (value, priv->yalign);
      break;
852 853 854
    case PROP_IMAGE_POSITION:
      g_value_set_enum (value, priv->image_position);
      break;
855 856 857 858 859 860
    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;
861
    case PROP_ACTION_NAME:
862
      g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper));
863 864
      break;
    case PROP_ACTION_TARGET:
865
      g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
866
      break;
Tim Janik's avatar
Tim Janik committed
867
    default:
868
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
869
      break;
Elliot Lee's avatar
Elliot Lee committed
870 871 872
    }
}

873 874 875 876 877
static const gchar *
gtk_button_get_action_name (GtkActionable *actionable)
{
  GtkButton *button = GTK_BUTTON (actionable);

878
  return gtk_action_helper_get_action_name (button->priv->action_helper);
879 880 881 882 883 884 885
}

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

886
  return gtk_action_helper_get_action_target_value (button->priv->action_helper);
887 888 889 890 891 892 893 894 895 896 897
}

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

898 899 900
static void 
gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
{
901 902
  iface->update = gtk_button_update;
  iface->sync_action_properties = gtk_button_sync_action_properties;
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
}

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
919
  GtkWidget *child;
920 921 922 923 924 925 926 927
  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
928
  child = gtk_bin_get_child (GTK_BIN (button));
929
  if (GTK_IS_IMAGE (image) ||
Javier Jardón's avatar
Javier Jardón committed
930 931
      child == NULL ||
      GTK_IS_LABEL (child))
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 962 963 964 965 966 967 968 969
    {
      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 
970 971 972
gtk_button_update (GtkActivatable *activatable,
		   GtkAction      *action,
	           const gchar    *property_name)
973
{
974 975
  GtkButton *button = GTK_BUTTON (activatable);
  GtkButtonPrivate *priv = button->priv;
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999

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

1000 1001 1002
static void
gtk_button_sync_action_properties (GtkActivatable *activatable,
			           GtkAction      *action)
1003
{
1004 1005
  GtkButton *button = GTK_BUTTON (activatable);
  GtkButtonPrivate *priv = button->priv;
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023

  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);
    }
1024 1025 1026

  gtk_button_set_always_show_image (button,
                                    gtk_action_get_always_show_image (action));
1027 1028 1029
}

static void
1030 1031
gtk_button_set_related_action (GtkButton *button,
			       GtkAction *action)
1032
{
1033
  GtkButtonPrivate *priv = button->priv;
1034

1035
  g_return_if_fail (gtk_action_helper_get_action_name (button->priv->action_helper) == NULL);
1036

1037 1038 1039
  if (priv->action == action)
    return;

1040 1041 1042 1043 1044 1045
  /* 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)
1046
    g_signal_connect_after (button, "clicked",
1047 1048
                            G_CALLBACK (gtk_real_button_clicked), NULL);

1049 1050 1051 1052 1053 1054
  gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);

  priv->action = action;
}

static void
1055 1056
gtk_button_set_use_action_appearance (GtkButton *button,
				      gboolean   use_appearance)
1057
{
1058
  GtkButtonPrivate *priv = button->priv;
1059 1060 1061 1062

  if (priv->use_action_appearance != use_appearance)
    {
      priv->use_action_appearance = use_appearance;
1063 1064

      gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (button), priv->action);
1065 1066 1067
    }
}

1068 1069 1070 1071 1072 1073 1074 1075
/**
 * 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
1076
GtkWidget*
1077
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
1078
{
Manish Singh's avatar
Manish Singh committed
1079
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
1080 1081
}

Matthias Clasen's avatar
Matthias Clasen committed
1082 1083 1084
static gboolean
show_image (GtkButton *button)
{
1085
  GtkButtonPrivate *priv = button->priv;
Matthias Clasen's avatar
Matthias Clasen committed
1086
  gboolean show;
1087

1088
  if (priv->label_text && !priv->always_show_image)
1089 1090
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
1091

1092 1093 1094 1095 1096
      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
1097 1098 1099 1100

  return show;
}

1101

1102 1103
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
1104
{
1105
  GtkButtonPrivate *priv = button->priv;
1106
  GtkStyleContext *context;
1107
  GtkStockItem item;
Javier Jardón's avatar
Javier Jardón committed
1108
  GtkWidget *child;
1109
  GtkWidget *label;
1110
  GtkWidget *box;
1111
  GtkWidget *align;
1112 1113
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
1114
  gint image_spacing;
1115

1116
  if (!priv->constructed)
1117
    return;
1118

1119
  if (!priv->label_text && !priv->image)
1120
    return;
1121

1122 1123 1124 1125 1126
  context = gtk_widget_get_style_context (GTK_WIDGET (button));

  gtk_style_context_get_style (context,
                               "image-spacing", &image_spacing,
                               NULL);
1127

1128 1129
  if (priv->image && !priv->image_is_stock)
    {
1130 1131
      GtkWidget *parent;

1132
      image = g_object_ref (priv->image);
1133 1134 1135 1136

      parent = gtk_widget_get_parent (image);
      if (parent)
	gtk_container_remove (GTK_CONTAINER (parent), image);
1137
    }
1138

1139 1140
  priv->image = NULL;

Javier Jardón's avatar
Javier Jardón committed
1141 1142 1143
  child = gtk_bin_get_child (GTK_BIN (button));
  if (child)
    gtk_container_remove (GTK_CONTAINER (button), child);
1144

1145 1146 1147
  if (priv->use_stock &&
      priv->label_text &&
      gtk_stock_lookup (priv->label_text, &item))
1148
    {
1149
      if (!image)
1150
	image = g_object_ref (gtk_image_new_from_stock (priv->label_text, GTK_ICON_SIZE_BUTTON));
1151 1152 1153 1154

      label_text = item.label;
    }
  else
1155
    label_text = priv->label_text;
1156

1157 1158 1159
  if (image)
    {
      priv->image = image;
1160
      g_object_set (priv->image,
Matthias Clasen's avatar
Matthias Clasen committed
1161
		    "visible", show_image (button),
1162
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
1163
		    NULL);
1164 1165 1166

      if (priv->image_position == GTK_POS_LEFT ||
	  priv->image_position == GTK_POS_RIGHT)
1167
	box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, image_spacing);
1168
      else
1169
	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, image_spacing);
1170

1171 1172 1173
      gtk_widget_set_valign (image, GTK_ALIGN_BASELINE);
      gtk_widget_set_valign (box, GTK_ALIGN_BASELINE);

1174 1175 1176 1177
      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);
1178

1179 1180
      gtk_widget_set_valign (align, GTK_ALIGN_BASELINE);

1181 1182 1183 1184 1185
      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);
1186 1187 1188

      if (label_text)
	{
1189
          if (priv->use_underline || priv->use_stock)
1190 1191 1192 1193 1194 1195 1196
            {
	      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);
1197

1198 1199
	  gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);

1200 1201 1202 1203 1204
	  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);
1205
	}
1206

1207
      gtk_container_add (GTK_CONTAINER (button), align);
1208
      gtk_container_add (GTK_CONTAINER (align), box);
1209
      gtk_widget_show_all (align);
1210

1211 1212
      g_object_unref (image);

1213
      return;
1214
    }
1215

1216
  if (priv->use_underline || priv->use_stock)
1217
    {
1218
      label = gtk_label_new_with_mnemonic (priv->label_text);
1219
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
1220
    }
1221
  else
1222
    label = gtk_label_new (priv->label_text);
1223

Alexander Larsson's avatar