gtkbutton.c 37.4 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 633 634 635 636 637 638
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      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);

875 876 877 878 879 880
	  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
881
	{
882 883 884 885
	  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
886
	}
887
       
888
      if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
889
	{
890 891 892 893
	  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
894
	}
895

896 897 898 899
      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,
900
		       state_type,
901 902 903
		       shadow_type, area, widget, "button",
		       x, y, width, height);
       
Elliot Lee's avatar
Elliot Lee committed
904 905
      if (GTK_WIDGET_HAS_FOCUS (widget))
	{
906 907
	  if (interior_focus)
	    {
908 909 910 911
	      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);
912 913 914
	    }
	  else
	    {
915 916 917 918
	      x -= focus_width + focus_pad;
	      y -= focus_width + focus_pad;
	      width += 2 * (focus_width + focus_pad);
	      height += 2 * (focus_width + focus_pad);
919
	    }
Elliot Lee's avatar
Elliot Lee committed
920

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

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

947
static gboolean
Elliot Lee's avatar
Elliot Lee committed
948 949 950 951 952 953 954 955 956
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
957
      if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget))
Elliot Lee's avatar
Elliot Lee committed
958 959 960
	gtk_widget_grab_focus (widget);

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

  return TRUE;
}

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

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

  return TRUE;
}

982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
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;
}

999
static gboolean
Elliot Lee's avatar
Elliot Lee committed
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
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;
}

1019
static gboolean
Elliot Lee's avatar
Elliot Lee committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
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)
{
1042 1043 1044
  if (button->activate_timeout)
    return;
  
Elliot Lee's avatar
Elliot Lee committed
1045
  button->button_down = TRUE;
1046
  gtk_button_update_state (button);
Elliot Lee's avatar
Elliot Lee committed
1047 1048 1049 1050 1051 1052 1053 1054 1055
}

static void
gtk_real_button_released (GtkButton *button)
{
  if (button->button_down)
    {
      button->button_down = FALSE;

1056