gtkbutton.c 44.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 "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
39 40

#define CHILD_SPACING     1
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,
63 64
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
65
  PROP_USE_STOCK,
66 67 68 69 70 71 72 73 74 75
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
  PROP_YALIGN
};

#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
76 77 78 79 80
  gfloat       xalign;
  gfloat       yalign;
  GtkSettings *settings;
  guint        show_image_connection;
  GtkWidget   *image;
81
};
82

Elliot Lee's avatar
Elliot Lee committed
83 84
static void gtk_button_class_init     (GtkButtonClass   *klass);
static void gtk_button_init           (GtkButton        *button);
85
static void gtk_button_destroy        (GtkObject        *object);
86 87 88 89 90 91 92 93
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);
Matthias Clasen's avatar
Matthias Clasen committed
94 95
static void gtk_button_screen_changed (GtkWidget        *widget,
				       GdkScreen        *previous_screen);
Elliot Lee's avatar
Elliot Lee committed
96
static void gtk_button_realize        (GtkWidget        *widget);
97
static void gtk_button_unrealize      (GtkWidget        *widget);
98 99
static void gtk_button_map            (GtkWidget        *widget);
static void gtk_button_unmap          (GtkWidget        *widget);
Elliot Lee's avatar
Elliot Lee committed
100 101 102 103 104 105 106 107 108 109
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);
110 111
static gint gtk_button_key_release    (GtkWidget        *widget,
				       GdkEventKey      *event);
Elliot Lee's avatar
Elliot Lee committed
112 113 114 115 116 117
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);
Manish Singh's avatar
Manish Singh committed
118
static void gtk_real_button_activate  (GtkButton         *button);
119
static void gtk_button_update_state   (GtkButton        *button);
120 121
static void gtk_button_add            (GtkContainer   *container,
			               GtkWidget      *widget);
Manish Singh's avatar
Manish Singh committed
122
static GType gtk_button_child_type    (GtkContainer     *container);
123 124
static void gtk_button_finish_activate (GtkButton *button,
					gboolean   do_it);
Elliot Lee's avatar
Elliot Lee committed
125

126 127 128
static GObject*	gtk_button_constructor     (GType                  type,
					    guint                  n_construct_properties,
					    GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
129
static void gtk_button_construct_child (GtkButton             *button);
130 131


132
static GtkBinClass *parent_class = NULL;
133
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
134 135


Manish Singh's avatar
Manish Singh committed
136
GType
137
gtk_button_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
138
{
Manish Singh's avatar
Manish Singh committed
139
  static GType button_type = 0;
Elliot Lee's avatar
Elliot Lee committed
140 141 142

  if (!button_type)
    {
143
      static const GTypeInfo button_info =
Elliot Lee's avatar
Elliot Lee committed
144 145
      {
	sizeof (GtkButtonClass),
146 147 148 149 150 151 152 153
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_button_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkButton),
	16,		/* n_preallocs */
	(GInstanceInitFunc) gtk_button_init,
Elliot Lee's avatar
Elliot Lee committed
154 155
      };

Manish Singh's avatar
Manish Singh committed
156 157
      button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton",
					    &button_info, 0);
Elliot Lee's avatar
Elliot Lee committed
158 159 160 161 162 163 164 165
    }

  return button_type;
}

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
166
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
167 168 169 170
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
171
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
172 173 174
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
175 176
  
  parent_class = g_type_class_peek_parent (klass);
177

Manish Singh's avatar
Manish Singh committed
178 179 180
  gobject_class->constructor = gtk_button_constructor;
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
181

182 183
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
184
  widget_class->screen_changed = gtk_button_screen_changed;
185
  widget_class->realize = gtk_button_realize;
186
  widget_class->unrealize = gtk_button_unrealize;
187 188
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
189 190 191 192 193
  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;
194
  widget_class->key_release_event = gtk_button_key_release;
195 196 197 198 199 200 201 202
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;

  container_class->child_type = gtk_button_child_type;

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
203 204
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
205
  klass->activate = gtk_real_button_activate;
206

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

  /**
   * 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",
279 280
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
281 282 283 284
                                                      0.0,
                                                      1.0,
                                                      0.5,
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
Elliot Lee's avatar
Elliot Lee committed
285 286

  button_signals[PRESSED] =
Manish Singh's avatar
Manish Singh committed
287 288 289 290 291 292 293
    g_signal_new ("pressed",
		  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);
Elliot Lee's avatar
Elliot Lee committed
294
  button_signals[RELEASED] =
Manish Singh's avatar
Manish Singh committed
295 296 297 298 299 300 301
    g_signal_new ("released",
		  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);
Elliot Lee's avatar
Elliot Lee committed
302
  button_signals[CLICKED] =
Manish Singh's avatar
Manish Singh committed
303 304 305 306 307 308 309
    g_signal_new ("clicked",
		  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);
Elliot Lee's avatar
Elliot Lee committed
310
  button_signals[ENTER] =
Manish Singh's avatar
Manish Singh committed
311 312 313 314 315 316 317
    g_signal_new ("enter",
		  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);
Elliot Lee's avatar
Elliot Lee committed
318
  button_signals[LEAVE] =
Manish Singh's avatar
Manish Singh committed
319 320 321 322 323 324 325
    g_signal_new ("leave",
		  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);
326 327 328 329 330 331 332 333 334

  /**
   * GtkButton::activate:
   *
   * 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.
   */
335
  button_signals[ACTIVATE] =
Manish Singh's avatar
Manish Singh committed
336 337 338 339 340 341 342
    g_signal_new ("activate",
		  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);
343
  widget_class->activate_signal = button_signals[ACTIVATE];
344 345

  gtk_widget_class_install_style_property (widget_class,
346
					   g_param_spec_boxed ("default_border",
347 348
							       P_("Default Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons"),
349 350 351 352 353
							       GTK_TYPE_BORDER,
							       G_PARAM_READABLE));

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_boxed ("default_outside_border",
354 355
							       P_("Default Outside Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
356 357
							       GTK_TYPE_BORDER,
							       G_PARAM_READABLE));
358 359
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("child_displacement_x",
360 361
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
362 363 364 365 366 367
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("child_displacement_y",
368 369
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
370 371 372 373
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
374

Matthias Clasen's avatar
Matthias Clasen committed
375 376 377 378 379 380
  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,
						       G_PARAM_READWRITE));
  
381
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));  
Elliot Lee's avatar
Elliot Lee committed
382 383 384 385 386
}

static void
gtk_button_init (GtkButton *button)
{
387 388
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

389
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
390
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
391

392 393 394
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
395 396
  button->in_button = FALSE;
  button->button_down = FALSE;
397
  button->relief = GTK_RELIEF_NORMAL;
398 399
  button->use_stock = FALSE;
  button->use_underline = FALSE;
400
  button->depressed = FALSE;
401
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
402
  button->focus_on_click = TRUE;
403 404 405

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Elliot Lee's avatar
Elliot Lee committed
406 407
}

408 409 410 411 412 413 414 415 416 417 418 419 420 421
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);
}

422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
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
444
static GType
445 446
gtk_button_child_type  (GtkContainer     *container)
{
447
  if (!GTK_BIN (container)->child)
448 449
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
450
    return G_TYPE_NONE;
451 452
}

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
static void
maybe_set_alignment (GtkWidget *widget,
		     gfloat     xalign,
		     gfloat     yalign)
{
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
      gtk_misc_set_alignment (misc, xalign, yalign);
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
      gtk_alignment_set (alignment, xalign, yalign, alignment->xscale, alignment->yscale);
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
  GtkButton *button = GTK_BUTTON (container);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

  maybe_set_alignment (widget, priv->xalign, priv->yalign);
  GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
}

Elliot Lee's avatar
Elliot Lee committed
481
static void
482 483 484 485
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
486
{
487 488
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
489

490
  switch (prop_id)
491
    {
492
    case PROP_LABEL:
493
      gtk_button_set_label (button, g_value_get_string (value));
494
      break;
495 496
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
497
      break;
498 499 500 501 502 503
    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
504 505 506
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
507 508 509 510 511 512
    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;
513
    default:
514
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515 516 517 518 519
      break;
    }
}

static void
520 521 522 523
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
524
{
525 526
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
527

528
  switch (prop_id)
529
    {
530
    case PROP_LABEL:
531
      g_value_set_string (value, button->label_text);
532
      break;
533
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
534
      g_value_set_enum (value, gtk_button_get_relief (button));
535
      break;
536 537 538 539 540 541
    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
542 543 544
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
545 546 547 548 549 550
    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
551
    default:
552
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
553
      break;
Elliot Lee's avatar
Elliot Lee committed
554 555 556 557
    }
}

GtkWidget*
558
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
559
{
Manish Singh's avatar
Manish Singh committed
560
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
561 562
}

Matthias Clasen's avatar
Matthias Clasen committed
563 564 565 566 567 568 569 570 571 572 573
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;
}

574 575
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
576
{
Matthias Clasen's avatar
Matthias Clasen committed
577
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
578 579 580
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
581
  GtkWidget *align;
582
  
583 584 585 586 587
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
588

589
  if (GTK_BIN (button)->child)
590 591 592 593 594
    {
      gtk_container_remove (GTK_CONTAINER (button),
  			    GTK_BIN (button)->child);
      priv->image = NULL;
    }
595 596 597
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
598
    {
599 600
      label = gtk_label_new_with_mnemonic (item.label);

601 602
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
603 604 605 606 607
      priv->image = gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON);
      g_object_set (priv->image, 
		    "visible", show_image (button),
		    "no_show_all", TRUE,
		    NULL);
608
      hbox = gtk_hbox_new (FALSE, 2);
609

610
      align = gtk_alignment_new (priv->xalign, priv->yalign, 0.0, 0.0);
611
      
Matthias Clasen's avatar
Matthias Clasen committed
612
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
613
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
614
      
615 616 617
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
618 619

      return;
620
    }
621 622

  if (button->use_underline)
623
    {
624 625
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
626
    }
627 628
  else
    label = gtk_label_new (button->label_text);
629
  
630
  gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649

  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.
650 651 652 653 654
 *
 * 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
655 656
 **/
GtkWidget*
657
gtk_button_new_from_stock (const gchar *stock_id)
658
{
659 660 661 662 663
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
                       "use_stock", TRUE,
                       "use_underline", TRUE,
                       NULL);
664 665
}

666 667 668 669 670 671 672
/**
 * 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.
673 674 675 676
 * 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.
677 678
 * Pressing Alt and that key activates the button.
 **/
679
GtkWidget*
680
gtk_button_new_with_mnemonic (const gchar *label)
681
{
682
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE,  NULL);
683 684
}

Elliot Lee's avatar
Elliot Lee committed
685 686 687
void
gtk_button_pressed (GtkButton *button)
{
688 689
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
690
  
Manish Singh's avatar
Manish Singh committed
691
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
692 693 694 695 696
}

void
gtk_button_released (GtkButton *button)
{
697 698
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
699
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
700 701 702 703 704
}

void
gtk_button_clicked (GtkButton *button)
{
705 706
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
707
  g_signal_emit (button, button_signals[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
708 709 710 711 712
}

void
gtk_button_enter (GtkButton *button)
{
713 714
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
715
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
716 717 718 719 720
}

void
gtk_button_leave (GtkButton *button)
{
721 722
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
723
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
724 725
}

726 727 728 729 730 731
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

732 733 734 735 736 737
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
738 739 740
}

GtkReliefStyle
741
gtk_button_get_relief (GtkButton *button)
742 743 744 745 746 747 748
{
  g_return_val_if_fail (button != NULL, GTK_RELIEF_NORMAL);
  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);

  return button->relief;
}

Elliot Lee's avatar
Elliot Lee committed
749 750 751 752 753 754
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
755
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
756 757 758 759

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

Owen Taylor's avatar
Owen Taylor committed
760 761
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
762
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
763 764 765 766
  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;
767
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
768
  attributes.event_mask = gtk_widget_get_events (widget);
769
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
770 771 772 773
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

774
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
775

776
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
777
  g_object_ref (widget->window);
778 779 780 781
  
  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
782 783 784 785

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

786 787 788 789 790 791 792
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
793 794 795 796 797 798 799 800

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
801 802 803
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

804 805 806 807 808 809 810
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
  g_return_if_fail (GTK_IS_BUTTON (widget));

811 812
  GTK_WIDGET_CLASS (parent_class)->map (widget);

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
  if (button->event_window)
    gdk_window_show (button->event_window);
}

static void
gtk_button_unmap (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
    
  g_return_if_fail (GTK_IS_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
830
static void
831
gtk_button_get_props (GtkButton *button,
832 833
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
834
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
835
{
836
  GtkWidget *widget =  GTK_WIDGET (button);
837 838 839 840 841 842 843 844 845 846 847 848 849 850
  GtkBorder *tmp_border;

  if (default_border)
    {
      gtk_widget_style_get (widget, "default_border", &tmp_border, NULL);

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

852 853 854 855 856 857 858 859 860 861 862 863
  if (default_outside_border)
    {
      gtk_widget_style_get (widget, "default_outside_border", &tmp_border, NULL);

      if (tmp_border)
	{
	  *default_outside_border = *tmp_border;
	  g_free (tmp_border);
	}
      else
	*default_outside_border = default_default_outside_border;
    }
Elliot Lee's avatar
Elliot Lee committed
864

865 866 867 868 869 870 871 872 873
  if (interior_focus)
    gtk_widget_style_get (widget, "interior_focus", interior_focus, NULL);
}
	
static void
gtk_button_size_request (GtkWidget      *widget,
			 GtkRequisition *requisition)
{
  GtkButton *button = GTK_BUTTON (widget);
874
  GtkBorder default_border;
875
  gboolean interior_focus;
876 877
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
878

879
  gtk_button_get_props (button, &default_border, NULL, &interior_focus);
880 881 882 883 884
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
Elliot Lee's avatar
Elliot Lee committed
885
  requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
886
			GTK_WIDGET (widget)->style->xthickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
887
  requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
888
			 GTK_WIDGET (widget)->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
889 890 891

  if (GTK_WIDGET_CAN_DEFAULT (widget))
    {
892 893
      requisition->width += default_border.left + default_border.right;
      requisition->height += default_border.top + default_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
894 895
    }

896
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
897
    {
898
      GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
899

900 901 902 903
      gtk_widget_size_request (GTK_BIN (button)->child, &child_requisition);

      requisition->width += child_requisition.width;
      requisition->height += child_requisition.height;
Elliot Lee's avatar
Elliot Lee committed
904
    }
905 906 907
  
  requisition->width += 2 * (focus_width + focus_pad);
  requisition->height += 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
908 909 910 911 912 913
}

static void
gtk_button_size_allocate (GtkWidget     *widget,
			  GtkAllocation *allocation)
{
914
  GtkButton *button = GTK_BUTTON (widget);
Elliot Lee's avatar
Elliot Lee committed
915 916
  GtkAllocation child_allocation;

917 918 919
  gint border_width = GTK_CONTAINER (widget)->border_width;
  gint xthickness = GTK_WIDGET (widget)->style->xthickness;
  gint ythickness = GTK_WIDGET (widget)->style->ythickness;
920
  GtkBorder default_border;
Elliot Lee's avatar
Elliot Lee committed
921

922
  gtk_button_get_props (button, &default_border, NULL, NULL);
923
			    
Elliot Lee's avatar
Elliot Lee committed
924 925 926
  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED (widget))
927
    gdk_window_move_resize (button->event_window,
Elliot Lee's avatar
Elliot Lee committed
928 929 930 931 932
			    widget->allocation.x + border_width,
			    widget->allocation.y + border_width,
			    widget->allocation.width - border_width * 2,
			    widget->allocation.height - border_width * 2);

933
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
934
    {
935 936 937 938 939 940 941
      child_allocation.x = widget->allocation.x + border_width + CHILD_SPACING + xthickness;
      child_allocation.y = widget->allocation.y + border_width + CHILD_SPACING + ythickness;
      
      child_allocation.width = MAX (1, widget->allocation.width - (CHILD_SPACING + xthickness) * 2 -
				    border_width * 2);
      child_allocation.height = MAX (1, widget->allocation.height - (CHILD_SPACING + ythickness) * 2 -
				     border_width * 2);
Elliot Lee's avatar
Elliot Lee committed
942 943 944

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
945 946 947 948
	  child_allocation.x += default_border.left;
	  child_allocation.y += default_border.top;
	  child_allocation.width =  MAX (1, child_allocation.width - default_border.left - default_border.right);
	  child_allocation.height = MAX (1, child_allocation.height - default_border.top - default_border.bottom);
Elliot Lee's avatar
Elliot Lee committed
949 950
	}

951 952 953 954 955 956 957 958 959 960 961 962 963
      if (button->depressed)
	{
	  gint child_displacement_x;
	  gint child_displacement_y;
	  
	  gtk_widget_style_get (widget,
				"child_displacement_x", &child_displacement_x, 
				"child_displacement_y", &child_displacement_y,
				NULL);
	  child_allocation.x += child_displacement_x;
	  child_allocation.y += child_displacement_y;
	}

964
      gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
965 966 967
    }
}

968 969 970 971 972 973 974
void
_gtk_button_paint (GtkButton    *button,
		   GdkRectangle *area,
		   GtkStateType  state_type,
		   GtkShadowType shadow_type,
		   const gchar  *main_detail,
		   const gchar  *default_detail)
Elliot Lee's avatar
Elliot Lee committed
975
{
976
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
977 978
  gint width, height;
  gint x, y;
979
  gint border_width;
980 981
  GtkBorder default_border;
  GtkBorder default_outside_border;
982
  gboolean interior_focus;
983 984
  gint focus_width;
  gint focus_pad;
985
   
986
  if (GTK_WIDGET_DRAWABLE (button))
Elliot Lee's avatar
Elliot Lee committed
987
    {
988
      widget = GTK_WIDGET (button);
989
      border_width = GTK_CONTAINER (widget)->border_width;
990

991
      gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
992 993 994 995
      gtk_widget_style_get (GTK_WIDGET (widget),
			    "focus-line-width", &focus_width,
			    "focus-padding", &focus_pad,
			    NULL); 
996
	
997 998 999 1000
      x = widget->allocation.x + border_width;
      y = widget->allocation.y + border_width;
      width = widget->allocation.width - border_width * 2;
      height = widget->allocation.height - border_width * 2;
1001 1002 1003 1004 1005 1006 1007 1008 1009

      if (GTK_WIDGET_HAS_DEFAULT (widget) &&
	  GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL)
	{
	  gtk_paint_box (widget->style, widget->window,
			 GTK_STATE_NORMAL, GTK_SHADOW_IN,
			 area, widget, "buttondefault",
			 x, y, width, height);

1010 1011 1012 1013 1014 1015
	  x += default_border.left;
	  y += default_border.top;
	  width -= default_border.left + default_border.right;
	  height -= default_border.top + default_border.bottom;
	}
      else if (GTK_WIDGET_CAN_DEFAULT (widget))
Elliot Lee's avatar
Elliot Lee committed
1016
	{
1017 1018 1019 1020
	  x += default_outside_border.left;
	  y += default_outside_border.top;
	  width -= default_outside_border.left + default_outside_border.right;
	  height -= default_outside_border.top + default_outside_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
1021
	}
1022
       
1023
      if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
1024
	{
1025 1026 1027 1028
	  x += focus_width + focus_pad;
	  y += focus_width + focus_pad;
	  width -= 2 * (focus_width + focus_pad);