gtkbutton.c 52.1 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 "gtkprivate.h"
39
#include "gtkintl.h"
40
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
41 42

#define CHILD_SPACING     1
43

44 45
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
46

Matthias Clasen's avatar
Matthias Clasen committed
47
/* Time out before giving up on getting a key release when animating
48 49 50
 * the close button.
 */
#define ACTIVATE_TIMEOUT 250
Elliot Lee's avatar
Elliot Lee committed
51 52 53 54 55 56 57

enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
58
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
59 60
  LAST_SIGNAL
};
61

62
enum {
63 64
  PROP_0,
  PROP_LABEL,
Matthias Clasen's avatar
Matthias Clasen committed
65
  PROP_IMAGE,
66 67
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
68
  PROP_USE_STOCK,
69 70
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
71
  PROP_YALIGN,
72 73 74 75 76 77 78
};

#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
79 80 81
  gfloat       xalign;
  gfloat       yalign;
  GtkWidget   *image;
Matthias Clasen's avatar
Matthias Clasen committed
82
  guint        align_set : 1;
Matthias Clasen's avatar
Matthias Clasen committed
83
  guint        image_is_stock : 1;
84 85
  guint        has_grab : 1;
  guint32      grab_time;
86
};
87

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

133 134 135
static GObject*	gtk_button_constructor (GType                  type,
					guint                  n_construct_properties,
					GObjectConstructParam *construct_params);
Matthias Clasen's avatar
Matthias Clasen committed
136
static void gtk_button_construct_child (GtkButton             *button);
137 138 139 140 141
static void gtk_button_state_changed   (GtkWidget             *widget,
					GtkStateType           previous_state);
static void gtk_button_grab_notify     (GtkWidget             *widget,
					gboolean               was_grabbed);

142 143


144
static GtkBinClass *parent_class = NULL;
145
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
146 147


Manish Singh's avatar
Manish Singh committed
148
GType
149
gtk_button_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
150
{
Manish Singh's avatar
Manish Singh committed
151
  static GType button_type = 0;
Elliot Lee's avatar
Elliot Lee committed
152 153 154

  if (!button_type)
    {
155
      static const GTypeInfo button_info =
Elliot Lee's avatar
Elliot Lee committed
156 157
      {
	sizeof (GtkButtonClass),
158 159 160 161 162 163
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_button_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkButton),
164
	0,		/* n_preallocs */
165
	(GInstanceInitFunc) gtk_button_init,
Elliot Lee's avatar
Elliot Lee committed
166 167
      };

Matthias Clasen's avatar
Matthias Clasen committed
168
      button_type = g_type_register_static (GTK_TYPE_BIN, I_("GtkButton"),
Manish Singh's avatar
Manish Singh committed
169
					    &button_info, 0);
Elliot Lee's avatar
Elliot Lee committed
170 171 172 173 174 175 176 177
    }

  return button_type;
}

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
178
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
179 180 181 182
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
183
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
184 185 186
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
187 188
  
  parent_class = g_type_class_peek_parent (klass);
189

Manish Singh's avatar
Manish Singh committed
190 191 192
  gobject_class->constructor = gtk_button_constructor;
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
193

194 195
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
196
  widget_class->screen_changed = gtk_button_screen_changed;
197
  widget_class->realize = gtk_button_realize;
198
  widget_class->unrealize = gtk_button_unrealize;
199 200
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
201 202 203 204 205
  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;
Matthias Clasen's avatar
Matthias Clasen committed
206
  widget_class->grab_broken_event = gtk_button_grab_broken;
207
  widget_class->key_release_event = gtk_button_key_release;
208 209
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
210 211
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
212 213

  container_class->child_type = gtk_button_child_type;
214
  container_class->add = gtk_button_add;
215 216 217 218

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
219 220
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
221
  klass->activate = gtk_real_button_activate;
222

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

  /**
   * 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",
295 296
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
297 298 299
                                                      0.0,
                                                      1.0,
                                                      0.5,
300
                                                      GTK_PARAM_READWRITE));
Elliot Lee's avatar
Elliot Lee committed
301

Matthias Clasen's avatar
Matthias Clasen committed
302 303 304 305 306 307 308 309 310 311 312 313 314
  /**
   * GtkButton::image:
   * 
   * The child widget to appear next to the button text.
   * 
   * Since: 2.6
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE,
                                   g_param_spec_object ("image",
                                                        P_("Image widget"),
                                                        P_("Child widget to appear next to the button text"),
                                                        GTK_TYPE_WIDGET,
315
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
316

317 318 319 320 321 322 323 324
  /**
   * GtkButton::pressed:
   * @button: the object that received the signal
   *
   * Emitted when the button is pressed.
   * 
   * @Deprecated: Use the GtkWidget::button-press-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
325
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
326
    g_signal_new (I_("pressed"),
Manish Singh's avatar
Manish Singh committed
327 328 329 330 331 332
		  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);
333 334 335 336 337 338 339 340 341

  /**
   * GtkButton::released:
   * @button: the object that received the signal
   *
   * Emitted when the button is released.
   * 
   * @Deprecated: Use the GtkWidget::button-release-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
342
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
343
    g_signal_new (I_("released"),
Manish Singh's avatar
Manish Singh committed
344 345 346 347 348 349
		  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);
350 351 352 353 354 355 356

  /**
   * GtkButton::clicked:
   * @button: the object that received the signal
   *
   * Emitted when the button has been activated (pressed and released).
   */ 
Elliot Lee's avatar
Elliot Lee committed
357
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
358
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
359 360 361 362 363 364
		  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);
365 366 367 368 369 370 371 372 373

  /**
   * GtkButton::enter:
   * @button: the object that received the signal
   *
   * Emitted when the pointer enters the button.
   * 
   * @Deprecated: Use the GtkWidget::enter-notify-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
374
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
375
    g_signal_new (I_("enter"),
Manish Singh's avatar
Manish Singh committed
376 377 378 379 380 381
		  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);
382 383 384 385 386 387 388 389 390

  /**
   * GtkButton::leave:
   * @button: the object that received the signal
   *
   * Emitted when the pointer leaves the button.
   * 
   * @Deprecated: Use the GtkWidget::leave-notify-event signal.
   */ 
Elliot Lee's avatar
Elliot Lee committed
391
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
392
    g_signal_new (I_("leave"),
Manish Singh's avatar
Manish Singh committed
393 394 395 396 397 398
		  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);
399 400 401

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
402
   * @widget: the object which received the signal.
403 404 405 406 407 408
   *
   * 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.
   */
409
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
410
    g_signal_new (I_("activate"),
Manish Singh's avatar
Manish Singh committed
411 412 413 414 415 416
		  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);
417
  widget_class->activate_signal = button_signals[ACTIVATE];
418 419

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
420
					   g_param_spec_boxed ("default-border",
421 422
							       P_("Default Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons"),
423
							       GTK_TYPE_BORDER,
424
							       GTK_PARAM_READABLE));
425 426

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
427
					   g_param_spec_boxed ("default-outside-border",
428 429
							       P_("Default Outside Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
430
							       GTK_TYPE_BORDER,
431
							       GTK_PARAM_READABLE));
432
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
433
					   g_param_spec_int ("child-displacement-x",
434 435
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
436 437 438
							     G_MININT,
							     G_MAXINT,
							     0,
439
							     GTK_PARAM_READABLE));
440
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
441
					   g_param_spec_int ("child-displacement-y",
442 443
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
444 445 446
							     G_MININT,
							     G_MAXINT,
							     0,
447
							     GTK_PARAM_READABLE));
448

449 450 451 452 453 454 455 456 457 458 459 460 461
  /**
   * 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,
462
						       GTK_PARAM_READABLE));
463

Matthias Clasen's avatar
Matthias Clasen committed
464 465 466 467
  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,
468
						       GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
469
  
470
  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));  
Elliot Lee's avatar
Elliot Lee committed
471 472 473 474 475
}

static void
gtk_button_init (GtkButton *button)
{
476 477
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

478
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
479
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
480

481 482 483
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
484 485
  button->in_button = FALSE;
  button->button_down = FALSE;
486
  button->relief = GTK_RELIEF_NORMAL;
487 488
  button->use_stock = FALSE;
  button->use_underline = FALSE;
489
  button->depressed = FALSE;
490
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
491
  button->focus_on_click = TRUE;
492 493 494

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
495
  priv->align_set = 0;
496
  priv->image_is_stock = TRUE;
Elliot Lee's avatar
Elliot Lee committed
497 498
}

499 500 501 502 503 504 505 506 507 508 509 510 511 512
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);
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
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
535
static GType
536 537
gtk_button_child_type  (GtkContainer     *container)
{
538
  if (!GTK_BIN (container)->child)
539 540
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
541
    return G_TYPE_NONE;
542 543
}

544
static void
Matthias Clasen's avatar
Matthias Clasen committed
545 546
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
547
{
Matthias Clasen's avatar
Matthias Clasen committed
548 549
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

550 551 552
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
553 554 555
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
556 557 558 559
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
560 561 562 563

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
564 565 566 567 568 569 570
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
571
  maybe_set_alignment (GTK_BUTTON (container), widget);
572 573 574 575

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

Elliot Lee's avatar
Elliot Lee committed
576
static void
577 578 579 580
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
581
{
582 583
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
584

585
  switch (prop_id)
586
    {
587
    case PROP_LABEL:
588
      gtk_button_set_label (button, g_value_get_string (value));
589
      break;
Matthias Clasen's avatar
Matthias Clasen committed
590 591 592
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
593 594
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
595
      break;
596 597 598 599 600 601
    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
602 603 604
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
605 606 607 608 609 610
    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;
611
    default:
612
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
613 614 615 616 617
      break;
    }
}

static void
618 619 620 621
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
622
{
623 624
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
625

626
  switch (prop_id)
627
    {
628
    case PROP_LABEL:
629
      g_value_set_string (value, button->label_text);
630
      break;
Matthias Clasen's avatar
Matthias Clasen committed
631 632 633
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
634
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
635
      g_value_set_enum (value, gtk_button_get_relief (button));
636
      break;
637 638 639 640 641 642
    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
643 644 645
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
646 647 648 649 650 651
    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
652
    default:
653
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
654
      break;
Elliot Lee's avatar
Elliot Lee committed
655 656 657 658
    }
}

GtkWidget*
659
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
660
{
Manish Singh's avatar
Manish Singh committed
661
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
662 663
}

Matthias Clasen's avatar
Matthias Clasen committed
664 665 666 667 668 669 670 671 672 673 674
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;
}

675 676
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
677
{
Matthias Clasen's avatar
Matthias Clasen committed
678
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
679 680 681
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
682
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
683 684
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
685
  
686 687 688 689 690
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
691

692
  if (GTK_BIN (button)->child)
693
    {
Matthias Clasen's avatar
Matthias Clasen committed
694
      if (priv->image && !priv->image_is_stock)
695 696 697 698 699
	{
	  image = g_object_ref (priv->image);
	  if (image->parent)
	    gtk_container_remove (GTK_CONTAINER (image->parent), image);
	}
Matthias Clasen's avatar
Matthias Clasen committed
700

701 702
      gtk_container_remove (GTK_CONTAINER (button),
  			    GTK_BIN (button)->child);
Matthias Clasen's avatar
Matthias Clasen committed
703
  
704 705
      priv->image = NULL;
    }
706 707 708
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
709
    {
Matthias Clasen's avatar
Matthias Clasen committed
710 711 712 713 714 715 716
      if (!image)
	image = g_object_ref (gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON));

      label_text = item.label;
    }
  else
    label_text = button->label_text;
717

Matthias Clasen's avatar
Matthias Clasen committed
718 719 720
  if (image)
    {
      label = gtk_label_new_with_mnemonic (label_text);
721 722
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
Matthias Clasen's avatar
Matthias Clasen committed
723 724
      priv->image = image;

Matthias Clasen's avatar
Matthias Clasen committed
725 726
      g_object_set (priv->image, 
		    "visible", show_image (button),
727
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
728
		    NULL);
729
      hbox = gtk_hbox_new (FALSE, 2);
730

Matthias Clasen's avatar
Matthias Clasen committed
731 732 733 734 735
      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
736
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
737
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
738
      
739 740 741
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
742

Matthias Clasen's avatar
Matthias Clasen committed
743 744
      g_object_unref (image);

745
      return;
746
    }
Matthias Clasen's avatar
Matthias Clasen committed
747 748
  
 if (button->use_underline)
749
    {
750 751
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
752
    }
753 754
  else
    label = gtk_label_new (button->label_text);
755
  
Matthias Clasen's avatar
Matthias Clasen committed
756 757
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776

  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.
777 778 779 780 781
 *
 * 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
782 783
 **/
GtkWidget*
784
gtk_button_new_from_stock (const gchar *stock_id)
785
{
786 787
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
788 789
                       "use-stock", TRUE,
                       "use-underline", TRUE,
790
                       NULL);
791 792
}

793 794 795 796 797 798 799
/**
 * 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.
800 801 802 803
 * 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.
804 805
 * Pressing Alt and that key activates the button.
 **/
806
GtkWidget*
807
gtk_button_new_with_mnemonic (const gchar *label)
808
{
809
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use-underline", TRUE,  NULL);
810 811
}

Elliot Lee's avatar
Elliot Lee committed
812 813 814
void
gtk_button_pressed (GtkButton *button)
{
815 816
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
817
  
Manish Singh's avatar
Manish Singh committed
818
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
819 820 821 822 823
}

void
gtk_button_released (GtkButton *button)
{
824 825
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
826
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
827 828 829 830 831
}

void
gtk_button_clicked (GtkButton *button)
{
832 833
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
834
  g_signal_emit (button, button_signals[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
835 836 837 838 839
}

void
gtk_button_enter (GtkButton *button)
{
840 841
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
842
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
843 844 845 846 847
}

void
gtk_button_leave (GtkButton *button)
{
848 849
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
850
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
851 852
}

853 854 855 856 857 858
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

859 860 861 862 863 864
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
865 866 867
}

GtkReliefStyle
868
gtk_button_get_relief (GtkButton *button)
869 870 871 872 873 874
{
  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);

  return button->relief;
}

Elliot Lee's avatar
Elliot Lee committed
875 876 877 878 879 880
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
881
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
882 883 884 885

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

Owen Taylor's avatar
Owen Taylor committed
886 887
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
888
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
889 890 891 892
  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;
893
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
894
  attributes.event_mask = gtk_widget_get_events (widget);
895
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
896 897 898 899
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

900
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
901

902
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
903
  g_object_ref (widget->window);
904 905 906 907
  
  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
908 909 910 911

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

912 913 914 915 916 917 918
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
919 920 921 922 923 924 925 926

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
927 928 929
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

930 931 932 933 934
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
935 936
  GTK_WIDGET_CLASS (parent_class)->map (widget);

937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
  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
952
static void
953
gtk_button_get_props (GtkButton *button,
954 955
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
956
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
957
{
958
  GtkWidget *widget =  GTK_WIDGET (button);
959 960 961 962
  GtkBorder *tmp_border;

  if (default_border)
    {
963
      gtk_widget_style_get (widget, "default-border", &tmp_border, NULL);
964 965 966 967 968 969 970 971 972

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

974 975
  if (default_outside_border)
    {
976
      gtk_widget_style_get (widget, "default-outside-border", &tmp_border, NULL);
977 978 979 980 981 982 983 984 985

      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
986