gtkbutton.c 53 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 43
static const GtkBorder default_default_border = { 1, 1, 1, 1 };
static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
44
static const GtkBorder default_inner_border = { 1, 1, 1, 1 };
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,
Matthias Clasen's avatar
Matthias Clasen committed
64
  PROP_IMAGE,
65 66
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
67
  PROP_USE_STOCK,
68 69
  PROP_FOCUS_ON_CLICK,
  PROP_XALIGN,
Matthias Clasen's avatar
Matthias Clasen committed
70
  PROP_YALIGN,
71 72 73 74 75 76 77
};

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

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

139

140
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
141

Matthias Clasen's avatar
Matthias Clasen committed
142
G_DEFINE_TYPE (GtkButton, gtk_button, GTK_TYPE_BIN)
Elliot Lee's avatar
Elliot Lee committed
143 144 145 146

static void
gtk_button_class_init (GtkButtonClass *klass)
{
Manish Singh's avatar
Manish Singh committed
147
  GObjectClass *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
148 149 150 151
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

Manish Singh's avatar
Manish Singh committed
152
  gobject_class = G_OBJECT_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
153 154 155
  object_class = (GtkObjectClass*) klass;
  widget_class = (GtkWidgetClass*) klass;
  container_class = (GtkContainerClass*) klass;
156
  
Manish Singh's avatar
Manish Singh committed
157 158 159
  gobject_class->constructor = gtk_button_constructor;
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
160

161 162
  object_class->destroy = gtk_button_destroy;

Matthias Clasen's avatar
Matthias Clasen committed
163
  widget_class->screen_changed = gtk_button_screen_changed;
164
  widget_class->realize = gtk_button_realize;
165
  widget_class->unrealize = gtk_button_unrealize;
166 167
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
168 169 170 171 172
  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
173
  widget_class->grab_broken_event = gtk_button_grab_broken;
174
  widget_class->key_release_event = gtk_button_key_release;
175 176
  widget_class->enter_notify_event = gtk_button_enter_notify;
  widget_class->leave_notify_event = gtk_button_leave_notify;
177 178
  widget_class->state_changed = gtk_button_state_changed;
  widget_class->grab_notify = gtk_button_grab_notify;
179 180

  container_class->child_type = gtk_button_child_type;
181
  container_class->add = gtk_button_add;
182 183 184 185

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
186 187
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
188
  klass->activate = gtk_real_button_activate;
189

Manish Singh's avatar
Manish Singh committed
190
  g_object_class_install_property (gobject_class,
191 192
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
193 194
                                                        P_("Label"),
                                                        P_("Text of the label widget inside the button, if the button contains a label widget"),
195
                                                        NULL,
196
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
197
  
Manish Singh's avatar
Manish Singh committed
198
  g_object_class_install_property (gobject_class,
199
                                   PROP_USE_UNDERLINE,
Matthias Clasen's avatar
x  
Matthias Clasen committed
200
                                   g_param_spec_boolean ("use-underline",
201 202
							 P_("Use underline"),
							 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
203
                                                        FALSE,
204
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
205
  
Manish Singh's avatar
Manish Singh committed
206
  g_object_class_install_property (gobject_class,
207
                                   PROP_USE_STOCK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
208
                                   g_param_spec_boolean ("use-stock",
209 210
							 P_("Use stock"),
							 P_("If set, the label is used to pick a stock item instead of being displayed"),
211
                                                        FALSE,
212
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
213
  
Soeren Sandmann's avatar
Soeren Sandmann committed
214 215
  g_object_class_install_property (gobject_class,
                                   PROP_FOCUS_ON_CLICK,
Matthias Clasen's avatar
x  
Matthias Clasen committed
216
                                   g_param_spec_boolean ("focus-on-click",
217 218
							 P_("Focus on click"),
							 P_("Whether the button grabs focus when it is clicked with the mouse"),
Soeren Sandmann's avatar
Soeren Sandmann committed
219
							 TRUE,
220
							 GTK_PARAM_READWRITE));
Soeren Sandmann's avatar
Soeren Sandmann committed
221
  
Manish Singh's avatar
Manish Singh committed
222
  g_object_class_install_property (gobject_class,
223 224
                                   PROP_RELIEF,
                                   g_param_spec_enum ("relief",
225 226
                                                      P_("Border relief"),
                                                      P_("The border relief style"),
227 228
                                                      GTK_TYPE_RELIEF_STYLE,
                                                      GTK_RELIEF_NORMAL,
229
                                                      GTK_PARAM_READWRITE));
230 231 232 233 234 235 236 237 238 239 240 241 242
  
  /**
   * 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",
243 244
                                                      P_("Horizontal alignment for child"),
                                                      P_("Horizontal position of child in available space. 0.0 is left aligned, 1.0 is right aligned"),
245 246 247
                                                      0.0,
                                                      1.0,
                                                      0.5,
248
                                                      GTK_PARAM_READWRITE));
249 250 251 252 253 254 255 256 257 258 259 260 261

  /**
   * 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",
262 263
                                                      P_("Vertical alignment for child"),
                                                      P_("Vertical position of child in available space. 0.0 is top aligned, 1.0 is bottom aligned"),
264 265 266
                                                      0.0,
                                                      1.0,
                                                      0.5,
267
                                                      GTK_PARAM_READWRITE));
Elliot Lee's avatar
Elliot Lee committed
268

Matthias Clasen's avatar
Matthias Clasen committed
269 270 271 272 273 274 275 276 277 278 279 280 281
  /**
   * 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,
282
                                                        GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
283

284 285 286 287 288 289 290 291
  /**
   * 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
292
  button_signals[PRESSED] =
Matthias Clasen's avatar
Matthias Clasen committed
293
    g_signal_new (I_("pressed"),
Manish Singh's avatar
Manish Singh committed
294 295 296 297 298 299
		  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);
300 301 302 303 304 305 306 307 308

  /**
   * 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
309
  button_signals[RELEASED] =
Matthias Clasen's avatar
Matthias Clasen committed
310
    g_signal_new (I_("released"),
Manish Singh's avatar
Manish Singh committed
311 312 313 314 315 316
		  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);
317 318 319 320 321 322 323

  /**
   * 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
324
  button_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
325
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
326 327 328 329 330 331
		  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);
332 333 334 335 336 337 338 339 340

  /**
   * 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
341
  button_signals[ENTER] =
Matthias Clasen's avatar
Matthias Clasen committed
342
    g_signal_new (I_("enter"),
Manish Singh's avatar
Manish Singh committed
343 344 345 346 347 348
		  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);
349 350 351 352 353 354 355 356 357

  /**
   * 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
358
  button_signals[LEAVE] =
Matthias Clasen's avatar
Matthias Clasen committed
359
    g_signal_new (I_("leave"),
Manish Singh's avatar
Manish Singh committed
360 361 362 363 364 365
		  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);
366 367 368

  /**
   * GtkButton::activate:
Matthias Clasen's avatar
Matthias Clasen committed
369
   * @widget: the object which received the signal.
370 371 372 373 374 375
   *
   * 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.
   */
376
  button_signals[ACTIVATE] =
Matthias Clasen's avatar
Matthias Clasen committed
377
    g_signal_new (I_("activate"),
Manish Singh's avatar
Manish Singh committed
378 379 380 381 382 383
		  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);
384
  widget_class->activate_signal = button_signals[ACTIVATE];
385 386

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
387
					   g_param_spec_boxed ("default-border",
388 389
							       P_("Default Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons"),
390
							       GTK_TYPE_BORDER,
391
							       GTK_PARAM_READABLE));
392 393

  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
394
					   g_param_spec_boxed ("default-outside-border",
395 396
							       P_("Default Outside Spacing"),
							       P_("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
397
							       GTK_TYPE_BORDER,
398
							       GTK_PARAM_READABLE));
399
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
400
					   g_param_spec_int ("child-displacement-x",
401 402
							     P_("Child X Displacement"),
							     P_("How far in the x direction to move the child when the button is depressed"),
403 404 405
							     G_MININT,
							     G_MAXINT,
							     0,
406
							     GTK_PARAM_READABLE));
407
  gtk_widget_class_install_style_property (widget_class,
Matthias Clasen's avatar
x  
Matthias Clasen committed
408
					   g_param_spec_int ("child-displacement-y",
409 410
							     P_("Child Y Displacement"),
							     P_("How far in the y direction to move the child when the button is depressed"),
411 412 413
							     G_MININT,
							     G_MAXINT,
							     0,
414
							     GTK_PARAM_READABLE));
415

416 417 418 419 420 421 422 423 424 425 426 427 428
  /**
   * 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,
429
						       GTK_PARAM_READABLE));
430

431
  /**
432
   * GtkButton:inner-border:
433
   *
434
   * Sets the border between the button edges and child.
435 436 437 438
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
439 440 441 442 443
					   g_param_spec_boxed ("inner-border",
                                                               P_("Inner Border"),
                                                               P_("Border between button edges and child."),
                                                               GTK_TYPE_BORDER,
                                                               GTK_PARAM_READABLE));
444

Matthias Clasen's avatar
Matthias Clasen committed
445 446 447 448
  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,
449
						       GTK_PARAM_READWRITE));
450 451

  g_type_class_add_private (gobject_class, sizeof (GtkButtonPrivate));
Elliot Lee's avatar
Elliot Lee committed
452 453 454 455 456
}

static void
gtk_button_init (GtkButton *button)
{
457 458
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

459
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
460
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
461

462 463 464
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
465 466
  button->in_button = FALSE;
  button->button_down = FALSE;
467
  button->relief = GTK_RELIEF_NORMAL;
468 469
  button->use_stock = FALSE;
  button->use_underline = FALSE;
470
  button->depressed = FALSE;
471
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
472
  button->focus_on_click = TRUE;
473 474 475

  priv->xalign = 0.5;
  priv->yalign = 0.5;
Matthias Clasen's avatar
Matthias Clasen committed
476
  priv->align_set = 0;
477
  priv->image_is_stock = TRUE;
Elliot Lee's avatar
Elliot Lee committed
478 479
}

480 481 482 483 484 485 486 487 488 489 490
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;
    }
  
Matthias Clasen's avatar
Matthias Clasen committed
491
  (* GTK_OBJECT_CLASS (gtk_button_parent_class)->destroy) (object);
492 493
}

494 495 496 497 498 499 500 501
static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;

Matthias Clasen's avatar
Matthias Clasen committed
502 503 504
  object = (* G_OBJECT_CLASS (gtk_button_parent_class)->constructor) (type,
								      n_construct_properties,
								      construct_params);
505 506 507 508 509 510 511 512 513 514 515

  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
516
static GType
517 518
gtk_button_child_type  (GtkContainer     *container)
{
519
  if (!GTK_BIN (container)->child)
520 521
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
522
    return G_TYPE_NONE;
523 524
}

525
static void
Matthias Clasen's avatar
Matthias Clasen committed
526 527
maybe_set_alignment (GtkButton *button,
		     GtkWidget *widget)
528
{
Matthias Clasen's avatar
Matthias Clasen committed
529 530
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);

531 532 533
  if (GTK_IS_MISC (widget))
    {
      GtkMisc *misc = GTK_MISC (widget);
Matthias Clasen's avatar
Matthias Clasen committed
534 535 536
      
      if (priv->align_set)
	gtk_misc_set_alignment (misc, priv->xalign, priv->yalign);
537 538 539 540
    }
  else if (GTK_IS_ALIGNMENT (widget))
    {
      GtkAlignment *alignment = GTK_ALIGNMENT (widget);
Matthias Clasen's avatar
Matthias Clasen committed
541 542 543 544

      if (priv->align_set)
	gtk_alignment_set (alignment, priv->xalign, priv->yalign, 
			   alignment->xscale, alignment->yscale);
545 546 547 548 549 550 551
    }
}

static void
gtk_button_add (GtkContainer *container,
		GtkWidget    *widget)
{
Matthias Clasen's avatar
Matthias Clasen committed
552
  maybe_set_alignment (GTK_BUTTON (container), widget);
553

Matthias Clasen's avatar
Matthias Clasen committed
554
  GTK_CONTAINER_CLASS (gtk_button_parent_class)->add (container, widget);
555 556
}

Elliot Lee's avatar
Elliot Lee committed
557
static void
558 559 560 561
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
562
{
563 564
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
Elliot Lee's avatar
Elliot Lee committed
565

566
  switch (prop_id)
567
    {
568
    case PROP_LABEL:
569
      gtk_button_set_label (button, g_value_get_string (value));
570
      break;
Matthias Clasen's avatar
Matthias Clasen committed
571 572 573
    case PROP_IMAGE:
      gtk_button_set_image (button, (GtkWidget *) g_value_get_object (value));
      break;
574 575
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
576
      break;
577 578 579 580 581 582
    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
583 584 585
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
586 587 588 589 590 591
    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;
592
    default:
593
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
594 595 596 597 598
      break;
    }
}

static void
599 600 601 602
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
603
{
604 605
  GtkButton *button = GTK_BUTTON (object);
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
606

607
  switch (prop_id)
608
    {
609
    case PROP_LABEL:
610
      g_value_set_string (value, button->label_text);
611
      break;
Matthias Clasen's avatar
Matthias Clasen committed
612 613 614
    case PROP_IMAGE:
      g_value_set_object (value, (GObject *)priv->image);
      break;
615
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
616
      g_value_set_enum (value, gtk_button_get_relief (button));
617
      break;
618 619 620 621 622 623
    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
624 625 626
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
627 628 629 630 631 632
    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
633
    default:
634
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
635
      break;
Elliot Lee's avatar
Elliot Lee committed
636 637 638 639
    }
}

GtkWidget*
640
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
641
{
Manish Singh's avatar
Manish Singh committed
642
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
643 644
}

Matthias Clasen's avatar
Matthias Clasen committed
645 646 647 648
static gboolean
show_image (GtkButton *button)
{
  gboolean show;
649 650 651 652
  
  if (button->label_text)
    {
      GtkSettings *settings;
Matthias Clasen's avatar
Matthias Clasen committed
653

654 655 656 657 658
      settings = gtk_widget_get_settings (GTK_WIDGET (button));        
      g_object_get (settings, "gtk-button-images", &show, NULL);
    }
  else
    show = TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
659 660 661 662

  return show;
}

663 664
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
665
{
Matthias Clasen's avatar
Matthias Clasen committed
666
  GtkButtonPrivate *priv = GTK_BUTTON_GET_PRIVATE (button);
667 668 669
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *hbox;
670
  GtkWidget *align;
Matthias Clasen's avatar
Matthias Clasen committed
671 672
  GtkWidget *image = NULL;
  gchar *label_text = NULL;
673
  
674 675
  if (!button->constructed)
    return;
676 677
 
  if (!button->label_text && !priv->image)
678
    return;
Matthias Clasen's avatar
Matthias Clasen committed
679
  
680 681 682 683 684 685
  if (priv->image && !priv->image_is_stock)
    {
      image = g_object_ref (priv->image);
      if (image->parent)
	gtk_container_remove (GTK_CONTAINER (image->parent), image);
      
686 687
      priv->image = NULL;
    }
688
  
689 690 691 692
  if (GTK_BIN (button)->child)
    gtk_container_remove (GTK_CONTAINER (button),
			  GTK_BIN (button)->child);
  
693
  if (button->use_stock &&
694
      button->label_text &&
695
      gtk_stock_lookup (button->label_text, &item))
696
    {
Matthias Clasen's avatar
Matthias Clasen committed
697 698 699 700 701 702 703
      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;
704

Matthias Clasen's avatar
Matthias Clasen committed
705 706 707 708
  if (image)
    {
      priv->image = image;

Matthias Clasen's avatar
Matthias Clasen committed
709 710
      g_object_set (priv->image, 
		    "visible", show_image (button),
711
		    "no-show-all", TRUE,
Matthias Clasen's avatar
Matthias Clasen committed
712
		    NULL);
713
      hbox = gtk_hbox_new (FALSE, 2);
714

Matthias Clasen's avatar
Matthias Clasen committed
715 716 717 718 719
      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
720
      gtk_box_pack_start (GTK_BOX (hbox), priv->image, FALSE, FALSE, 0);
721 722 723 724 725 726 727 728 729

      if (label_text)
	{
	  label = gtk_label_new_with_mnemonic (label_text);
	  gtk_label_set_mnemonic_widget (GTK_LABEL (label), 
					 GTK_WIDGET (button));

	  gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
	}
730
      
731 732 733
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
734

Matthias Clasen's avatar
Matthias Clasen committed
735 736
      g_object_unref (image);

737
      return;
738
    }
Matthias Clasen's avatar
Matthias Clasen committed
739
  
740
  if (button->use_underline)
741
    {
742 743
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
744
    }
745 746
  else
    label = gtk_label_new (button->label_text);
747
  
Matthias Clasen's avatar
Matthias Clasen committed
748 749
  if (priv->align_set)
    gtk_misc_set_alignment (GTK_MISC (label), priv->xalign, priv->yalign);
750
  
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
  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.
769 770 771 772 773
 *
 * 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
774 775
 **/
GtkWidget*
776
gtk_button_new_from_stock (const gchar *stock_id)
777
{
778 779
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
780 781
                       "use-stock", TRUE,
                       "use-underline", TRUE,
782
                       NULL);
783 784
}

785 786 787 788 789 790 791
/**
 * 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.
792 793 794 795
 * 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.
796 797
 * Pressing Alt and that key activates the button.
 **/
798
GtkWidget*
799
gtk_button_new_with_mnemonic (const gchar *label)
800
{
801
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use-underline", TRUE,  NULL);
802 803
}

Elliot Lee's avatar
Elliot Lee committed
804 805 806
void
gtk_button_pressed (GtkButton *button)
{
807 808
  g_return_if_fail (GTK_IS_BUTTON (button));

Matthias Clasen's avatar
Matthias Clasen committed
809
  
Manish Singh's avatar
Manish Singh committed
810
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
811 812 813 814 815
}

void
gtk_button_released (GtkButton *button)
{
816 817
  g_return_if_fail (GTK_IS_BUTTON (button));

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

void
gtk_button_clicked (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[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
827 828 829 830 831
}

void
gtk_button_enter (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[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
835 836 837 838 839
}

void
gtk_button_leave (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[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
843 844
}

845 846 847 848 849 850
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

851 852 853 854 855 856
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
857 858 859
}

GtkReliefStyle
860
gtk_button_get_relief (GtkButton *button)
861 862 863 864 865 866
{
  g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);

  return button->relief;
}

Elliot Lee's avatar
Elliot Lee committed
867 868 869 870 871 872
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
873
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
874 875 876 877

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

Owen Taylor's avatar
Owen Taylor committed
878 879
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
880
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
881 882 883 884
  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;
885
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
886
  attributes.event_mask = gtk_widget_get_events (widget);
887
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
888 889 890 891
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

892
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
893

894
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
895
  g_object_ref (widget->window);
896 897 898 899
  
  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
900 901 902 903

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

904 905 906 907 908 909 910
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
911 912 913 914 915 916 917 918

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
Matthias Clasen's avatar
Matthias Clasen committed
919
  GTK_WIDGET_CLASS (gtk_button_parent_class)->unrealize (widget);
920 921
}

922 923 924 925 926
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
Matthias Clasen's avatar
Matthias Clasen committed
927
  GTK_WIDGET_CLASS (gtk_button_parent_class)->map (widget);
928

929 930 931 932 933 934 935 936 937 938 939 940
  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);

Matthias Clasen's avatar
Matthias Clasen committed
941
  GTK_WIDGET_CLASS (gtk_button_parent_class)->unmap (widget);
942 943
}

Elliot Lee's avatar
Elliot Lee committed
944
static void
945
gtk_button_get_props (GtkButton *button,
946 947
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
948
                      GtkBorder *inner_border,
949
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
950
{
951
  GtkWidget *widget =  GTK_WIDGET (button);
952 953 954 955
  GtkBorder *tmp_border;

  if (default_border)
    {
956
      gtk_widget_style_get (widget, "default-border", &tmp_border, NULL);
957 958 959 960 961 962 963 964 965

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

967 968
  if (default_outside_border)
    {
969
      gtk_widget_style_get (widget, "default-outside-border", &tmp_border, NULL);
970 971 972 973 974 975 976 977 978

      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
979

980 981 982 983 984 985 986 987 988 989 990 991 992
  if (inner_border)
    {
      gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL);

      if (tmp_border)
	{
	  *inner_border = *tmp_border;
	  g_free (tmp_border);
	}
      else
	*inner_border = default_inner_border;
    }

993
  if (interior_focus)
994
    gtk_widget_style_get (widget, "interior-focus", interior_focus, NULL);
995 996 997 998 999 1000 1001
}
	
static void
gtk_button_size_request (GtkWidget      *widget,
			 GtkRequisition *requisition)
{
  GtkButton *button = GTK_BUTTON (widget);
1002
  GtkBorder default_border;
1003
  GtkBorder inner_border;
1004 1005
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
1006

1007
  gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL);
1008 1009 1010 1011 1012
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
1013