gtkbutton.c 45.4 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 "gtkalias.h"
30
#include "gtkalignment.h"
Elliot Lee's avatar
Elliot Lee committed
31 32 33
#include "gtkbutton.h"
#include "gtklabel.h"
#include "gtkmain.h"
34
#include "gtkmarshalers.h"
35 36 37 38
#include "gtkimage.h"
#include "gtkhbox.h"
#include "gtkstock.h"
#include "gtkiconfactory.h"
39
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
40 41

#define CHILD_SPACING     1
42

43 44
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
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,
64 65
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
66
  PROP_USE_STOCK,
67 68 69 70 71 72 73 74 75 76
  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
77 78 79 80 81
  gfloat       xalign;
  gfloat       yalign;
  GtkSettings *settings;
  guint        show_image_connection;
  GtkWidget   *image;
Matthias Clasen's avatar
Matthias Clasen committed
82
  guint        align_set : 1;
83
};
84

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

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


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


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

  if (!button_type)
    {
145
      static const GTypeInfo button_info =
Elliot Lee's avatar
Elliot Lee committed
146 147
      {
	sizeof (GtkButtonClass),
148 149 150 151 152 153 154 155
	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
156 157
      };

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

  return button_type;
}

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

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

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

184 185
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
186
  widget_class->screen_changed = gtk_button_screen_changed;
187
  widget_class->realize = gtk_button_realize;
188
  widget_class->unrealize = gtk_button_unrealize;
189 190
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
191 192 193 194 195
  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;
196
  widget_class->key_release_event = gtk_button_key_release;
197 198 199 200
  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;
201
  container_class->add = gtk_button_add;
202 203 204 205

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

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

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

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
332
   * @widget: the object which received the signal.
333 334 335 336 337 338
   *
   * 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.
   */
339
  button_signals[ACTIVATE] =
Manish Singh's avatar
Manish Singh committed
340 341 342 343 344 345 346
    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);
347
  widget_class->activate_signal = button_signals[ACTIVATE];
348 349

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

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

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

static void
gtk_button_init (GtkButton *button)
{
391 392
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

393
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
394
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
395

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

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
410
  priv->align_set = 0;
Elliot Lee's avatar
Elliot Lee committed
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426
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);
}

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

458
static void
Matthias Clasen's avatar
Matthias Clasen committed
459 460
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
461
{
Matthias Clasen's avatar
Matthias Clasen committed
462 463
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

464 465 466
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
467 468 469
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
470 471 472 473
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
474 475 476 477

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
478 479 480 481 482 483 484
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
485
  maybe_set_alignment (GTK_BUTTON (container), widget);
486 487 488 489

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

Elliot Lee's avatar
Elliot Lee committed
490
static void
491 492 493 494
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
495
{
496 497
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
498

499
  switch (prop_id)
500
    {
501
    case PROP_LABEL:
502
      gtk_button_set_label (button, g_value_get_string (value));
503
      break;
504 505
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
506
      break;
507 508 509 510 511 512
    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
513 514 515
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
516 517 518 519 520 521
    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;
522
    default:
523
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
524 525 526 527 528
      break;
    }
}

static void
529 530 531 532
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
533
{
534 535
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
536

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

GtkWidget*
567
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
568
{
Manish Singh's avatar
Manish Singh committed
569
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
570 571
}

Matthias Clasen's avatar
Matthias Clasen committed
572 573 574 575 576 577 578 579 580 581 582
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;
}

583 584
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
585
{
Matthias Clasen's avatar
Matthias Clasen committed
586
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
587 588 589
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
590
  GtkWidget *align;
591
  
592 593 594 595 596
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
597

598
  if (GTK_BIN (button)->child)
599 600 601 602 603
    {
      gtk_container_remove (GTK_CONTAINER (button),
  			    GTK_BIN (button)->child);
      priv->image = NULL;
    }
604 605 606
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
607
    {
608 609
      label = gtk_label_new_with_mnemonic (item.label);

610 611
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
612 613 614 615 616
      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);
617
      hbox = gtk_hbox_new (FALSE, 2);
618

Matthias Clasen's avatar
Matthias Clasen committed
619 620 621 622 623
      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
624
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
625
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
626
      
627 628 629
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
630 631

      return;
632
    }
633 634

  if (button->use_underline)
635
    {
636 637
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
638
    }
639 640
  else
    label = gtk_label_new (button->label_text);
641
  
Matthias Clasen's avatar
Matthias Clasen committed
642 643
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

  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.
663 664 665 666 667
 *
 * 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
668 669
 **/
GtkWidget*
670
gtk_button_new_from_stock (const gchar *stock_id)
671
{
672 673 674 675 676
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
                       "use_stock", TRUE,
                       "use_underline", TRUE,
                       NULL);
677 678
}

679 680 681 682 683 684 685
/**
 * 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.
686 687 688 689
 * 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.
690 691
 * Pressing Alt and that key activates the button.
 **/
692
GtkWidget*
693
gtk_button_new_with_mnemonic (const gchar *label)
694
{
695
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE,  NULL);
696 697
}

Elliot Lee's avatar
Elliot Lee committed
698 699 700
void
gtk_button_pressed (GtkButton *button)
{
701 702
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
703
  
Manish Singh's avatar
Manish Singh committed
704
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
705 706 707 708 709
}

void
gtk_button_released (GtkButton *button)
{
710 711
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
712
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
713 714 715 716 717
}

void
gtk_button_clicked (GtkButton *button)
{
718 719
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_enter (GtkButton *button)
{
726 727
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
728
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
729 730 731 732 733
}

void
gtk_button_leave (GtkButton *button)
{
734 735
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
736
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
737 738
}

739 740 741 742 743 744
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

745 746 747 748 749 750
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
751 752 753
}

GtkReliefStyle
754
gtk_button_get_relief (GtkButton *button)
755 756 757 758 759 760 761
{
  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
762 763 764 765 766 767
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
768
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
769 770 771 772

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

Owen Taylor's avatar
Owen Taylor committed
773 774
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
775
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
776 777 778 779
  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;
780
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
781
  attributes.event_mask = gtk_widget_get_events (widget);
782
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
783 784 785 786
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

787
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
788

789
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
790
  g_object_ref (widget->window);
791 792 793 794
  
  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
795 796 797 798

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

799 800 801 802 803 804 805
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
806 807 808 809 810 811 812 813

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
814 815 816
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

817 818 819 820 821 822 823
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
  g_return_if_fail (GTK_IS_BUTTON (widget));

824 825
  GTK_WIDGET_CLASS (parent_class)->map (widget);

826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
  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
843
static void
844
gtk_button_get_props (GtkButton *button,
845 846
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
847
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
848
{
849
  GtkWidget *widget =  GTK_WIDGET (button);
850 851 852 853 854 855 856 857 858 859 860 861 862 863
  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
864

865 866 867 868 869 870 871 872 873 874 875 876
  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
877

878 879 880 881 882 883 884 885 886
  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);
887
  GtkBorder default_border;
888 889
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
890

891
  gtk_button_get_props (button, &default_border, NULL, NULL);
892 893 894 895 896
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
Elliot Lee's avatar
Elliot Lee committed
897
  requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
898
			GTK_WIDGET (widget)->style->xthickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
899
  requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
900
			 GTK_WIDGET (widget)->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
901 902 903

  if (GTK_WIDGET_CAN_DEFAULT (widget))
    {
904 905
      requisition->width += default_border.left + default_border.right;
      requisition->height += default_border.top + default_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
906 907
    }

908
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
909
    {
910
      GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
911

912 913 914 915
      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
916
    }
917
  
918 919
  requisition->width += 2 * (focus_width + focus_pad);
  requisition->height += 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
920 921 922 923 924 925
}

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

929 930 931
  gint border_width = GTK_CONTAINER (widget)->border_width;
  gint xthickness = GTK_WIDGET (widget)->style->xthickness;
  gint ythickness = GTK_WIDGET (widget)->style->ythickness;
932
  GtkBorder default_border;
933 934
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
935

936
  gtk_button_get_props (button, &default_border, NULL, NULL);
937 938 939 940 941
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
942
			    
Elliot Lee's avatar
Elliot Lee committed
943 944 945
  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED (widget))
946
    gdk_window_move_resize (button->event_window,
Elliot Lee's avatar
Elliot Lee committed
947 948 949 950 951
			    widget->allocation.x + border_width,
			    widget->allocation.y + border_width,
			    widget->allocation.width - border_width * 2,
			    widget->allocation.height - border_width * 2);

952
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
953
    {
954 955 956 957 958 959 960
      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
961 962 963

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
964 965 966 967
	  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
968
	}
969

970 971 972 973 974 975 976
      if (GTK_WIDGET_CAN_FOCUS (button))
	{
	  child_allocation.x += focus_width + focus_pad;
	  child_allocation.y += focus_width + focus_pad;
	  child_allocation.width =  MAX (1, child_allocation.width - (focus_width + focus_pad) * 2);
	  child_allocation.height = MAX (1, child_allocation.height - (focus_width + focus_pad) * 2);
	}
977

978 979 980 981 982 983 984 985 986 987 988 989 990
      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;
	}

991
      gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
992 993 994
    }
}

995 996 997 998 999 1000 1001
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
1002
{
1003
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
1004 1005
  gint width, height;
  gint x, y;
1006
  gint border_width;
1007 1008
  GtkBorder default_border;
  GtkBorder default_outside_border;
1009
  gboolean interior_focus;
1010 1011
  gint focus_width;
  gint focus_pad;
1012
   
1013
  if (GTK_WIDGET_DRAWABLE (button))