gtkbutton.c 53.6 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 };
44
static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
Elliot Lee's avatar
Elliot Lee committed
45

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

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

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

#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
78 79 80
  gfloat       xalign;
  gfloat       yalign;
  GtkWidget   *image;
Matthias Clasen's avatar
Matthias Clasen committed
81
  guint        align_set : 1;
Matthias Clasen's avatar
Matthias Clasen committed
82
  guint        image_is_stock : 1;
83 84
  guint        has_grab : 1;
  guint32      grab_time;
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 130
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
131

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

141 142


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


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

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

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

  return button_type;
}

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

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

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

193 194
  object_class->destroy = gtk_button_destroy;

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

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

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

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

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

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

316 317 318 319 320 321 322 323
  /**
   * 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
324
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
325
    g_signal_new (I_("pressed"),
Manish Singh's avatar
Manish Singh committed
326 327 328 329 330 331
		  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);
332 333 334 335 336 337 338 339 340

  /**
   * 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
341
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
342
    g_signal_new (I_("released"),
Manish Singh's avatar
Manish Singh committed
343 344 345 346 347 348
		  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);
349 350 351 352 353 354 355

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

  /**
   * 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
373
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
374
    g_signal_new (I_("enter"),
Manish Singh's avatar
Manish Singh committed
375 376 377 378 379 380
		  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);
381 382 383 384 385 386 387 388 389

  /**
   * 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
390
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
391
    g_signal_new (I_("leave"),
Manish Singh's avatar
Manish Singh committed
392 393 394 395 396 397
		  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);
398 399 400

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
401
   * @widget: the object which received the signal.
402 403 404 405 406 407
   *
   * 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.
   */
408
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
409
    g_signal_new (I_("activate"),
Manish Singh's avatar
Manish Singh committed
410 411 412 413 414 415
		  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);
416
  widget_class->activate_signal = button_signals[ACTIVATE];
417 418

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

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

448 449 450 451 452 453 454 455 456 457 458 459 460
  /**
   * 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,
461
						       GTK_PARAM_READABLE));
462

463
  /**
464
   * GtkButton:inner-border:
465
   *
466
   * Sets the border between the button edges and child.
467 468 469 470
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
471 472 473 474 475
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
476

Matthias Clasen's avatar
Matthias Clasen committed
477 478 479 480
  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,
481
						       GTK_PARAM_READWRITE));
482 483

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

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

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

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

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

512 513 514 515 516 517 518 519 520 521 522 523 524 525
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);
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
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
548
static GType
549 550
gtk_button_child_type  (GtkContainer     *container)
{
551
  if (!GTK_BIN (container)->child)
552 553
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
554
    return G_TYPE_NONE;
555 556
}

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

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

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

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

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

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

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

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

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

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

Matthias Clasen's avatar
Matthias Clasen committed
677 678 679 680
static gboolean
show_image (GtkButton *button)
{
  gboolean show;
681 682 683 684
  
  if (button->label_text)
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
685

686 687 688 689 690
      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
691 692 693 694

  return show;
}

695 696
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
697
{
Matthias Clasen's avatar
Matthias Clasen committed
698
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
699 700 701
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
702
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
703 704
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
705
  
706 707
  if (!button->constructed)
    return;
708 709
 
  if (!button->label_text && !priv->image)
710
    return;
Matthias Clasen's avatar
Matthias Clasen committed
711
  
712 713 714 715 716 717
  if (priv->image && !priv->image_is_stock)
    {
      image = g_object_ref (priv->image);
      if (image->parent)
	gtk_container_remove (GTK_CONTAINER (image->parent), image);
      
718 719
      priv->image = NULL;
    }
720
  
721 722 723 724
  if (GTK_BIN (button)->child)
    gtk_container_remove (GTK_CONTAINER (button),
			  GTK_BIN (button)->child);
  
725
  if (button->use_stock &&
726
      button->label_text &&
727
      gtk_stock_lookup (button->label_text, &item))
728
    {
Matthias Clasen's avatar
Matthias Clasen committed
729 730 731 732 733 734 735
      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;
736

Matthias Clasen's avatar
Matthias Clasen committed
737 738 739 740
  if (image)
    {
      priv->image = image;

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

Matthias Clasen's avatar
Matthias Clasen committed
747 748 749 750 751
      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
752
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
753 754 755 756 757 758 759 760 761

      if (label_text)
	{
	  label = gtk_label_new_with_mnemonic (label_text);
	  gtk_label_set_mnemonic_widget (GTK_LABEL (label), 
					 GTK_WIDGET (button));

	  gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
	}
762
      
763 764 765
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
766

Matthias Clasen's avatar
Matthias Clasen committed
767 768
      g_object_unref (image);

769
      return;
770
    }
Matthias Clasen's avatar
Matthias Clasen committed
771
  
772
  if (button->use_underline)
773
    {
774 775
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
776
    }
777 778
  else
    label = gtk_label_new (button->label_text);
779
  
Matthias Clasen's avatar
Matthias Clasen committed
780 781
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
782
  
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
  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.
801 802 803 804 805
 *
 * 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
806 807
 **/
GtkWidget*
808
gtk_button_new_from_stock (const gchar *stock_id)
809
{
810 811
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
812 813
                       "use-stock", TRUE,
                       "use-underline", TRUE,
814
                       NULL);
815 816
}

817 818 819 820 821 822 823
/**
 * 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.
824 825 826 827
 * 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.
828 829
 * Pressing Alt and that key activates the button.
 **/
830
GtkWidget*
831
gtk_button_new_with_mnemonic (const gchar *label)
832
{
833
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use-underline", TRUE,  NULL);
834 835
}

Elliot Lee's avatar
Elliot Lee committed
836 837 838
void
gtk_button_pressed (GtkButton *button)
{
839 840
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
841
  
Manish Singh's avatar
Manish Singh committed
842
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
843 844 845 846 847
}

void
gtk_button_released (GtkButton *button)
{
848 849
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
850
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
851 852 853 854 855
}

void
gtk_button_clicked (GtkButton *button)
{
856 857
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
858
  g_signal_emit (button, button_signals[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
859 860 861 862 863
}

void
gtk_button_enter (GtkButton *button)
{
864 865
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
866
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
867 868 869 870 871
}

void
gtk_button_leave (GtkButton *button)
{
872 873
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
874
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
875 876
}

877 878 879 880 881 882
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

883 884 885 886 887 888
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
889 890 891
}

GtkReliefStyle
892
gtk_button_get_relief (GtkButton *button)
893 894 895 896 897 898
{
  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);

  return button->relief;
}

Elliot Lee's avatar
Elliot Lee committed
899 900 901 902 903 904
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
905
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
906 907 908 909

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

Owen Taylor's avatar
Owen Taylor committed
910 911
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
912
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
913 914 915 916
  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;
917
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
918
  attributes.event_mask = gtk_widget_get_events (widget);
919
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
920 921 922 923
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

924
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
925

926
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
927
  g_object_ref (widget->window);
928 929 930 931
  
  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
932 933 934 935

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

936 937 938 939 940 941 942
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
943 944 945 946 947 948 949 950

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
951 952 953
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

954 955 956 957 958
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
959 960
  GTK_WIDGET_CLASS (parent_class)->map (widget);

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
  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
976
static void
977
gtk_button_get_props (GtkButton *button,
978 979
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
980
                      GtkBorder *inner_border,