gtkbutton.c 64.8 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
#include "config.h"
28
#include <string.h>
29
#include "gtkalignment.h"
Elliot Lee's avatar
Elliot Lee committed
30 31 32
#include "gtkbutton.h"
#include "gtklabel.h"
#include "gtkmain.h"
33
#include "gtkmarshalers.h"
34 35
#include "gtkimage.h"
#include "gtkhbox.h"
36
#include "gtkvbox.h"
37 38
#include "gtkstock.h"
#include "gtkiconfactory.h"
39
#include "gtkactivatable.h"
40
#include "gtkprivate.h"
41
#include "gtkintl.h"
42
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
43

44 45
static const GtkBorder default_default_border = { 1, 1, 1, 1 };
static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
46
static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
Elliot Lee's avatar
Elliot Lee committed
47

Matthias Clasen's avatar
Matthias Clasen committed
48
/* Time out before giving up on getting a key release when animating
49 50 51
 * the close button.
 */
#define ACTIVATE_TIMEOUT 250
Elliot Lee's avatar
Elliot Lee committed
52 53 54 55 56 57 58

enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
59
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
60 61
  LAST_SIGNAL
};
62

63
enum {
64 65
  PROP_0,
  PROP_LABEL,
Matthias Clasen's avatar
Matthias Clasen committed
66
  PROP_IMAGE,
67 68
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
69
  PROP_USE_STOCK,
70 71
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
72
  PROP_YALIGN,
73 74 75 76 77
  PROP_IMAGE_POSITION,

  /* activatable properties */
  PROP_ACTIVATABLE_RELATED_ACTION,
  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
78 79 80 81 82 83 84
};

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

struct _GtkButtonPrivate
{
85 86 87
  gfloat          xalign;
  gfloat          yalign;
  GtkWidget      *image;
88 89 90 91
  guint           align_set             : 1;
  guint           image_is_stock        : 1;
  guint           has_grab              : 1;
  guint           use_action_appearance : 1;
92 93
  guint32         grab_time;
  GtkPositionType image_position;
94
  GtkAction      *action;
95
};
96

97
static void gtk_button_destroy        (GtkObject          *object);
98
static void gtk_button_dispose        (GObject            *object);
99 100 101 102 103 104 105 106 107 108
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);
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
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_request (GtkWidget * widget,
				     GtkRequisition * requisition);
static void gtk_button_size_allocate (GtkWidget * widget,
				      GtkAllocation * allocation);
static gint gtk_button_expose (GtkWidget * widget, GdkEventExpose * event);
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);
133 134 135 136 137 138 139
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
140

141 142 143
static GObject*	gtk_button_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
144
static void gtk_button_construct_child (GtkButton             *button);
145 146 147 148 149
static void gtk_button_state_changed   (GtkWidget             *widget,
					GtkStateType           previous_state);
static void gtk_button_grab_notify     (GtkWidget             *widget,
					gboolean               was_grabbed);

150

151 152 153 154 155 156 157 158 159 160
static void gtk_button_activatable_interface_init         (GtkActivatableIface  *iface);
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);
161

162
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
163

164 165 166
G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
						gtk_button_activatable_interface_init))
Elliot Lee's avatar
Elliot Lee committed
167 168 169 170

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
171
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
172 173 174 175
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
176
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
177 178 179
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
180
  
181 182
  gobject_class->constructor  = gtk_button_constructor;
  gobject_class->dispose      = gtk_button_dispose;
Manish Singh's avatar
Manish Singh committed
183 184
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
185

186 187
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
188
  widget_class->screen_changed = gtk_button_screen_changed;
189
  widget_class->realize = gtk_button_realize;
190
  widget_class->unrealize = gtk_button_unrealize;
191 192
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
193
  widget_class->style_set = gtk_button_style_set;
194 195 196 197 198
  widget_class->size_request = gtk_button_size_request;
  widget_class->size_allocate = gtk_button_size_allocate;
  widget_class->expose_event = gtk_button_expose;
  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
199
  widget_class->grab_broken_event = gtk_button_grab_broken;
200
  widget_class->key_release_event = gtk_button_key_release;
201 202
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
203 204
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
205 206

  container_class->child_type = gtk_button_child_type;
207
  container_class->add = gtk_button_add;
208 209 210

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
211
  klass->clicked = NULL;
212 213
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
214
  klass->activate = gtk_real_button_activate;
215

Manish Singh's avatar
Manish Singh committed
216
  g_object_class_install_property (gobject_class,
217 218
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
219 220
                                                        P_("Label"),
                                                        P_("Text of the label widget inside the button, if the button contains a label widget"),
221
                                                        NULL,
222
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
223
  
Manish Singh's avatar
Manish Singh committed
224
  g_object_class_install_property (gobject_class,
225
                                   PROP_USE_UNDERLINE,
Matthias Clasen's avatar
x  
Matthias Clasen committed
226
                                   g_param_spec_boolean ("use-underline",
227 228
							 P_("Use underline"),
							 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
229
                                                        FALSE,
230
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
231
  
Manish Singh's avatar
Manish Singh committed
232
  g_object_class_install_property (gobject_class,
233
                                   PROP_USE_STOCK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
234
                                   g_param_spec_boolean ("use-stock",
235 236
							 P_("Use stock"),
							 P_("If set, the label is used to pick a stock item instead of being displayed"),
237
                                                        FALSE,
238
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
239
  
Soeren Sandmann's avatar
Soeren Sandmann committed
240 241
  g_object_class_install_property (gobject_class,
                                   PROP_FOCUS_ON_CLICK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
242
                                   g_param_spec_boolean ("focus-on-click",
243 244
							 P_("Focus on click"),
							 P_("Whether the button grabs focus when it is clicked with the mouse"),
Soeren Sandmann's avatar
Soeren Sandmann committed
245
							 TRUE,
246
							 GTK_PARAM_READWRITE));
Soeren Sandmann's avatar
Soeren Sandmann committed
247
  
Manish Singh's avatar
Manish Singh committed
248
  g_object_class_install_property (gobject_class,
249 250
                                   PROP_RELIEF,
                                   g_param_spec_enum ("relief",
251 252
                                                      P_("Border relief"),
                                                      P_("The border relief style"),
253 254
                                                      GTK_TYPE_RELIEF_STYLE,
                                                      GTK_RELIEF_NORMAL,
255
                                                      GTK_PARAM_READWRITE));
256 257 258 259 260 261 262 263 264 265 266 267 268
  
  /**
   * 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.
   * 
   * Since: 2.4
   */
  g_object_class_install_property (gobject_class,
                                   PROP_XALIGN,
                                   g_param_spec_float("xalign",
269 270
                                                      P_("Horizontal alignment for child"),
                                                      P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"),
271 272 273
                                                      0.0,
                                                      1.0,
                                                      0.5,
274
                                                      GTK_PARAM_READWRITE));
275 276 277 278 279 280 281 282 283 284 285 286 287

  /**
   * 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.
   * 
   * Since: 2.4
   */
  g_object_class_install_property (gobject_class,
                                   PROP_YALIGN,
                                   g_param_spec_float("yalign",
288 289
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
290 291 292
                                                      0.0,
                                                      1.0,
                                                      0.5,
293
                                                      GTK_PARAM_READWRITE));
Elliot Lee's avatar
Elliot Lee committed
294

Matthias Clasen's avatar
Matthias Clasen committed
295 296 297 298 299 300 301 302 303 304 305 306 307
  /**
   * GtkButton::image:
   * 
   * The child widget to appear next to the button text.
   * 
   * 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,
308
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
309

310 311 312
  /**
   * GtkButton:image-position:
   *
313
   * The position of the image relative to the text inside the button.
314 315 316 317 318 319
   * 
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE_POSITION,
                                   g_param_spec_enum ("image-position",
320
                                            P_("Image position"),
321 322 323 324 325
                                                      P_("The position of the image relative to the text"),
                                                      GTK_TYPE_POSITION_TYPE,
                                                      GTK_POS_LEFT,
                                                      GTK_PARAM_READWRITE));

326 327 328
  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");

329 330 331 332 333 334
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
   * 
335
   * Deprecated: 2.8: Use the #GtkWidget::button-press-event signal.
336
   */ 
Elliot Lee's avatar
Elliot Lee committed
337
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
338
    g_signal_new (I_("pressed"),
Manish Singh's avatar
Manish Singh committed
339 340 341 342 343 344
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, pressed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
345 346 347 348 349 350 351

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

  /**
   * 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
369
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
370
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
371 372 373 374 375 376
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, clicked),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
377 378 379 380 381 382 383

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
   * 
384
   * Deprecated: 2.8: Use the #GtkWidget::enter-notify-event signal.
385
   */ 
Elliot Lee's avatar
Elliot Lee committed
386
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
387
    g_signal_new (I_("enter"),
Manish Singh's avatar
Manish Singh committed
388 389 390 391 392 393
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkButtonClass, enter),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
394 395 396 397 398 399 400

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

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
414
   * @widget: the object which received the signal.
415
   *
416
   * The ::activate signal on GtkButton is an action signal and
417 418
   * emitting it causes the button to animate press then release. 
   * Applications should never connect to this signal, but use the
419
   * #GtkButton::clicked signal.
420
   */
421
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
422
    g_signal_new (I_("activate"),
Manish Singh's avatar
Manish Singh committed
423 424 425 426 427 428
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkButtonClass, activate),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
429
  widget_class->activate_signal = button_signals[ACTIVATE];
430 431

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
432
					   g_param_spec_boxed ("default-border",
433 434
							       P_("Default Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons"),
435
							       GTK_TYPE_BORDER,
436
							       GTK_PARAM_READABLE));
437 438

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
439
					   g_param_spec_boxed ("default-outside-border",
440 441
							       P_("Default Outside Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
442
							       GTK_TYPE_BORDER,
443
							       GTK_PARAM_READABLE));
444
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
445
					   g_param_spec_int ("child-displacement-x",
446 447
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
448 449 450
							     G_MININT,
							     G_MAXINT,
							     0,
451
							     GTK_PARAM_READABLE));
452
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
453
					   g_param_spec_int ("child-displacement-y",
454 455
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
456 457 458
							     G_MININT,
							     G_MAXINT,
							     0,
459
							     GTK_PARAM_READABLE));
460

461 462 463
  /**
   * GtkButton:displace-focus:
   *
464 465
   * Whether the child_displacement_x/child_displacement_y properties 
   * should also affect the focus rectangle.
466 467 468 469 470 471 472
   *
   * 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
473 474
								 FALSE,
								 GTK_PARAM_READABLE));
475

476
  /**
477
   * GtkButton:inner-border:
478
   *
479
   * Sets the border between the button edges and child.
480 481 482 483
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
484 485 486 487 488
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
489

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
  /**
   * GtkButton::image-spacing:
   * 
   * Spacing in pixels between the image and label.
   * 
   * 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));

Johan Dahlin's avatar
Johan Dahlin committed
506
  /**
507 508 509 510
   * GtkSettings::gtk-button-images:
   *
   * Whether images should be shown on buttons
   *
Johan Dahlin's avatar
Johan Dahlin committed
511 512
   * Since: 2.4
   */
Matthias Clasen's avatar
Matthias Clasen committed
513 514
  gtk_settings_install_property (g_param_spec_boolean ("gtk-button-images",
						       P_("Show button images"),
515
						       P_("Whether images should be shown on buttons"),
Matthias Clasen's avatar
Matthias Clasen committed
516
						       TRUE,
517
						       GTK_PARAM_READWRITE));
518 519

  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
Elliot Lee's avatar
Elliot Lee committed
520 521 522 523 524
}

static void
gtk_button_init (GtkButton *button)
{
525 526
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

527
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
528
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
529

530 531 532
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
533 534
  button->in_button = FALSE;
  button->button_down = FALSE;
535
  button->relief = GTK_RELIEF_NORMAL;
536 537
  button->use_stock = FALSE;
  button->use_underline = FALSE;
538
  button->depressed = FALSE;
539
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
540
  button->focus_on_click = TRUE;
541 542 543

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
544
  priv->align_set = 0;
545
  priv->image_is_stock = TRUE;
546
  priv->image_position = GTK_POS_LEFT;
547
  priv->use_action_appearance = TRUE;
Elliot Lee's avatar
Elliot Lee committed
548 549
}

550 551 552 553 554 555 556 557 558 559
static void
gtk_button_destroy (GtkObject *object)
{
  GtkButton *button = GTK_BUTTON (object);
  
  if (button->label_text)
    {
      g_free (button->label_text);
      button->label_text = NULL;
    }
560 561

  GTK_OBJECT_CLASS (gtk_button_parent_class)->destroy (object);
562 563
}

564 565 566 567 568 569 570 571
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;

572 573 574
  object = G_OBJECT_CLASS (gtk_button_parent_class)->constructor (type,
                                                                  n_construct_properties,
                                                                  construct_params);
575 576 577 578 579 580 581 582 583 584 585

  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
586
static GType
587 588
gtk_button_child_type  (GtkContainer     *container)
{
589
  if (!GTK_BIN (container)->child)
590 591
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
592
    return G_TYPE_NONE;
593 594
}

595
static void
Matthias Clasen's avatar
Matthias Clasen committed
596 597
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
598
{
Matthias Clasen's avatar
Matthias Clasen committed
599 600
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

601 602 603
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
604 605 606
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
607 608 609 610
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
611 612 613 614

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
615 616 617 618 619 620 621
    }
}

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

Matthias Clasen's avatar
Matthias Clasen committed
624
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
625 626
}

627 628 629 630 631 632 633 634 635 636 637 638 639 640
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
641
static void
642 643 644 645
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
646
{
647 648
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
649

650
  switch (prop_id)
651
    {
652
    case PROP_LABEL:
653
      gtk_button_set_label (button, g_value_get_string (value));
654
      break;
Matthias Clasen's avatar
Matthias Clasen committed
655 656 657
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
658 659
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
660
      break;
661 662 663 664 665 666
    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
667 668 669
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
670 671 672 673 674 675
    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;
676 677 678
    case PROP_IMAGE_POSITION:
      gtk_button_set_image_position (button, g_value_get_enum (value));
      break;
679 680 681 682 683 684
    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;
685
    default:
686
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
687 688 689 690 691
      break;
    }
}

static void
692 693 694 695
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
696
{
697 698
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
699

700
  switch (prop_id)
701
    {
702
    case PROP_LABEL:
703
      g_value_set_string (value, button->label_text);
704
      break;
Matthias Clasen's avatar
Matthias Clasen committed
705 706 707
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
708
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
709
      g_value_set_enum (value, gtk_button_get_relief (button));
710
      break;
711 712 713 714 715 716
    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
717 718 719
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
720 721 722 723 724 725
    case PROP_XALIGN:
      g_value_set_float (value, priv->xalign);
      break;
    case PROP_YALIGN:
      g_value_set_float (value, priv->yalign);
      break;
726 727 728
    case PROP_IMAGE_POSITION:
      g_value_set_enum (value, priv->image_position);
      break;
729 730 731 732 733 734
    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
735
    default:
736
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
737
      break;
Elliot Lee's avatar
Elliot Lee committed
738 739 740
    }
}

741 742 743
static void 
gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
{
744 745
  iface->update = gtk_button_update;
  iface->sync_action_properties = gtk_button_sync_action_properties;
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
}

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)
{
  GtkWidget *image;

  if (gtk_button_get_use_stock (button))
    return;

  image = gtk_button_get_image (button);

  /* Dont touch custom child... */
  if (GTK_IS_IMAGE (image) ||
      GTK_BIN (button)->child == NULL || 
      GTK_IS_LABEL (GTK_BIN (button)->child))
    {
      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 
811 812 813
gtk_button_update (GtkActivatable *activatable,
		   GtkAction      *action,
	           const gchar    *property_name)
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
{
  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);
}

840 841 842
static void
gtk_button_sync_action_properties (GtkActivatable *activatable,
			           GtkAction      *action)
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
{
  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
866 867
gtk_button_set_related_action (GtkButton *button,
			       GtkAction *action)
868 869 870 871 872 873
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

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

874 875 876 877 878 879
  /* 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)
880
    g_signal_connect_after (button, "clicked",
881 882
                            G_CALLBACK (gtk_real_button_clicked), NULL);

883 884 885 886 887 888
  gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);

  priv->action = action;
}

static void
889 890
gtk_button_set_use_action_appearance (GtkButton *button,
				      gboolean   use_appearance)
891 892 893 894 895 896
{
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

  if (priv->use_action_appearance != use_appearance)
    {
      priv->use_action_appearance = use_appearance;
897 898

      gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (button), priv->action);
899 900 901
    }
}

Elliot Lee's avatar
Elliot Lee committed
902
GtkWidget*
903
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
904
{
Manish Singh's avatar
Manish Singh committed
905
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
906 907
}

Matthias Clasen's avatar
Matthias Clasen committed
908 909 910 911
static gboolean
show_image (GtkButton *button)
{
  gboolean show;
912 913 914 915
  
  if (button->label_text)
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
916

917 918 919 920 921
      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
922 923 924 925

  return show;
}

926 927
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
928
{
Matthias Clasen's avatar
Matthias Clasen committed
929
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
930 931
  GtkStockItem item;
  GtkWidget *label;
932
  GtkWidget *box;
933
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
934 935
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
936
  gint image_spacing;
937
  
938 939
  if (!button->constructed)
    return;
940 941
 
  if (!button->label_text && !priv->image)
942
    return;
Matthias Clasen's avatar
Matthias Clasen committed
943
  
944 945 946 947
  gtk_widget_style_get (GTK_WIDGET (button), 
			"image-spacing", &image_spacing, 
			NULL);

948 949 950 951 952
  if (priv->image && !priv->image_is_stock)
    {
      image = g_object_ref (priv->image);
      if (image->parent)
	gtk_container_remove (GTK_CONTAINER (image->parent), image);
953
    }
954
  
955 956
  priv->image = NULL;

957 958 959
  if (GTK_BIN (button)->child)
    gtk_container_remove (GTK_CONTAINER (button),
			  GTK_BIN (button)->child);
960

961
  if (button->use_stock &&
962
      button->label_text &&
963
      gtk_stock_lookup (button->label_text, &item))
964
    {
Matthias Clasen's avatar
Matthias Clasen committed
965 966 967 968 969 970 971
      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;
972

Matthias Clasen's avatar
Matthias Clasen committed
973 974 975
  if (image)
    {
      priv->image = image;
Matthias Clasen's avatar
Matthias Clasen committed
976 977
      g_object_set (priv->image, 
		    "visible", show_image (button),
978
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
979
		    NULL);
980 981 982

      if (priv->image_position == GTK_POS_LEFT ||
	  priv->image_position == GTK_POS_RIGHT)
983
	box = gtk_hbox_new (FALSE, image_spacing);
984
      else
985
	box = gtk_vbox_new (FALSE, image_spacing);
986

Matthias Clasen's avatar
Matthias Clasen committed
987 988 989 990
      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);
991 992 993 994 995 996

      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);
997 998 999

      if (label_text)
	{
1000 1001 1002 1003 1004 1005 1006 1007
          if (button->use_underline)
            {
	      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);
1008

1009 1010 1011 1012 1013
	  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);
1014
	}
1015
      
1016
      gtk_container_add (GTK_CONTAINER (button), align);
1017
      gtk_container_add (GTK_CONTAINER (align), box);
1018
      gtk_widget_show_all (align);
1019

Matthias Clasen's avatar
Matthias Clasen committed
1020 1021
      g_object_unref (image);

1022
      return;
1023
    }
Matthias Clasen's avatar
Matthias Clasen committed
1024
  
1025
  if (button->use_underline)
1026
    {
1027 1028
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
1029
    }
1030 1031
  else
    label = gtk_label_new (button->label_text);
1032
  
Matthias Clasen's avatar
Matthias Clasen committed
1033 1034
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
1035
  
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
  gtk_widget_show (label);
  gtk_container_add (GTK_CONTAINER (button), label);
}


GtkWidget*
gtk_button_new_with_label (const gchar *label)
{
  return g_object_new (GTK_TYPE_BUTTON, "label", label, NULL);
}

/**
 * gtk_button_new_from_stock:
 * @stock_id: the name of the stock item 
 *
 * Creates a new #GtkButton containing the image and text from a stock item.
 * Some stock ids have preprocessor macros like #GTK_STOCK_OK and
 * #GTK_STOCK_APPLY.
1054 1055 1056 1057 1058
 *
 * If @stock_id is unknown, then it will be treated as a mnemonic
 * label (as for gtk_button_new_with_mnemonic()).
 *
 * Returns: a new #GtkButton
1059 1060
 **/
GtkWidget*
1061
gtk_button_new_from_stock (const gchar *stock_id)
1062
{
1063 1064
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
1065 1066
                       "use-stock", TRUE,
                       "use-underline", TRUE,
1067
                       NULL);