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
  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;
199
  container_class->add = gtk_button_add;
200 201 202 203

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

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

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

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

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

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

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

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

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

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

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

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

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
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
445
static GType
446 447
gtk_button_child_type  (GtkContainer     *container)
{
448
  if (!GTK_BIN (container)->child)
449 450
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
451
    return G_TYPE_NONE;
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 481
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
482
static void
483 484 485 486
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
487
{
488 489
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
490

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

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

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

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

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

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

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

602 603
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
604 605 606 607 608
      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);
609
      hbox = gtk_hbox_new (FALSE, 2);
610

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

      return;
621
    }
622 623

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GtkReliefStyle
742
gtk_button_get_relief (GtkButton *button)
743 744 745 746 747 748 749
{
  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
750 751 752 753 754 755
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
756
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
757 758 759 760

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

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

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

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

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

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

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

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

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

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

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

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
  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
831
static void
832
gtk_button_get_props (GtkButton *button,
833 834
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
835
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
836
{
837
  GtkWidget *widget =  GTK_WIDGET (button);
838 839 840 841 842 843 844 845 846 847 848 849 850 851
  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
852

853 854 855 856 857 858 859 860 861 862 863 864
  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
865

866 867 868 869 870 871 872 873 874
  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);
875
  GtkBorder default_border;
876
  gboolean interior_focus;
877 878
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
879

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

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

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

901 902 903 904
      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
905
    }
906 907 908
  
  requisition->width += 2 * (focus_width + focus_pad);
  requisition->height += 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
909 910 911 912 913 914
}

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

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

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

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

934
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
935
    {
936 937 938 939 940 941 942
      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
943 944 945

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
946 947 948 949
	  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
950 951
	}

952 953 954 955 956 957 958 959 960 961 962 963 964
      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;
	}

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

969 970 971 972 973 974 975
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
976
{
977
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
978 979
  gint width, height;
  gint x, y;
980
  gint border_width;
981 982
  GtkBorder default_border;
  GtkBorder default_outside_border;
983
  gboolean interior_focus;
984 985
  gint focus_width;
  gint focus_pad;
986
   
987
  if (GTK_WIDGET_DRAWABLE (button))
Elliot Lee's avatar
Elliot Lee committed
988
    {
989
      widget = GTK_WIDGET (button);
990
      border_width = GTK_CONTAINER (widget)->border_width;
991

992
      gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
993 994 995 996
      gtk_widget_style_get (GTK_WIDGET (widget),
			    "focus-line-width", &focus_width,
			    "focus-padding", &focus_pad,
			    NULL); 
997
	
998 999 1000 1001
      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;
1002 1003 1004 1005 1006 1007 1008 1009 1010

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

1011 1012 1013 1014 1015 1016
	  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
1017
	{
1018 1019 1020 1021
	  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
1022
	}
1023
       
1024
      if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
1025
	{
</