gtkbutton.c 46.2 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

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
  /**
   * GtkButton:displace-focus:
   *
   * Whether the child_displacement_x/child_displacement_y properties should also 
   * affect the focus rectangle.
   *
   * Since: 2.6
   */
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_boolean ("displace-focus",
								 P_("Displace focus"),
								 P_("Whether the child_displacement_x/_y properties should also affect the focus rectangle"),
						       FALSE,
						       G_PARAM_READABLE));

Matthias Clasen's avatar
Matthias Clasen committed
394 395 396 397 398 399
  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));
  
400
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));  
Elliot Lee's avatar
Elliot Lee committed
401 402 403 404 405
}

static void
gtk_button_init (GtkButton *button)
{
406 407
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

408
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
409
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
410

411 412 413
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
414 415
  button->in_button = FALSE;
  button->button_down = FALSE;
416
  button->relief = GTK_RELIEF_NORMAL;
417 418
  button->use_stock = FALSE;
  button->use_underline = FALSE;
419
  button->depressed = FALSE;
420
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
421
  button->focus_on_click = TRUE;
422 423 424

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
425
  priv->align_set = 0;
Elliot Lee's avatar
Elliot Lee committed
426 427
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441
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);
}

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
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
464
static GType
465 466
gtk_button_child_type  (GtkContainer     *container)
{
467
  if (!GTK_BIN (container)->child)
468 469
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
470
    return G_TYPE_NONE;
471 472
}

473
static void
Matthias Clasen's avatar
Matthias Clasen committed
474 475
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
476
{
Matthias Clasen's avatar
Matthias Clasen committed
477 478
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

479 480 481
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
482 483 484
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
485 486 487 488
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
489 490 491 492

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
493 494 495 496 497 498 499
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
500
  maybe_set_alignment (GTK_BUTTON (container), widget);
501 502 503 504

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

Elliot Lee's avatar
Elliot Lee committed
505
static void
506 507 508 509
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
510
{
511 512
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
513

514
  switch (prop_id)
515
    {
516
    case PROP_LABEL:
517
      gtk_button_set_label (button, g_value_get_string (value));
518
      break;
519 520
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
521
      break;
522 523 524 525 526 527
    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
528 529 530
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
531 532 533 534 535 536
    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;
537
    default:
538
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
539 540 541 542 543
      break;
    }
}

static void
544 545 546 547
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
548
{
549 550
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
551

552
  switch (prop_id)
553
    {
554
    case PROP_LABEL:
555
      g_value_set_string (value, button->label_text);
556
      break;
557
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
558
      g_value_set_enum (value, gtk_button_get_relief (button));
559
      break;
560 561 562 563 564 565
    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
566 567 568
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
569 570 571 572 573 574
    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
575
    default:
576
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
577
      break;
Elliot Lee's avatar
Elliot Lee committed
578 579 580 581
    }
}

GtkWidget*
582
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
583
{
Manish Singh's avatar
Manish Singh committed
584
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
585 586
}

Matthias Clasen's avatar
Matthias Clasen committed
587 588 589 590 591 592 593 594 595 596 597
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;
}

598 599
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
600
{
Matthias Clasen's avatar
Matthias Clasen committed
601
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
602 603 604
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
605
  GtkWidget *align;
606
  
607 608 609 610 611
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
612

613
  if (GTK_BIN (button)->child)
614 615 616 617 618
    {
      gtk_container_remove (GTK_CONTAINER (button),
  			    GTK_BIN (button)->child);
      priv->image = NULL;
    }
619 620 621
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
622
    {
623 624
      label = gtk_label_new_with_mnemonic (item.label);

625 626
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
627 628 629 630 631
      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);
632
      hbox = gtk_hbox_new (FALSE, 2);
633

Matthias Clasen's avatar
Matthias Clasen committed
634 635 636 637 638
      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
639
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
640
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
641
      
642 643 644
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
645 646

      return;
647
    }
648 649

  if (button->use_underline)
650
    {
651 652
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
653
    }
654 655
  else
    label = gtk_label_new (button->label_text);
656
  
Matthias Clasen's avatar
Matthias Clasen committed
657 658
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677

  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.
678 679 680 681 682
 *
 * 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
683 684
 **/
GtkWidget*
685
gtk_button_new_from_stock (const gchar *stock_id)
686
{
687 688 689 690 691
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
                       "use_stock", TRUE,
                       "use_underline", TRUE,
                       NULL);
692 693
}

694 695 696 697 698 699 700
/**
 * 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.
701 702 703 704
 * 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.
705 706
 * Pressing Alt and that key activates the button.
 **/
707
GtkWidget*
708
gtk_button_new_with_mnemonic (const gchar *label)
709
{
710
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE,  NULL);
711 712
}

Elliot Lee's avatar
Elliot Lee committed
713 714 715
void
gtk_button_pressed (GtkButton *button)
{
716 717
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
718
  
Manish Singh's avatar
Manish Singh committed
719
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
720 721 722 723 724
}

void
gtk_button_released (GtkButton *button)
{
725 726
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_clicked (GtkButton *button)
{
733 734
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_enter (GtkButton *button)
{
741 742
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
743
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
744 745 746 747 748
}

void
gtk_button_leave (GtkButton *button)
{
749 750
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
751
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
752 753
}

754 755 756 757 758 759
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

760 761 762 763 764 765
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
766 767 768
}

GtkReliefStyle
769
gtk_button_get_relief (GtkButton *button)
770 771 772 773 774 775 776
{
  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
777 778 779 780 781 782
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
783
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
784 785 786 787

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

Owen Taylor's avatar
Owen Taylor committed
788 789
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
790
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
791 792 793 794
  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;
795
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
796
  attributes.event_mask = gtk_widget_get_events (widget);
797
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
798 799 800 801
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

802
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
803

804
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
805
  g_object_ref (widget->window);
806 807 808 809
  
  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
810 811 812 813

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

814 815 816 817 818 819 820
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
821 822 823 824 825 826 827 828

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
829 830 831
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

832 833 834 835 836
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
837 838
  GTK_WIDGET_CLASS (parent_class)->map (widget);

839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
  if (button->event_window)
    gdk_window_show (button->event_window);
}

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

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

Elliot Lee's avatar
Elliot Lee committed
854
static void
855
gtk_button_get_props (GtkButton *button,
856 857
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
858
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
859
{
860
  GtkWidget *widget =  GTK_WIDGET (button);
861 862 863 864 865 866 867 868 869 870 871 872 873 874
  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
875

876 877 878 879 880 881 882 883 884 885 886 887
  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
888

889 890 891 892 893 894 895 896 897
  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);
898
  GtkBorder default_border;
899 900
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
901

902
  gtk_button_get_props (button, &default_border, NULL, NULL);
903 904 905 906 907
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
Elliot Lee's avatar
Elliot Lee committed
908
  requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
909
			GTK_WIDGET (widget)->style->xthickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
910
  requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
911
			 GTK_WIDGET (widget)->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
912 913 914

  if (GTK_WIDGET_CAN_DEFAULT (widget))
    {
915 916
      requisition->width += default_border.left + default_border.right;
      requisition->height += default_border.top + default_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
917 918
    }

919
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
920
    {
921
      GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
922

923 924 925 926
      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
927
    }
928
  
929 930
  requisition->width += 2 * (focus_width + focus_pad);
  requisition->height += 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
931 932 933 934 935 936
}

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

940 941 942
  gint border_width = GTK_CONTAINER (widget)->border_width;
  gint xthickness = GTK_WIDGET (widget)->style->xthickness;
  gint ythickness = GTK_WIDGET (widget)->style->ythickness;
943
  GtkBorder default_border;
944 945
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
946

947
  gtk_button_get_props (button, &default_border, NULL, NULL);
948 949 950 951 952
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
953
			    
Elliot Lee's avatar
Elliot Lee committed
954 955 956
  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED (widget))
957
    gdk_window_move_resize (button->event_window,
Elliot Lee's avatar
Elliot Lee committed
958 959 960 961 962
			    widget->allocation.x + border_width,
			    widget->allocation.y + border_width,
			    widget->allocation.width - border_width * 2,
			    widget->allocation.height - border_width * 2);

963
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
964
    {
965 966 967 968 969 970 971
      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
972 973 974

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
975 976 977 978
	  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
979
	}
980

981 982 983 984 985 986 987
      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);
	}
988

989 990 991 992 993 994 995 996 997 998 999 1000 1001
      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;
	}

1002
      gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
1003 1004 1005
    }
}

1006 1007 1008 1009 1010 1011 1012
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
1013
{
1014
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
1015 1016
  gint width, height;
  gint x, y;
1017
  gint border_width;
1018 1019
  GtkBorder default_border;
  GtkBorder default_outside_border;
1020
  gboolean interior_focus;