gtkbutton.c 52.7 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * 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 36 37
#include "gtkimage.h"
#include "gtkhbox.h"
#include "gtkstock.h"
#include "gtkiconfactory.h"
38
#include "gtkprivate.h"
39
#include "gtkintl.h"
40
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
41

42 43
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
44

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

enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
56
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
57 58
  LAST_SIGNAL
};
59

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

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

struct _GtkButtonPrivate
{
Matthias Clasen's avatar
Matthias Clasen committed
77 78 79
  gfloat       xalign;
  gfloat       yalign;
  GtkWidget   *image;
Matthias Clasen's avatar
Matthias Clasen committed
80
  guint        align_set : 1;
Matthias Clasen's avatar
Matthias Clasen committed
81
  guint        image_is_stock : 1;
82 83
  guint        has_grab : 1;
  guint32      grab_time;
84
};
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
static void gtk_button_class_init     (GtkButtonClass     *klass);
static void gtk_button_init           (GtkButton          *button);
static void gtk_button_destroy        (GtkObject          *object);
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);
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_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_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
130

131 132 133
static GObject*	gtk_button_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
134
static void gtk_button_construct_child (GtkButton             *button);
135 136 137 138 139
static void gtk_button_state_changed   (GtkWidget             *widget,
					GtkStateType           previous_state);
static void gtk_button_grab_notify     (GtkWidget             *widget,
					gboolean               was_grabbed);

140 141


142
static GtkBinClass *parent_class = NULL;
143
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
144 145


Manish Singh's avatar
Manish Singh committed
146
GType
147
gtk_button_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
148
{
Manish Singh's avatar
Manish Singh committed
149
  static GType button_type = 0;
Elliot Lee's avatar
Elliot Lee committed
150 151 152

  if (!button_type)
    {
153
      static const GTypeInfo button_info =
Elliot Lee's avatar
Elliot Lee committed
154 155
      {
	sizeof (GtkButtonClass),
156 157 158 159 160 161
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_button_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkButton),
162
	0,		/* n_preallocs */
163
	(GInstanceInitFunc) gtk_button_init,
Elliot Lee's avatar
Elliot Lee committed
164 165
      };

Matthias Clasen's avatar
Matthias Clasen committed
166
      button_type = g_type_register_static (GTK_TYPE_BIN, I_("GtkButton"),
Manish Singh's avatar
Manish Singh committed
167
					    &button_info, 0);
Elliot Lee's avatar
Elliot Lee committed
168 169 170 171 172 173 174 175
    }

  return button_type;
}

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
176
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
177 178 179 180
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
181
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
182 183 184
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
185 186
  
  parent_class = g_type_class_peek_parent (klass);
187

Manish Singh's avatar
Manish Singh committed
188 189 190
  gobject_class->constructor = gtk_button_constructor;
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
191

192 193
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
194
  widget_class->screen_changed = gtk_button_screen_changed;
195
  widget_class->realize = gtk_button_realize;
196
  widget_class->unrealize = gtk_button_unrealize;
197 198
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
199 200 201 202 203
  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
204
  widget_class->grab_broken_event = gtk_button_grab_broken;
205
  widget_class->key_release_event = gtk_button_key_release;
206 207
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
208 209
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
210 211

  container_class->child_type = gtk_button_child_type;
212
  container_class->add = gtk_button_add;
213 214 215 216

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
217 218
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
219
  klass->activate = gtk_real_button_activate;
220

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

  /**
   * 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",
293 294
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
295 296 297
                                                      0.0,
                                                      1.0,
                                                      0.5,
298
                                                      GTK_PARAM_READWRITE));
Elliot Lee's avatar
Elliot Lee committed
299

Matthias Clasen's avatar
Matthias Clasen committed
300 301 302 303 304 305 306 307 308 309 310 311 312
  /**
   * 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,
313
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
314

315 316 317 318 319 320 321 322
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
   * 
   * @Deprecated: Use the GtkWidget::button-press-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
323
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
324
    g_signal_new (I_("pressed"),
Manish Singh's avatar
Manish Singh committed
325 326 327 328 329 330
		  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);
331 332 333 334 335 336 337 338 339

  /**
   * GtkButton::released:
   * @button: the object that received the signal
   *
   * Emitted when the button is released.
   * 
   * @Deprecated: Use the GtkWidget::button-release-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
340
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
341
    g_signal_new (I_("released"),
Manish Singh's avatar
Manish Singh committed
342 343 344 345 346 347
		  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);
348 349 350 351 352 353 354

  /**
   * 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
355
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
356
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
357 358 359 360 361 362
		  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);
363 364 365 366 367 368 369 370 371

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
   * 
   * @Deprecated: Use the GtkWidget::enter-notify-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
372
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
373
    g_signal_new (I_("enter"),
Manish Singh's avatar
Manish Singh committed
374 375 376 377 378 379
		  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);
380 381 382 383 384 385 386 387 388

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
   * 
   * @Deprecated: Use the GtkWidget::leave-notify-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
389
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
390
    g_signal_new (I_("leave"),
Manish Singh's avatar
Manish Singh committed
391 392 393 394 395 396
		  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);
397 398 399

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
400
   * @widget: the object which received the signal.
401 402 403 404 405 406
   *
   * The "activate" signal on GtkButton is an action signal and
   * emitting it causes the button to animate press then release. 
   * Applications should never connect to this signal, but use the
   * "clicked" signal.
   */
407
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
408
    g_signal_new (I_("activate"),
Manish Singh's avatar
Manish Singh committed
409 410 411 412 413 414
		  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);
415
  widget_class->activate_signal = button_signals[ACTIVATE];
416 417

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
418
					   g_param_spec_boxed ("default-border",
419 420
							       P_("Default Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons"),
421
							       GTK_TYPE_BORDER,
422
							       GTK_PARAM_READABLE));
423 424

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
425
					   g_param_spec_boxed ("default-outside-border",
426 427
							       P_("Default Outside Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
428
							       GTK_TYPE_BORDER,
429
							       GTK_PARAM_READABLE));
430
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
431
					   g_param_spec_int ("child-displacement-x",
432 433
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
434 435 436
							     G_MININT,
							     G_MAXINT,
							     0,
437
							     GTK_PARAM_READABLE));
438
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
439
					   g_param_spec_int ("child-displacement-y",
440 441
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
442 443 444
							     G_MININT,
							     G_MAXINT,
							     0,
445
							     GTK_PARAM_READABLE));
446

447 448 449 450 451 452 453 454 455 456 457 458 459
  /**
   * GtkButton:displace-focus:
   *
   * Whether the child_displacement_x/child_displacement_y properties should also 
   * affect the focus rectangle.
   *
   * 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"),
						       FALSE,
460
						       GTK_PARAM_READABLE));
461

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
  /**
   * GtkButton:child-spacing:
   *
   * Sets the spacing between the button edges and child.
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("child-spacing",
							     _("Child spacing"),
							     _("Spacing between button edges and child."),
							     0,
							     G_MAXINT,
                                                             1,
							     GTK_PARAM_READABLE));

Matthias Clasen's avatar
Matthias Clasen committed
478 479 480 481
  gtk_settings_install_property (g_param_spec_boolean ("gtk-button-images",
						       P_("Show button images"),
						       P_("Whether stock icons should be shown in buttons"),
						       TRUE,
482
						       GTK_PARAM_READWRITE));
483 484

  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
Elliot Lee's avatar
Elliot Lee committed
485 486 487 488 489
}

static void
gtk_button_init (GtkButton *button)
{
490 491
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

492
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
493
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
494

495 496 497
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
498 499
  button->in_button = FALSE;
  button->button_down = FALSE;
500
  button->relief = GTK_RELIEF_NORMAL;
501 502
  button->use_stock = FALSE;
  button->use_underline = FALSE;
503
  button->depressed = FALSE;
504
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
505
  button->focus_on_click = TRUE;
506 507 508

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
509
  priv->align_set = 0;
510
  priv->image_is_stock = TRUE;
Elliot Lee's avatar
Elliot Lee committed
511 512
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526
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;
    }
  
  (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;

  object = (* G_OBJECT_CLASS (parent_class)->constructor) (type,
							   n_construct_properties,
							   construct_params);

  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
549
static GType
550 551
gtk_button_child_type  (GtkContainer     *container)
{
552
  if (!GTK_BIN (container)->child)
553 554
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
555
    return G_TYPE_NONE;
556 557
}

558
static void
Matthias Clasen's avatar
Matthias Clasen committed
559 560
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
561
{
Matthias Clasen's avatar
Matthias Clasen committed
562 563
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

564 565 566
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
567 568 569
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
570 571 572 573
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
574 575 576 577

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
578 579 580 581 582 583 584
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
585
  maybe_set_alignment (GTK_BUTTON (container), widget);
586 587 588 589

  GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
}

Elliot Lee's avatar
Elliot Lee committed
590
static void
591 592 593 594
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
595
{
596 597
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
598

599
  switch (prop_id)
600
    {
601
    case PROP_LABEL:
602
      gtk_button_set_label (button, g_value_get_string (value));
603
      break;
Matthias Clasen's avatar
Matthias Clasen committed
604 605 606
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
607 608
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
609
      break;
610 611 612 613 614 615
    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
616 617 618
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
619 620 621 622 623 624
    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;
625
    default:
626
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
627 628 629 630 631
      break;
    }
}

static void
632 633 634 635
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
636
{
637 638
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
639

640
  switch (prop_id)
641
    {
642
    case PROP_LABEL:
643
      g_value_set_string (value, button->label_text);
644
      break;
Matthias Clasen's avatar
Matthias Clasen committed
645 646 647
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
648
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
649
      g_value_set_enum (value, gtk_button_get_relief (button));
650
      break;
651 652 653 654 655 656
    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
657 658 659
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
660 661 662 663 664 665
    case PROP_XALIGN:
      g_value_set_float (value, priv->xalign);
      break;
    case PROP_YALIGN:
      g_value_set_float (value, priv->yalign);
      break;
Tim Janik's avatar
Tim Janik committed
666
    default:
667
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
668
      break;
Elliot Lee's avatar
Elliot Lee committed
669 670 671 672
    }
}

GtkWidget*
673
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
674
{
Manish Singh's avatar
Manish Singh committed
675
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
676 677
}

Matthias Clasen's avatar
Matthias Clasen committed
678 679 680 681 682 683 684 685 686 687 688
static gboolean
show_image (GtkButton *button)
{
  GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (button));  
  gboolean show;

  g_object_get (settings, "gtk-button-images", &show, NULL);

  return show;
}

689 690
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
691
{
Matthias Clasen's avatar
Matthias Clasen committed
692
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
693 694 695
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
696
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
697 698
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
699
  
700 701 702 703 704
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
705

706
  if (GTK_BIN (button)->child)
707
    {
Matthias Clasen's avatar
Matthias Clasen committed
708
      if (priv->image && !priv->image_is_stock)
709 710 711 712 713
	{
	  image = g_object_ref (priv->image);
	  if (image->parent)
	    gtk_container_remove (GTK_CONTAINER (image->parent), image);
	}
Matthias Clasen's avatar
Matthias Clasen committed
714

715 716
      gtk_container_remove (GTK_CONTAINER (button),
  			    GTK_BIN (button)->child);
Matthias Clasen's avatar
Matthias Clasen committed
717
  
718 719
      priv->image = NULL;
    }
720 721 722
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
723
    {
Matthias Clasen's avatar
Matthias Clasen committed
724 725 726 727 728 729 730
      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;
731

Matthias Clasen's avatar
Matthias Clasen committed
732 733 734
  if (image)
    {
      label = gtk_label_new_with_mnemonic (label_text);
735 736
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
737 738
      priv->image = image;

Matthias Clasen's avatar
Matthias Clasen committed
739 740
      g_object_set (priv->image, 
		    "visible", show_image (button),
741
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
742
		    NULL);
743
      hbox = gtk_hbox_new (FALSE, 2);
744

Matthias Clasen's avatar
Matthias Clasen committed
745 746 747 748 749
      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);
	
Matthias Clasen's avatar
Matthias Clasen committed
750
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
751
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
752
      
753 754 755
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
756

Matthias Clasen's avatar
Matthias Clasen committed
757 758
      g_object_unref (image);

759
      return;
760
    }
Matthias Clasen's avatar
Matthias Clasen committed
761 762
  
 if (button->use_underline)
763
    {
764 765
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
766
    }
767 768
  else
    label = gtk_label_new (button->label_text);
769
  
Matthias Clasen's avatar
Matthias Clasen committed
770 771
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

  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.
791 792 793 794 795
 *
 * 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
796 797
 **/
GtkWidget*
798
gtk_button_new_from_stock (const gchar *stock_id)
799
{
800 801
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
802 803
                       "use-stock", TRUE,
                       "use-underline", TRUE,
804
                       NULL);
805 806
}

807 808 809 810 811 812 813
/**
 * gtk_button_new_with_mnemonic:
 * @label: The text of the button, with an underscore in front of the
 *         mnemonic character
 * @returns: a new #GtkButton
 *
 * Creates a new #GtkButton containing a label.
814 815 816 817
 * If characters in @label are preceded by an underscore, they are underlined.
 * If you need a literal underscore character in a label, use '__' (two 
 * underscores). The first underlined character represents a keyboard 
 * accelerator called a mnemonic.
818 819
 * Pressing Alt and that key activates the button.
 **/
820
GtkWidget*
821
gtk_button_new_with_mnemonic (const gchar *label)
822
{
823
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use-underline", TRUE,  NULL);
824 825
}

Elliot Lee's avatar
Elliot Lee committed
826 827 828
void
gtk_button_pressed (GtkButton *button)
{
829 830
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
831
  
Manish Singh's avatar
Manish Singh committed
832
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
833 834 835 836 837
}

void
gtk_button_released (GtkButton *button)
{
838 839
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
840
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
841 842 843 844 845
}

void
gtk_button_clicked (GtkButton *button)
{
846 847
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
848
  g_signal_emit (button, button_signals[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
849 850 851 852 853
}

void
gtk_button_enter (GtkButton *button)
{
854 855
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
856
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
857 858 859 860 861
}

void
gtk_button_leave (GtkButton *button)
{
862 863
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
864
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
865 866
}

867 868 869 870 871 872
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

873 874 875 876 877 878
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
879 880 881
}

GtkReliefStyle
882
gtk_button_get_relief (GtkButton *button)
883 884 885 886 887 888
{
  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);

  return button->relief;
}

Elliot Lee's avatar
Elliot Lee committed
889 890 891 892 893 894
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
895
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
896 897 898 899

  button = GTK_BUTTON (widget);
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

Owen Taylor's avatar
Owen Taylor committed
900 901
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
902
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
903 904 905 906
  attributes.x = widget->allocation.x + border_width;
  attributes.y = widget->allocation.y + border_width;
  attributes.width = widget->allocation.width - border_width * 2;
  attributes.height = widget->allocation.height - border_width * 2;
907
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
908
  attributes.event_mask = gtk_widget_get_events (widget);
909
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
910 911 912 913
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

914
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
915

916
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
917
  g_object_ref (widget->window);
918 919 920 921
  
  button->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
					 &attributes, attributes_mask);
  gdk_window_set_user_data (button->event_window, button);
Elliot Lee's avatar
Elliot Lee committed
922 923 924 925

  widget->style = gtk_style_attach (widget->style, widget->window);
}

926 927 928 929 930 931 932
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
933 934 935 936 937 938 939 940

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
941 942 943
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

944 945 946 947 948
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
949 950
  GTK_WIDGET_CLASS (parent_class)->map (widget);

951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
  if (button->event_window)
    gdk_window_show (button->event_window);
}

static void
gtk_button_unmap (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
    
  if (button->event_window)
    gdk_window_hide (button->event_window);

  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}

Elliot Lee's avatar
Elliot Lee committed
966
static void
967
gtk_button_get_props (GtkButton *button,
968 969
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
970
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
971
{
972
  GtkWidget *widget =  GTK_WIDGET (button);
973 974 975 976
  GtkBorder *tmp_border;

  if (default_border)
    {
977
      gtk_widget_style_get (widget, "default-border", &tmp_border, NULL);
978 979 980 981 982 983 984 985 986

      if (tmp_border)
	{
	  *default_border = *tmp_border;
	  g_free (tmp_border);
	}
      else
	*default_border = default_default_border;
    }
Elliot Lee's avatar
Elliot Lee committed
987

988 989
  if (default_outside_border)
    {
990
      gtk_widget_style_get (widget, "default-outside-border", &tmp_border, NULL);
991 992 993 994 995