gtkbutton.c 44.9 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;
Matthias Clasen's avatar
Matthias Clasen committed
81
  guint        align_set : 1;
82
};
83

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

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


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


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

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

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

  return button_type;
}

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

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

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

183 184
  object_class->destroy = gtk_button_destroy;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      return;
630
    }
631 632

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

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

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

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

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

void
gtk_button_released (GtkButton *button)
{
708 709
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_clicked (GtkButton *button)
{
716 717
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_enter (GtkButton *button)
{
724 725
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_leave (GtkButton *button)
{
732 733
  g_return_if_fail (GTK_IS_BUTTON (button));

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

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

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

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

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

Owen Taylor's avatar
Owen Taylor committed
771 772
  border_width = GTK_CONTAINER (widget)->border_width;

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

785
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
786

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

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

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

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

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

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

822 823
  GTK_WIDGET_CLASS (parent_class)->map (widget);

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

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

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

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

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

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

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

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

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

933
  gtk_button_get_props (button, &default_border, NULL, NULL);
934
			    
Elliot Lee's avatar
Elliot Lee committed
935 936 937
  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED (widget))
938
    gdk_window_move_resize (button->event_window,
Elliot Lee's avatar
Elliot Lee committed
939 940 941 942 943
			    widget->allocation.x + border_width,
			    widget->allocation.y + border_width,
			    widget->allocation.width - border_width * 2,
			    widget->allocation.height - border_width * 2);

944
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
945
    {
946 947 948 949 950 951 952
      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
953 954 955

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
956 957 958 959
	  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
960 961
	}

962 963 964 965 966 967 968 969 970 971 972 973 974
      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;
	}

975
      gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
976 977 978
    }
}

979 980 981 982 983 984 985
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
986
{
987
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
988 989
  gint width, height;
  gint x, y;
990
  gint border_width;
991 992
  GtkBorder default_border;
  GtkBorder default_outside_border;
993
  gboolean interior_focus;
994 995
  gint focus_width;
  gint focus_pad;
996
   
997
  if (GTK_WIDGET_DRAWABLE (button))
Elliot Lee's avatar
Elliot Lee committed
998
    {
999
      widget = GTK_WIDGET (button);
1000
      border_width = GTK_CONTAINER (widget)->border_width;
1001

1002
      gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
1003 1004 1005 1006
      gtk_widget_style_get (GTK_WIDGET (widget),
			    "focus-line-width", &focus_width,
			    "focus-padding", &focus_pad,
			    NULL); 
1007
	
1008 1009 1010 1011
      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;