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

#define CHILD_SPACING     1
40

41 42
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
43

44 45 46 47
/* Time out before giving up on getting a key release when animatng
 * the close button.
 */
#define ACTIVATE_TIMEOUT 250
Elliot Lee's avatar
Elliot Lee committed
48 49 50 51 52 53 54

enum {
  PRESSED,
  RELEASED,
  CLICKED,
  ENTER,
  LEAVE,
55
  ACTIVATE,
Elliot Lee's avatar
Elliot Lee committed
56 57
  LAST_SIGNAL
};
58

59
enum {
60 61
  PROP_0,
  PROP_LABEL,
62 63
  PROP_RELIEF,
  PROP_USE_UNDERLINE,
Soeren Sandmann's avatar
Soeren Sandmann committed
64 65
  PROP_USE_STOCK,
  PROP_FOCUS_ON_CLICK
66
};
67

Elliot Lee's avatar
Elliot Lee committed
68 69
static void gtk_button_class_init     (GtkButtonClass   *klass);
static void gtk_button_init           (GtkButton        *button);
70
static void gtk_button_destroy        (GtkObject        *object);
71 72 73 74 75 76 77 78
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);
Elliot Lee's avatar
Elliot Lee committed
79
static void gtk_button_realize        (GtkWidget        *widget);
80
static void gtk_button_unrealize      (GtkWidget        *widget);
81 82
static void gtk_button_map            (GtkWidget        *widget);
static void gtk_button_unmap          (GtkWidget        *widget);
Elliot Lee's avatar
Elliot Lee committed
83 84 85 86 87 88 89 90 91 92
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);
93 94
static gint gtk_button_key_release    (GtkWidget        *widget,
				       GdkEventKey      *event);
Elliot Lee's avatar
Elliot Lee committed
95 96 97 98 99 100
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
101
static void gtk_real_button_activate  (GtkButton         *button);
102
static void gtk_button_update_state   (GtkButton        *button);
Manish Singh's avatar
Manish Singh committed
103
static GType gtk_button_child_type    (GtkContainer     *container);
104 105
static void gtk_button_finish_activate (GtkButton *button,
					gboolean   do_it);
Elliot Lee's avatar
Elliot Lee committed
106

107 108 109 110 111 112
static GObject*	gtk_button_constructor     (GType                  type,
					    guint                  n_construct_properties,
					    GObjectConstructParam *construct_params);
static void     gtk_button_construct_child (GtkButton             *button);


113
static GtkBinClass *parent_class = NULL;
114
static guint button_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
115 116


Manish Singh's avatar
Manish Singh committed
117
GType
118
gtk_button_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
119
{
Manish Singh's avatar
Manish Singh committed
120
  static GType button_type = 0;
Elliot Lee's avatar
Elliot Lee committed
121 122 123

  if (!button_type)
    {
124
      static const GTypeInfo button_info =
Elliot Lee's avatar
Elliot Lee committed
125 126
      {
	sizeof (GtkButtonClass),
127 128 129 130 131 132 133 134
	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
135 136
      };

Manish Singh's avatar
Manish Singh committed
137 138
      button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton",
					    &button_info, 0);
Elliot Lee's avatar
Elliot Lee committed
139 140 141 142 143 144 145 146
    }

  return button_type;
}

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 157
  
  parent_class = g_type_class_peek_parent (klass);
158

Manish Singh's avatar
Manish Singh committed
159 160 161
  gobject_class->constructor = gtk_button_constructor;
  gobject_class->set_property = gtk_button_set_property;
  gobject_class->get_property = gtk_button_get_property;
162

163 164
  object_class->destroy = gtk_button_destroy;

165
  widget_class->realize = gtk_button_realize;
166
  widget_class->unrealize = gtk_button_unrealize;
167 168
  widget_class->map = gtk_button_map;
  widget_class->unmap = gtk_button_unmap;
169 170 171 172 173
  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;
174
  widget_class->key_release_event = gtk_button_key_release;
175 176 177 178 179 180 181 182
  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;

  klass->pressed = gtk_real_button_pressed;
  klass->released = gtk_real_button_released;
  klass->clicked = NULL;
183 184
  klass->enter = gtk_button_update_state;
  klass->leave = gtk_button_update_state;
185
  klass->activate = gtk_real_button_activate;
186

Manish Singh's avatar
Manish Singh committed
187
  g_object_class_install_property (gobject_class,
188 189 190
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
                                                        _("Label"),
Soren Sandmann's avatar
Soren Sandmann committed
191
                                                        _("Text of the label widget inside the button, if the button contains a label widget"),
192
                                                        NULL,
193 194
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
  
Manish Singh's avatar
Manish Singh committed
195
  g_object_class_install_property (gobject_class,
196 197 198 199 200 201 202
                                   PROP_USE_UNDERLINE,
                                   g_param_spec_boolean ("use_underline",
							 _("Use underline"),
							 _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
                                                        FALSE,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
  
Manish Singh's avatar
Manish Singh committed
203
  g_object_class_install_property (gobject_class,
204 205 206 207 208 209
                                   PROP_USE_STOCK,
                                   g_param_spec_boolean ("use_stock",
							 _("Use stock"),
							 _("If set, the label is used to pick a stock item instead of being displayed"),
                                                        FALSE,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
210
  
Soeren Sandmann's avatar
Soeren Sandmann committed
211 212 213 214 215 216 217 218
  g_object_class_install_property (gobject_class,
                                   PROP_FOCUS_ON_CLICK,
                                   g_param_spec_boolean ("focus_on_click",
							 _("Focus on click"),
							 _("Whether the button grabs focus when it is clicked with the mouse"),
							 TRUE,
							 G_PARAM_READWRITE));
  
Manish Singh's avatar
Manish Singh committed
219
  g_object_class_install_property (gobject_class,
220 221 222
                                   PROP_RELIEF,
                                   g_param_spec_enum ("relief",
                                                      _("Border relief"),
Soren Sandmann's avatar
Soren Sandmann committed
223
                                                      _("The border relief style"),
224 225 226
                                                      GTK_TYPE_RELIEF_STYLE,
                                                      GTK_RELIEF_NORMAL,
                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
Elliot Lee's avatar
Elliot Lee committed
227 228

  button_signals[PRESSED] =
Manish Singh's avatar
Manish Singh committed
229 230 231 232 233 234 235
    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
236
  button_signals[RELEASED] =
Manish Singh's avatar
Manish Singh committed
237 238 239 240 241 242 243
    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
244
  button_signals[CLICKED] =
Manish Singh's avatar
Manish Singh committed
245 246 247 248 249 250 251
    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
252
  button_signals[ENTER] =
Manish Singh's avatar
Manish Singh committed
253 254 255 256 257 258 259
    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
260
  button_signals[LEAVE] =
Manish Singh's avatar
Manish Singh committed
261 262 263 264 265 266 267
    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);
268
  button_signals[ACTIVATE] =
Manish Singh's avatar
Manish Singh committed
269 270 271 272 273 274 275
    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);
276
  widget_class->activate_signal = button_signals[ACTIVATE];
277 278

  gtk_widget_class_install_style_property (widget_class,
279 280 281 282 283 284 285 286 287 288 289 290
					   g_param_spec_boxed ("default_border",
							       _("Default Spacing"),
							       _("Extra space to add for CAN_DEFAULT buttons"),
							       GTK_TYPE_BORDER,
							       G_PARAM_READABLE));

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_boxed ("default_outside_border",
							       _("Default Outside Spacing"),
							       _("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
							       GTK_TYPE_BORDER,
							       G_PARAM_READABLE));
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("child_displacement_x",
							     _("Child X Displacement"),
							     _("How far in the x direction to move the child when the button is depressed"),
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("child_displacement_y",
							     _("Child Y Displacement"),
							     _("How far in the y direction to move the child when the button is depressed"),
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
Elliot Lee's avatar
Elliot Lee committed
307 308 309 310 311
}

static void
gtk_button_init (GtkButton *button)
{
312
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
313
  GTK_WIDGET_SET_FLAGS (button, GTK_NO_WINDOW);
Elliot Lee's avatar
Elliot Lee committed
314

315 316 317
  button->label_text = NULL;
  
  button->constructed = FALSE;
Elliot Lee's avatar
Elliot Lee committed
318 319
  button->in_button = FALSE;
  button->button_down = FALSE;
320
  button->relief = GTK_RELIEF_NORMAL;
321 322
  button->use_stock = FALSE;
  button->use_underline = FALSE;
323
  button->depressed = FALSE;
324
  button->depress_on_activate = TRUE;
Soeren Sandmann's avatar
Soeren Sandmann committed
325
  button->focus_on_click = TRUE;
Elliot Lee's avatar
Elliot Lee committed
326 327
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341
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);
}

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
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
364
static GType
365 366
gtk_button_child_type  (GtkContainer     *container)
{
367
  if (!GTK_BIN (container)->child)
368 369
    return GTK_TYPE_WIDGET;
  else
Manish Singh's avatar
Manish Singh committed
370
    return G_TYPE_NONE;
371 372
}

Elliot Lee's avatar
Elliot Lee committed
373
static void
374 375 376 377
gtk_button_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
Elliot Lee's avatar
Elliot Lee committed
378
{
379 380 381
  GtkButton *button;

  button = GTK_BUTTON (object);
Elliot Lee's avatar
Elliot Lee committed
382

383
  switch (prop_id)
384
    {
385
    case PROP_LABEL:
386
      gtk_button_set_label (button, g_value_get_string (value));
387
      break;
388 389
    case PROP_RELIEF:
      gtk_button_set_relief (button, g_value_get_enum (value));
390
      break;
391 392 393 394 395 396
    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
397 398 399
    case PROP_FOCUS_ON_CLICK:
      gtk_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
400
    default:
401
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
402 403 404 405 406
      break;
    }
}

static void
407 408 409 410
gtk_button_get_property (GObject         *object,
                         guint            prop_id,
                         GValue          *value,
                         GParamSpec      *pspec)
411
{
412 413 414 415
  GtkButton *button;

  button = GTK_BUTTON (object);

416
  switch (prop_id)
417
    {
418
    case PROP_LABEL:
419
      g_value_set_string (value, button->label_text);
420
      break;
421
    case PROP_RELIEF:
Alexander Larsson's avatar
Alexander Larsson committed
422
      g_value_set_enum (value, gtk_button_get_relief (button));
423
      break;
424 425 426 427 428 429
    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
430 431 432
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value, button->focus_on_click);
      break;
Tim Janik's avatar
Tim Janik committed
433
    default:
434
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Tim Janik's avatar
Tim Janik committed
435
      break;
Elliot Lee's avatar
Elliot Lee committed
436 437 438 439
    }
}

GtkWidget*
440
gtk_button_new (void)
Elliot Lee's avatar
Elliot Lee committed
441
{
Manish Singh's avatar
Manish Singh committed
442
  return g_object_new (GTK_TYPE_BUTTON, NULL);
Elliot Lee's avatar
Elliot Lee committed
443 444
}

445 446
static void
gtk_button_construct_child (GtkButton *button)
Elliot Lee's avatar
Elliot Lee committed
447
{
448 449 450 451
  GtkStockItem item;
  GtkWidget *label;
  GtkWidget *image;
  GtkWidget *hbox;
452
  GtkWidget *align;
Elliot Lee's avatar
Elliot Lee committed
453

454 455 456 457 458
  if (!button->constructed)
    return;
  
  if (button->label_text == NULL)
    return;
Elliot Lee's avatar
Elliot Lee committed
459

460 461 462
  if (GTK_BIN (button)->child)
    gtk_container_remove (GTK_CONTAINER (button),
			  GTK_BIN (button)->child);
463

464 465 466
  
  if (button->use_stock &&
      gtk_stock_lookup (button->label_text, &item))
467
    {
468 469
      label = gtk_label_new_with_mnemonic (item.label);

470 471 472
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
      
      image = gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON);
473
      hbox = gtk_hbox_new (FALSE, 2);
474

475 476
      align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
      
Havoc Pennington's avatar
Havoc Pennington committed
477
      gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
478
      gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
479
      
480 481 482
      gtk_container_add (GTK_CONTAINER (button), align);
      gtk_container_add (GTK_CONTAINER (align), hbox);
      gtk_widget_show_all (align);
483 484

      return;
485
    }
486 487

  if (button->use_underline)
488
    {
489 490
      label = gtk_label_new_with_mnemonic (button->label_text);
      gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
491
    }
492 493
  else
    label = gtk_label_new (button->label_text);
494
  
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
  gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);

  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.
515 516 517 518 519
 *
 * 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
520 521
 **/
GtkWidget*
522
gtk_button_new_from_stock (const gchar *stock_id)
523
{
524 525 526 527 528
  return g_object_new (GTK_TYPE_BUTTON,
                       "label", stock_id,
                       "use_stock", TRUE,
                       "use_underline", TRUE,
                       NULL);
529 530
}

531 532 533 534 535 536 537
/**
 * 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.
538 539 540 541
 * 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.
542 543
 * Pressing Alt and that key activates the button.
 **/
544
GtkWidget*
545
gtk_button_new_with_mnemonic (const gchar *label)
546
{
547
  return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE,  NULL);
548 549
}

Elliot Lee's avatar
Elliot Lee committed
550 551 552
void
gtk_button_pressed (GtkButton *button)
{
553 554
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
555
  g_signal_emit (button, button_signals[PRESSED], 0);
Elliot Lee's avatar
Elliot Lee committed
556 557 558 559 560
}

void
gtk_button_released (GtkButton *button)
{
561 562
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
563
  g_signal_emit (button, button_signals[RELEASED], 0);
Elliot Lee's avatar
Elliot Lee committed
564 565 566 567 568
}

void
gtk_button_clicked (GtkButton *button)
{
569 570
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
571
  g_signal_emit (button, button_signals[CLICKED], 0);
Elliot Lee's avatar
Elliot Lee committed
572 573 574 575 576
}

void
gtk_button_enter (GtkButton *button)
{
577 578
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
579
  g_signal_emit (button, button_signals[ENTER], 0);
Elliot Lee's avatar
Elliot Lee committed
580 581 582 583 584
}

void
gtk_button_leave (GtkButton *button)
{
585 586
  g_return_if_fail (GTK_IS_BUTTON (button));

Manish Singh's avatar
Manish Singh committed
587
  g_signal_emit (button, button_signals[LEAVE], 0);
Elliot Lee's avatar
Elliot Lee committed
588 589
}

590 591 592 593 594 595
void
gtk_button_set_relief (GtkButton *button,
		       GtkReliefStyle newrelief)
{
  g_return_if_fail (GTK_IS_BUTTON (button));

596 597 598 599 600 601
  if (newrelief != button->relief) 
    {
       button->relief = newrelief;
       g_object_notify (G_OBJECT (button), "relief");
       gtk_widget_queue_draw (GTK_WIDGET (button));
    }
602 603 604
}

GtkReliefStyle
605
gtk_button_get_relief (GtkButton *button)
606 607 608 609 610 611 612
{
  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
613 614 615 616 617 618
static void
gtk_button_realize (GtkWidget *widget)
{
  GtkButton *button;
  GdkWindowAttr attributes;
  gint attributes_mask;
Owen Taylor's avatar
Owen Taylor committed
619
  gint border_width;
Elliot Lee's avatar
Elliot Lee committed
620 621 622 623

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

Owen Taylor's avatar
Owen Taylor committed
624 625
  border_width = GTK_CONTAINER (widget)->border_width;

Elliot Lee's avatar
Elliot Lee committed
626
  attributes.window_type = GDK_WINDOW_CHILD;
Owen Taylor's avatar
Owen Taylor committed
627 628 629 630
  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;
631
  attributes.wclass = GDK_INPUT_ONLY;
Elliot Lee's avatar
Elliot Lee committed
632
  attributes.event_mask = gtk_widget_get_events (widget);
633
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
Elliot Lee's avatar
Elliot Lee committed
634 635 636 637
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

638
  attributes_mask = GDK_WA_X | GDK_WA_Y;
Elliot Lee's avatar
Elliot Lee committed
639

640
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
641
  g_object_ref (widget->window);
642 643 644 645
  
  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
646 647 648 649

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

650 651 652 653 654 655 656
static void
gtk_button_unrealize (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    gtk_button_finish_activate (button, FALSE);
657 658 659 660 661 662 663 664

  if (button->event_window)
    {
      gdk_window_set_user_data (button->event_window, NULL);
      gdk_window_destroy (button->event_window);
      button->event_window = NULL;
    }
  
665 666 667
  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}

668 669 670 671 672 673 674
static void
gtk_button_map (GtkWidget *widget)
{
  GtkButton *button = GTK_BUTTON (widget);
  
  g_return_if_fail (GTK_IS_BUTTON (widget));

675 676
  GTK_WIDGET_CLASS (parent_class)->map (widget);

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
  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
694
static void
695
gtk_button_get_props (GtkButton *button,
696 697
		      GtkBorder *default_border,
		      GtkBorder *default_outside_border,
698
		      gboolean  *interior_focus)
Elliot Lee's avatar
Elliot Lee committed
699
{
700
  GtkWidget *widget =  GTK_WIDGET (button);
701 702 703 704 705 706 707 708 709 710 711 712 713 714
  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
715

716 717 718 719 720 721 722 723 724 725 726 727
  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
728

729 730 731 732 733 734 735 736 737
  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);
738
  GtkBorder default_border;
739
  gboolean interior_focus;
740 741
  gint focus_width;
  gint focus_pad;
Elliot Lee's avatar
Elliot Lee committed
742

743
  gtk_button_get_props (button, &default_border, NULL, &interior_focus);
744 745 746 747 748
  gtk_widget_style_get (GTK_WIDGET (widget),
			"focus-line-width", &focus_width,
			"focus-padding", &focus_pad,
			NULL);
 
Elliot Lee's avatar
Elliot Lee committed
749
  requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
750
			GTK_WIDGET (widget)->style->xthickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
751
  requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
752
			 GTK_WIDGET (widget)->style->ythickness) * 2;
Elliot Lee's avatar
Elliot Lee committed
753 754 755

  if (GTK_WIDGET_CAN_DEFAULT (widget))
    {
756 757
      requisition->width += default_border.left + default_border.right;
      requisition->height += default_border.top + default_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
758 759
    }

760
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
761
    {
762
      GtkRequisition child_requisition;
Elliot Lee's avatar
Elliot Lee committed
763

764 765 766 767
      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
768
    }
769 770 771
  
  requisition->width += 2 * (focus_width + focus_pad);
  requisition->height += 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
772 773 774 775 776 777
}

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

781 782 783
  gint border_width = GTK_CONTAINER (widget)->border_width;
  gint xthickness = GTK_WIDGET (widget)->style->xthickness;
  gint ythickness = GTK_WIDGET (widget)->style->ythickness;
784
  GtkBorder default_border;
Elliot Lee's avatar
Elliot Lee committed
785

786
  gtk_button_get_props (button, &default_border, NULL, NULL);
787
			    
Elliot Lee's avatar
Elliot Lee committed
788 789 790
  widget->allocation = *allocation;

  if (GTK_WIDGET_REALIZED (widget))
791
    gdk_window_move_resize (button->event_window,
Elliot Lee's avatar
Elliot Lee committed
792 793 794 795 796
			    widget->allocation.x + border_width,
			    widget->allocation.y + border_width,
			    widget->allocation.width - border_width * 2,
			    widget->allocation.height - border_width * 2);

797
  if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
Elliot Lee's avatar
Elliot Lee committed
798
    {
799 800 801 802 803 804 805
      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
806 807 808

      if (GTK_WIDGET_CAN_DEFAULT (button))
	{
809 810 811 812
	  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
813 814
	}

815 816 817 818 819 820 821 822 823 824 825 826 827
      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;
	}

828
      gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
Elliot Lee's avatar
Elliot Lee committed
829 830 831
    }
}

832 833 834 835 836 837 838
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
839
{
840
  GtkWidget *widget;
Elliot Lee's avatar
Elliot Lee committed
841 842
  gint width, height;
  gint x, y;
843
  gint border_width;
844 845
  GtkBorder default_border;
  GtkBorder default_outside_border;
846
  gboolean interior_focus;
847 848
  gint focus_width;
  gint focus_pad;
849
   
850
  if (GTK_WIDGET_DRAWABLE (button))
Elliot Lee's avatar
Elliot Lee committed
851
    {
852
      widget = GTK_WIDGET (button);
853
      border_width = GTK_CONTAINER (widget)->border_width;
854

855
      gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
856 857 858 859
      gtk_widget_style_get (GTK_WIDGET (widget),
			    "focus-line-width", &focus_width,
			    "focus-padding", &focus_pad,
			    NULL); 
860
	
861 862 863 864
      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;
865 866 867 868 869 870 871 872 873

      if (GTK_WIDGET_HAS_DEFAULT (widget) &&
	  GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL)
	{
	  gtk_paint_box (widget->style, widget->window,
			 GTK_STATE_NORMAL, GTK_SHADOW_IN,
			 area, widget, "buttondefault",
			 x, y, width, height);

874 875 876 877 878 879
	  x += default_border.left;
	  y += default_border.top;
	  width -= default_border.left + default_border.right;
	  height -= default_border.top + default_border.bottom;
	}
      else if (GTK_WIDGET_CAN_DEFAULT (widget))
Elliot Lee's avatar
Elliot Lee committed
880
	{
881 882 883 884
	  x += default_outside_border.left;
	  y += default_outside_border.top;
	  width -= default_outside_border.left + default_outside_border.right;
	  height -= default_outside_border.top + default_outside_border.bottom;
Elliot Lee's avatar
Elliot Lee committed
885
	}
886
       
887
      if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
888
	{
889 890 891 892
	  x += focus_width + focus_pad;
	  y += focus_width + focus_pad;
	  width -= 2 * (focus_width + focus_pad);
	  height -= 2 * (focus_width + focus_pad);
Elliot Lee's avatar
Elliot Lee committed
893
	}
894

895 896 897 898
      if ((button->relief != GTK_RELIEF_NONE) ||
	  ((GTK_WIDGET_STATE(widget) != GTK_STATE_NORMAL) &&
	   (GTK_WIDGET_STATE(widget) != GTK_STATE_INSENSITIVE)))
	gtk_paint_box (widget->style, widget->window,
899
		       state_type,
900 901 902
		       shadow_type, area, widget, "button",
		       x, y, width, height);
       
Elliot Lee's avatar
Elliot Lee committed
903 904
      if (GTK_WIDGET_HAS_FOCUS (widget))
	{
905 906
	  if (interior_focus)
	    {
907 908 909 910
	      x += widget->style->xthickness + focus_pad;
	      y += widget->style->ythickness + focus_pad;
	      width -= 2 * (widget->style->xthickness + focus_pad);
	      height -=  2 * (widget->style->xthickness + focus_pad);
911 912 913
	    }
	  else
	    {
914 915 916 917
	      x -= focus_width + focus_pad;
	      y -= focus_width + focus_pad;
	      width += 2 * (focus_width + focus_pad);
	      height += 2 * (focus_width + focus_pad);
918
	    }
Elliot Lee's avatar
Elliot Lee committed
919

920
	  gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
921
			   area, widget, "button",
922
			   x, y, width, height);
Elliot Lee's avatar
Elliot Lee committed
923 924 925 926
	}
    }
}

927
static gboolean
Elliot Lee's avatar
Elliot Lee committed
928 929 930 931 932
gtk_button_expose (GtkWidget      *widget,
		   GdkEventExpose *event)
{
  if (GTK_WIDGET_DRAWABLE (widget))
    {
933
      GtkButton *button = GTK_BUTTON (widget);
934
      
935 936 937 938
      _gtk_button_paint (button, &event->area,
			 GTK_WIDGET_STATE (widget),
			 button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
			 "button", "buttondefault");
939 940
      
      (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
Elliot Lee's avatar
Elliot Lee committed
941
    }
942
  
Elliot Lee's avatar
Elliot Lee committed
943 944 945
  return FALSE;
}

946
static gboolean
Elliot Lee's avatar
Elliot Lee committed
947 948 949 950 951 952 953 954 955
gtk_button_button_press (GtkWidget      *widget,
			 GdkEventButton *event)
{
  GtkButton *button;

  if (event->type == GDK_BUTTON_PRESS)
    {
      button = GTK_BUTTON (widget);

Soeren Sandmann's avatar
Soeren Sandmann committed
956
      if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
957 958 959
	gtk_widget_grab_focus (widget);

      if (event->button == 1)
960
	gtk_button_pressed (button);
Elliot Lee's avatar
Elliot Lee committed
961 962 963 964 965
    }

  return TRUE;
}

966
static gboolean
Elliot Lee's avatar
Elliot Lee committed
967 968 969 970 971 972 973 974 975 976 977 978 979 980
gtk_button_button_release (GtkWidget      *widget,
			   GdkEventButton *event)
{
  GtkButton *button;

  if (event->button == 1)
    {
      button = GTK_BUTTON (widget);
      gtk_button_released (button);
    }

  return TRUE;
}

981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
static gboolean
gtk_button_key_release (GtkWidget   *widget,
			GdkEventKey *event)
{
  GtkButton *button = GTK_BUTTON (widget);

  if (button->activate_timeout)
    {
      gtk_button_finish_activate (button, TRUE);
      return TRUE;
    }
  else if (GTK_WIDGET_CLASS (parent_class)->key_release_event)
    return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
  else
    return FALSE;
}

998
static gboolean
Elliot Lee's avatar
Elliot Lee committed
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
gtk_button_enter_notify (GtkWidget        *widget,
			 GdkEventCrossing *event)
{
  GtkButton *button;
  GtkWidget *event_widget;

  button = GTK_BUTTON (widget);
  event_widget = gtk_get_event_widget ((GdkEvent*) event);

  if ((event_widget == widget) &&
      (event->detail != GDK_NOTIFY_INFERIOR))
    {
      button->in_button = TRUE;
      gtk_button_enter (button);
    }

  return FALSE;
}

1018
static gboolean
Elliot Lee's avatar
Elliot Lee committed
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
gtk_button_leave_notify (GtkWidget        *widget,
			 GdkEventCrossing *event)
{
  GtkButton *button;
  GtkWidget *event_widget;

  button = GTK_BUTTON (widget);
  event_widget = gtk_get_event_widget ((GdkEvent*) event);

  if ((event_widget == widget) &&
      (event->detail != GDK_NOTIFY_INFERIOR))
    {
      button->in_button = FALSE;
      gtk_button_leave (button);
    }

  return FALSE;
}

static void
gtk_real_button_pressed (GtkButton *button)
{
1041 1042 1043
  if (button->activate_timeout)
    return;
  
Elliot Lee's avatar
Elliot Lee committed
1044
  button->button_down = TRUE;
1045
  gtk_button_update_state (button);
Elliot Lee's avatar
Elliot Lee committed
1046 1047 1048 1049 1050 1051 1052 1053 1054
}

static void
gtk_real_button_released (GtkButton *button)
{
  if (button->button_down)
    {
      button->button_down <