]> Pileus Git - ~andy/gtk/blob - gtk/gtkbutton.c
Oct 26 20:55:57 2001 Owen Taylor <otaylor@redhat.com>
[~andy/gtk] / gtk / gtkbutton.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <string.h>
28 #include "gtkalignment.h"
29 #include "gtkbutton.h"
30 #include "gtklabel.h"
31 #include "gtkmain.h"
32 #include "gtksignal.h"
33 #include "gtkimage.h"
34 #include "gtkhbox.h"
35 #include "gtkstock.h"
36 #include "gtkiconfactory.h"
37 #include "gtkintl.h"
38
39 #define CHILD_SPACING     1
40
41 static GtkBorder default_default_border = { 1, 1, 1, 1 };
42 static GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
43
44 /* Time out before giving up on getting a key release when animatng
45  * the close button.
46  */
47 #define ACTIVATE_TIMEOUT 250
48
49 enum {
50   PRESSED,
51   RELEASED,
52   CLICKED,
53   ENTER,
54   LEAVE,
55   ACTIVATE,
56   LAST_SIGNAL
57 };
58
59 enum {
60   PROP_0,
61   PROP_LABEL,
62   PROP_RELIEF,
63   PROP_USE_UNDERLINE,
64   PROP_USE_STOCK
65 };
66
67 static void gtk_button_class_init     (GtkButtonClass   *klass);
68 static void gtk_button_init           (GtkButton        *button);
69 static void gtk_button_set_property   (GObject         *object,
70                                        guint            prop_id,
71                                        const GValue    *value,
72                                        GParamSpec      *pspec);
73 static void gtk_button_get_property   (GObject         *object,
74                                        guint            prop_id,
75                                        GValue          *value,
76                                        GParamSpec      *pspec);
77 static void gtk_button_realize        (GtkWidget        *widget);
78 static void gtk_button_unrealize      (GtkWidget        *widget);
79 static void gtk_button_size_request   (GtkWidget        *widget,
80                                        GtkRequisition   *requisition);
81 static void gtk_button_size_allocate  (GtkWidget        *widget,
82                                        GtkAllocation    *allocation);
83 static void gtk_button_paint          (GtkWidget        *widget,
84                                        GdkRectangle     *area);
85 static gint gtk_button_expose         (GtkWidget        *widget,
86                                        GdkEventExpose   *event);
87 static gint gtk_button_button_press   (GtkWidget        *widget,
88                                        GdkEventButton   *event);
89 static gint gtk_button_button_release (GtkWidget        *widget,
90                                        GdkEventButton   *event);
91 static gint gtk_button_key_release    (GtkWidget        *widget,
92                                        GdkEventKey      *event);
93 static gint gtk_button_enter_notify   (GtkWidget        *widget,
94                                        GdkEventCrossing *event);
95 static gint gtk_button_leave_notify   (GtkWidget        *widget,
96                                        GdkEventCrossing *event);
97 static void gtk_real_button_pressed   (GtkButton        *button);
98 static void gtk_real_button_released  (GtkButton        *button);
99 static void gtk_real_button_activate (GtkButton         *button);
100 static void gtk_button_update_state   (GtkButton        *button);
101 static GtkType gtk_button_child_type  (GtkContainer     *container);
102 static void gtk_button_finish_activate (GtkButton *button,
103                                         gboolean   do_it);
104
105 static GObject* gtk_button_constructor     (GType                  type,
106                                             guint                  n_construct_properties,
107                                             GObjectConstructParam *construct_params);
108 static void     gtk_button_construct_child (GtkButton             *button);
109
110
111 static GtkBinClass *parent_class = NULL;
112 static guint button_signals[LAST_SIGNAL] = { 0 };
113
114
115 GtkType
116 gtk_button_get_type (void)
117 {
118   static GtkType button_type = 0;
119
120   if (!button_type)
121     {
122       static const GTypeInfo button_info =
123       {
124         sizeof (GtkButtonClass),
125         NULL,           /* base_init */
126         NULL,           /* base_finalize */
127         (GClassInitFunc) gtk_button_class_init,
128         NULL,           /* class_finalize */
129         NULL,           /* class_data */
130         sizeof (GtkButton),
131         16,             /* n_preallocs */
132         (GInstanceInitFunc) gtk_button_init,
133       };
134
135       button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton", &button_info, 0);
136     }
137
138   return button_type;
139 }
140
141 static void
142 gtk_button_class_init (GtkButtonClass *klass)
143 {
144   GObjectClass *g_object_class;
145   GtkObjectClass *object_class;
146   GtkWidgetClass *widget_class;
147   GtkContainerClass *container_class;
148
149   g_object_class = G_OBJECT_CLASS (klass);
150   object_class = (GtkObjectClass*) klass;
151   widget_class = (GtkWidgetClass*) klass;
152   container_class = (GtkContainerClass*) klass;
153   
154   parent_class = g_type_class_peek_parent (klass);
155
156   g_object_class->constructor = gtk_button_constructor;
157   g_object_class->set_property = gtk_button_set_property;
158   g_object_class->get_property = gtk_button_get_property;
159
160   widget_class->realize = gtk_button_realize;
161   widget_class->unrealize = gtk_button_unrealize;
162   widget_class->size_request = gtk_button_size_request;
163   widget_class->size_allocate = gtk_button_size_allocate;
164   widget_class->expose_event = gtk_button_expose;
165   widget_class->button_press_event = gtk_button_button_press;
166   widget_class->button_release_event = gtk_button_button_release;
167   widget_class->key_release_event = gtk_button_key_release;
168   widget_class->enter_notify_event = gtk_button_enter_notify;
169   widget_class->leave_notify_event = gtk_button_leave_notify;
170
171   container_class->child_type = gtk_button_child_type;
172
173   klass->pressed = gtk_real_button_pressed;
174   klass->released = gtk_real_button_released;
175   klass->clicked = NULL;
176   klass->enter = gtk_button_update_state;
177   klass->leave = gtk_button_update_state;
178   klass->activate = gtk_real_button_activate;
179
180   g_object_class_install_property (G_OBJECT_CLASS(object_class),
181                                    PROP_LABEL,
182                                    g_param_spec_string ("label",
183                                                         _("Label"),
184                                                         _("Text of the label widget inside the button, if the button contains a label widget."),
185                                                         NULL,
186                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
187   
188   g_object_class_install_property (G_OBJECT_CLASS(object_class),
189                                    PROP_USE_UNDERLINE,
190                                    g_param_spec_boolean ("use_underline",
191                                                          _("Use underline"),
192                                                          _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
193                                                         FALSE,
194                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
195   
196   g_object_class_install_property (G_OBJECT_CLASS(object_class),
197                                    PROP_USE_STOCK,
198                                    g_param_spec_boolean ("use_stock",
199                                                          _("Use stock"),
200                                                          _("If set, the label is used to pick a stock item instead of being displayed"),
201                                                         FALSE,
202                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
203   
204   g_object_class_install_property (G_OBJECT_CLASS(object_class),
205                                    PROP_RELIEF,
206                                    g_param_spec_enum ("relief",
207                                                       _("Border relief"),
208                                                       _("The border relief style."),
209                                                       GTK_TYPE_RELIEF_STYLE,
210                                                       GTK_RELIEF_NORMAL,
211                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
212
213   button_signals[PRESSED] =
214     gtk_signal_new ("pressed",
215                     GTK_RUN_FIRST,
216                     GTK_CLASS_TYPE (object_class),
217                     GTK_SIGNAL_OFFSET (GtkButtonClass, pressed),
218                     gtk_marshal_VOID__VOID,
219                     GTK_TYPE_NONE, 0);
220   button_signals[RELEASED] =
221     gtk_signal_new ("released",
222                     GTK_RUN_FIRST,
223                     GTK_CLASS_TYPE (object_class),
224                     GTK_SIGNAL_OFFSET (GtkButtonClass, released),
225                     gtk_marshal_VOID__VOID,
226                     GTK_TYPE_NONE, 0);
227   button_signals[CLICKED] =
228     gtk_signal_new ("clicked",
229                     GTK_RUN_FIRST | GTK_RUN_ACTION,
230                     GTK_CLASS_TYPE (object_class),
231                     GTK_SIGNAL_OFFSET (GtkButtonClass, clicked),
232                     gtk_marshal_VOID__VOID,
233                     GTK_TYPE_NONE, 0);
234   button_signals[ENTER] =
235     gtk_signal_new ("enter",
236                     GTK_RUN_FIRST,
237                     GTK_CLASS_TYPE (object_class),
238                     GTK_SIGNAL_OFFSET (GtkButtonClass, enter),
239                     gtk_marshal_VOID__VOID,
240                     GTK_TYPE_NONE, 0);
241   button_signals[LEAVE] =
242     gtk_signal_new ("leave",
243                     GTK_RUN_FIRST,
244                     GTK_CLASS_TYPE (object_class),
245                     GTK_SIGNAL_OFFSET (GtkButtonClass, leave),
246                     gtk_marshal_VOID__VOID,
247                     GTK_TYPE_NONE, 0);
248   button_signals[ACTIVATE] =
249     gtk_signal_new ("activate",
250                     GTK_RUN_FIRST,
251                     GTK_CLASS_TYPE (object_class),
252                     GTK_SIGNAL_OFFSET (GtkButtonClass, activate),
253                     gtk_marshal_VOID__VOID,
254                     GTK_TYPE_NONE, 0);
255   widget_class->activate_signal = button_signals[ACTIVATE];
256
257   gtk_widget_class_install_style_property (widget_class,
258                                            g_param_spec_boxed ("default_border",
259                                                                _("Default Spacing"),
260                                                                _("Extra space to add for CAN_DEFAULT buttons"),
261                                                                GTK_TYPE_BORDER,
262                                                                G_PARAM_READABLE));
263
264   gtk_widget_class_install_style_property (widget_class,
265                                            g_param_spec_boxed ("default_outside_border",
266                                                                _("Default Outside Spacing"),
267                                                                _("Extra space to add for CAN_DEFAULT buttons that is always drawn outside the border"),
268                                                                GTK_TYPE_BORDER,
269                                                                G_PARAM_READABLE));
270   gtk_widget_class_install_style_property (widget_class,
271                                            g_param_spec_int ("child_displacement_x",
272                                                              _("Child X Displacement"),
273                                                              _("How far in the x direction to move the child when the button is depressed"),
274                                                              G_MININT,
275                                                              G_MAXINT,
276                                                              0,
277                                                              G_PARAM_READABLE));
278   gtk_widget_class_install_style_property (widget_class,
279                                            g_param_spec_int ("child_displacement_y",
280                                                              _("Child Y Displacement"),
281                                                              _("How far in the y direction to move the child when the button is depressed"),
282                                                              G_MININT,
283                                                              G_MAXINT,
284                                                              0,
285                                                              G_PARAM_READABLE));
286 }
287
288 static void
289 gtk_button_init (GtkButton *button)
290 {
291   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
292   GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);
293
294   button->label_text = NULL;
295   
296   button->constructed = FALSE;
297   button->in_button = FALSE;
298   button->button_down = FALSE;
299   button->relief = GTK_RELIEF_NORMAL;
300   button->use_stock = FALSE;
301   button->use_underline = FALSE;
302   button->depressed = FALSE;
303 }
304
305 static GObject*
306 gtk_button_constructor (GType                  type,
307                         guint                  n_construct_properties,
308                         GObjectConstructParam *construct_params)
309 {
310   GObject *object;
311   GtkButton *button;
312
313   object = (* G_OBJECT_CLASS (parent_class)->constructor) (type,
314                                                            n_construct_properties,
315                                                            construct_params);
316
317   button = GTK_BUTTON (object);
318   button->constructed = TRUE;
319
320   if (button->label_text != NULL)
321     gtk_button_construct_child (button);
322   
323   return object;
324 }
325
326
327 static GtkType
328 gtk_button_child_type  (GtkContainer     *container)
329 {
330   if (!GTK_BIN (container)->child)
331     return GTK_TYPE_WIDGET;
332   else
333     return GTK_TYPE_NONE;
334 }
335
336 static void
337 gtk_button_set_property (GObject         *object,
338                          guint            prop_id,
339                          const GValue    *value,
340                          GParamSpec      *pspec)
341 {
342   GtkButton *button;
343
344   button = GTK_BUTTON (object);
345
346   switch (prop_id)
347     {
348     case PROP_LABEL:
349       gtk_button_set_label (button, g_value_get_string (value));
350       break;
351     case PROP_RELIEF:
352       gtk_button_set_relief (button, g_value_get_enum (value));
353       break;
354     case PROP_USE_UNDERLINE:
355       gtk_button_set_use_underline (button, g_value_get_boolean (value));
356       break;
357     case PROP_USE_STOCK:
358       gtk_button_set_use_stock (button, g_value_get_boolean (value));
359       break;
360     default:
361       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362       break;
363     }
364 }
365
366 static void
367 gtk_button_get_property (GObject         *object,
368                          guint            prop_id,
369                          GValue          *value,
370                          GParamSpec      *pspec)
371 {
372   GtkButton *button;
373
374   button = GTK_BUTTON (object);
375
376   switch (prop_id)
377     {
378     case PROP_LABEL:
379       g_value_set_string (value, button->label_text);
380       break;
381     case PROP_RELIEF:
382       g_value_set_enum (value, gtk_button_get_relief (button));
383       break;
384     case PROP_USE_UNDERLINE:
385       g_value_set_boolean (value, button->use_underline);
386       break;
387     case PROP_USE_STOCK:
388       g_value_set_boolean (value, button->use_stock);
389       break;
390     default:
391       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392       break;
393     }
394 }
395
396 GtkWidget*
397 gtk_button_new (void)
398 {
399   return GTK_WIDGET (gtk_type_new (gtk_button_get_type ()));
400 }
401
402 static void
403 gtk_button_construct_child (GtkButton *button)
404 {
405   GtkStockItem item;
406   GtkWidget *label;
407   GtkWidget *image;
408   GtkWidget *hbox;
409   GtkWidget *align;
410
411   if (!button->constructed)
412     return;
413   
414   if (button->label_text == NULL)
415     return;
416
417   if (GTK_BIN (button)->child)
418     gtk_container_remove (GTK_CONTAINER (button),
419                           GTK_BIN (button)->child);
420
421   
422   if (button->use_stock &&
423       gtk_stock_lookup (button->label_text, &item))
424     {
425       label = gtk_label_new_with_mnemonic (item.label);
426
427       gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
428       
429       image = gtk_image_new_from_stock (button->label_text, GTK_ICON_SIZE_BUTTON);
430       hbox = gtk_hbox_new (FALSE, 2);
431
432       align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
433       
434       gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
435       gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
436       
437       gtk_container_add (GTK_CONTAINER (button), align);
438       gtk_container_add (GTK_CONTAINER (align), hbox);
439       gtk_widget_show_all (align);
440
441       return;
442     }
443
444   if (button->use_underline)
445     {
446       label = gtk_label_new_with_mnemonic (button->label_text);
447       gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
448     }
449   else
450     label = gtk_label_new (button->label_text);
451   
452   gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
453
454   gtk_widget_show (label);
455   gtk_container_add (GTK_CONTAINER (button), label);
456 }
457
458
459 GtkWidget*
460 gtk_button_new_with_label (const gchar *label)
461 {
462   return g_object_new (GTK_TYPE_BUTTON, "label", label, NULL);
463 }
464
465 /**
466  * gtk_button_new_from_stock:
467  * @stock_id: the name of the stock item 
468  *
469  * Creates a new #GtkButton containing the image and text from a stock item.
470  * Some stock ids have preprocessor macros like #GTK_STOCK_OK and
471  * #GTK_STOCK_APPLY.
472  *
473  * If @stock_id is unknown, then it will be treated as a mnemonic
474  * label (as for gtk_button_new_with_mnemonic()).
475  *
476  * Returns: a new #GtkButton
477  **/
478 GtkWidget*
479 gtk_button_new_from_stock (const gchar *stock_id)
480 {
481   return g_object_new (GTK_TYPE_BUTTON,
482                        "label", stock_id,
483                        "use_stock", TRUE,
484                        "use_underline", TRUE,
485                        NULL);
486 }
487
488 /**
489  * gtk_button_new_with_mnemonic:
490  * @label: The text of the button, with an underscore in front of the
491  *         mnemonic character
492  * @returns: a new #GtkButton
493  *
494  * Creates a new #GtkButton containing a label.
495  * If characters in @label are preceded by an underscore, they are underlined
496  * indicating that they represent a keyboard accelerator called a mnemonic.
497  * Pressing Alt and that key activates the button.
498  **/
499 GtkWidget*
500 gtk_button_new_with_mnemonic (const gchar *label)
501 {
502   return g_object_new (GTK_TYPE_BUTTON, "label", label, "use_underline", TRUE,  NULL);
503 }
504
505 void
506 gtk_button_pressed (GtkButton *button)
507 {
508   g_return_if_fail (GTK_IS_BUTTON (button));
509
510   gtk_signal_emit (GTK_OBJECT (button), button_signals[PRESSED]);
511 }
512
513 void
514 gtk_button_released (GtkButton *button)
515 {
516   g_return_if_fail (GTK_IS_BUTTON (button));
517
518   gtk_signal_emit (GTK_OBJECT (button), button_signals[RELEASED]);
519 }
520
521 void
522 gtk_button_clicked (GtkButton *button)
523 {
524   g_return_if_fail (GTK_IS_BUTTON (button));
525
526   gtk_signal_emit (GTK_OBJECT (button), button_signals[CLICKED]);
527 }
528
529 void
530 gtk_button_enter (GtkButton *button)
531 {
532   g_return_if_fail (GTK_IS_BUTTON (button));
533
534   gtk_signal_emit (GTK_OBJECT (button), button_signals[ENTER]);
535 }
536
537 void
538 gtk_button_leave (GtkButton *button)
539 {
540   g_return_if_fail (GTK_IS_BUTTON (button));
541
542   gtk_signal_emit (GTK_OBJECT (button), button_signals[LEAVE]);
543 }
544
545 void
546 gtk_button_set_relief (GtkButton *button,
547                        GtkReliefStyle newrelief)
548 {
549   g_return_if_fail (GTK_IS_BUTTON (button));
550
551   button->relief = newrelief;
552   g_object_notify(G_OBJECT(button), "relief");
553   gtk_widget_queue_draw (GTK_WIDGET (button));
554 }
555
556 GtkReliefStyle
557 gtk_button_get_relief (GtkButton *button)
558 {
559   g_return_val_if_fail (button != NULL, GTK_RELIEF_NORMAL);
560   g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);
561
562   return button->relief;
563 }
564
565 static void
566 gtk_button_realize (GtkWidget *widget)
567 {
568   GtkButton *button;
569   GdkWindowAttr attributes;
570   gint attributes_mask;
571   gint border_width;
572
573   g_return_if_fail (GTK_IS_BUTTON (widget));
574
575   button = GTK_BUTTON (widget);
576   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
577
578   border_width = GTK_CONTAINER (widget)->border_width;
579
580   attributes.window_type = GDK_WINDOW_CHILD;
581   attributes.x = widget->allocation.x + border_width;
582   attributes.y = widget->allocation.y + border_width;
583   attributes.width = widget->allocation.width - border_width * 2;
584   attributes.height = widget->allocation.height - border_width * 2;
585   attributes.wclass = GDK_INPUT_OUTPUT;
586   attributes.visual = gtk_widget_get_visual (widget);
587   attributes.colormap = gtk_widget_get_colormap (widget);
588   attributes.event_mask = gtk_widget_get_events (widget);
589   attributes.event_mask |= (GDK_EXPOSURE_MASK |
590                             GDK_BUTTON_PRESS_MASK |
591                             GDK_BUTTON_RELEASE_MASK |
592                             GDK_ENTER_NOTIFY_MASK |
593                             GDK_LEAVE_NOTIFY_MASK);
594
595   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
596
597   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
598   gdk_window_set_user_data (widget->window, button);
599
600   widget->style = gtk_style_attach (widget->style, widget->window);
601   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
602 }
603
604 static void
605 gtk_button_unrealize (GtkWidget *widget)
606 {
607   GtkButton *button = GTK_BUTTON (widget);
608
609   if (button->activate_timeout)
610     gtk_button_finish_activate (button, FALSE);
611     
612   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
613 }
614
615 static void
616 gtk_button_get_props (GtkButton *button,
617                       GtkBorder *default_border,
618                       GtkBorder *default_outside_border,
619                       gboolean  *interior_focus)
620 {
621   GtkWidget *widget =  GTK_WIDGET (button);
622   GtkBorder *tmp_border;
623
624   if (default_border)
625     {
626       gtk_widget_style_get (widget, "default_border", &tmp_border, NULL);
627
628       if (tmp_border)
629         {
630           *default_border = *tmp_border;
631           g_free (tmp_border);
632         }
633       else
634         *default_border = default_default_border;
635     }
636
637   if (default_outside_border)
638     {
639       gtk_widget_style_get (widget, "default_outside_border", &tmp_border, NULL);
640
641       if (tmp_border)
642         {
643           *default_outside_border = *tmp_border;
644           g_free (tmp_border);
645         }
646       else
647         *default_outside_border = default_default_outside_border;
648     }
649
650   if (interior_focus)
651     gtk_widget_style_get (widget, "interior_focus", interior_focus, NULL);
652 }
653         
654 static void
655 gtk_button_size_request (GtkWidget      *widget,
656                          GtkRequisition *requisition)
657 {
658   GtkButton *button = GTK_BUTTON (widget);
659   GtkBorder default_border;
660   gboolean interior_focus;
661
662   gtk_button_get_props (button, &default_border, NULL, &interior_focus);
663   
664   requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
665                         GTK_WIDGET (widget)->style->xthickness) * 2;
666   requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
667                          GTK_WIDGET (widget)->style->ythickness) * 2;
668
669   if (GTK_WIDGET_CAN_DEFAULT (widget))
670     {
671       requisition->width += default_border.left + default_border.right;
672       requisition->height += default_border.top + default_border.bottom;
673     }
674
675   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
676     {
677       GtkRequisition child_requisition;
678
679       gtk_widget_size_request (GTK_BIN (button)->child, &child_requisition);
680
681       requisition->width += child_requisition.width;
682       requisition->height += child_requisition.height;
683     }
684
685   if (interior_focus)
686     {
687       requisition->width += 2;
688       requisition->height += 2;
689     }
690 }
691
692 static void
693 gtk_button_size_allocate (GtkWidget     *widget,
694                           GtkAllocation *allocation)
695 {
696   GtkButton *button = GTK_BUTTON (widget);
697   GtkAllocation child_allocation;
698
699   gint border_width = GTK_CONTAINER (widget)->border_width;
700   gint xthickness = GTK_WIDGET (widget)->style->xthickness;
701   gint ythickness = GTK_WIDGET (widget)->style->ythickness;
702   GtkBorder default_border;
703
704   gtk_button_get_props (button, &default_border, NULL, NULL);
705   
706   widget->allocation = *allocation;
707
708   if (GTK_WIDGET_REALIZED (widget))
709     gdk_window_move_resize (widget->window,
710                             widget->allocation.x + border_width,
711                             widget->allocation.y + border_width,
712                             widget->allocation.width - border_width * 2,
713                             widget->allocation.height - border_width * 2);
714
715   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
716     {
717       child_allocation.x = (CHILD_SPACING + xthickness);
718       child_allocation.y = (CHILD_SPACING + ythickness);
719
720       child_allocation.width = MAX (1, (gint)widget->allocation.width - child_allocation.x * 2 -
721                                  border_width * 2);
722       child_allocation.height = MAX (1, (gint)widget->allocation.height - child_allocation.y * 2 -
723                                   border_width * 2);
724
725       if (GTK_WIDGET_CAN_DEFAULT (button))
726         {
727           child_allocation.x += default_border.left;
728           child_allocation.y += default_border.top;
729           child_allocation.width =  MAX (1, child_allocation.width - default_border.left - default_border.right);
730           child_allocation.height = MAX (1, child_allocation.height - default_border.top - default_border.bottom);
731         }
732
733       if (button->depressed)
734         {
735           gint child_displacement_x;
736           gint child_displacement_y;
737           
738           gtk_widget_style_get (widget,
739                                 "child_displacement_x", &child_displacement_x, 
740                                 "child_displacement_y", &child_displacement_y,
741                                 NULL);
742           child_allocation.x += child_displacement_x;
743           child_allocation.y += child_displacement_y;
744         }
745
746       gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
747     }
748 }
749
750 /*
751  * +------------------------------------------------+
752  * |                   BORDER                       |
753  * |  +------------------------------------------+  |
754  * |  |\\\\\\\\\\\\\\\\DEFAULT\\\\\\\\\\\\\\\\\  |  |
755  * |  |\\+------------------------------------+  |  |
756  * |  |\\| |           SPACING       3      | |  |  |
757  * |  |\\| +--------------------------------+ |  |  |
758  * |  |\\| |########## FOCUS ###############| |  |  |
759  * |  |\\| |#+----------------------------+#| |  |  |
760  * |  |\\| |#|         RELIEF            \|#| |  |  |
761  * |  |\\| |#|  +-----------------------+\|#| |  |  |
762  * |  |\\|1|#|  +     THE TEXT          +\|#|2|  |  |
763  * |  |\\| |#|  +-----------------------+\|#| |  |  |
764  * |  |\\| |#| \\\\\ ythickness \\\\\\\\\\|#| |  |  |
765  * |  |\\| |#+----------------------------+#| |  |  |
766  * |  |\\| |########### 1 ##################| |  |  |
767  * |  |\\| +--------------------------------+ |  |  |
768  * |  |\\| |        default spacing   4     | |  |  |
769  * |  |\\+------------------------------------+  |  |
770  * |  |\            ythickness                   |  |
771  * |  +------------------------------------------+  |
772  * |                border_width                    |
773  * +------------------------------------------------+
774  */
775
776 static void
777 gtk_button_paint (GtkWidget    *widget,
778                   GdkRectangle *area)
779 {
780   GtkButton *button;
781   GtkShadowType shadow_type;
782   gint width, height;
783   gint x, y;
784   GtkBorder default_border;
785   GtkBorder default_outside_border;
786   gboolean interior_focus;
787    
788   if (GTK_WIDGET_DRAWABLE (widget))
789     {
790       button = GTK_BUTTON (widget);
791
792       gtk_button_get_props (button, &default_border, &default_outside_border, &interior_focus);
793         
794       x = 0;
795       y = 0;
796       width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
797       height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
798
799       gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
800       gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
801
802       if (GTK_WIDGET_HAS_DEFAULT (widget) &&
803           GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL)
804         {
805           gtk_paint_box (widget->style, widget->window,
806                          GTK_STATE_NORMAL, GTK_SHADOW_IN,
807                          area, widget, "buttondefault",
808                          x, y, width, height);
809
810           x += default_border.left;
811           y += default_border.top;
812           width -= default_border.left + default_border.right;
813           height -= default_border.top + default_border.bottom;
814         }
815       else if (GTK_WIDGET_CAN_DEFAULT (widget))
816         {
817           x += default_outside_border.left;
818           y += default_outside_border.top;
819           width -= default_outside_border.left + default_outside_border.right;
820           height -= default_outside_border.top + default_outside_border.bottom;
821         }
822        
823       if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
824         {
825           x += 1;
826           y += 1;
827           width -= 2;
828           height -= 2;
829         }
830
831       shadow_type = button->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
832
833       if ((button->relief != GTK_RELIEF_NONE) ||
834           ((GTK_WIDGET_STATE(widget) != GTK_STATE_NORMAL) &&
835            (GTK_WIDGET_STATE(widget) != GTK_STATE_INSENSITIVE)))
836         gtk_paint_box (widget->style, widget->window,
837                        GTK_WIDGET_STATE (widget),
838                        shadow_type, area, widget, "button",
839                        x, y, width, height);
840        
841       if (GTK_WIDGET_HAS_FOCUS (widget))
842         {
843           if (interior_focus)
844             {
845               x += widget->style->xthickness + 1;
846               y += widget->style->ythickness + 1;
847               width -= 2 * (widget->style->xthickness + 1);
848               height -=  2 * (widget->style->xthickness + 1);
849             }
850           else
851             {
852               x -= 1;
853               y -= 1;
854               width += 2;
855               height += 2;
856             }
857
858           gtk_paint_focus (widget->style, widget->window,
859                            area, widget, "button",
860                            x, y, width - 1, height - 1);
861         }
862     }
863 }
864
865 static gboolean
866 gtk_button_expose (GtkWidget      *widget,
867                    GdkEventExpose *event)
868 {
869   GtkBin *bin;
870
871   g_return_val_if_fail (widget != NULL, FALSE);
872   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
873   g_return_val_if_fail (event != NULL, FALSE);
874
875   if (GTK_WIDGET_DRAWABLE (widget))
876     {
877       bin = GTK_BIN (widget);
878       
879       gtk_button_paint (widget, &event->area);
880       
881       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
882     }
883   
884   return FALSE;
885 }
886
887 static gboolean
888 gtk_button_button_press (GtkWidget      *widget,
889                          GdkEventButton *event)
890 {
891   GtkButton *button;
892
893   g_return_val_if_fail (widget != NULL, FALSE);
894   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
895   g_return_val_if_fail (event != NULL, FALSE);
896
897   if (event->type == GDK_BUTTON_PRESS)
898     {
899       button = GTK_BUTTON (widget);
900
901       if (!GTK_WIDGET_HAS_FOCUS (widget))
902         gtk_widget_grab_focus (widget);
903
904       if (event->button == 1)
905         gtk_button_pressed (button);
906     }
907
908   return TRUE;
909 }
910
911 static gboolean
912 gtk_button_button_release (GtkWidget      *widget,
913                            GdkEventButton *event)
914 {
915   GtkButton *button;
916
917   g_return_val_if_fail (widget != NULL, FALSE);
918   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
919   g_return_val_if_fail (event != NULL, FALSE);
920
921   if (event->button == 1)
922     {
923       button = GTK_BUTTON (widget);
924       gtk_button_released (button);
925     }
926
927   return TRUE;
928 }
929
930 static gboolean
931 gtk_button_key_release (GtkWidget   *widget,
932                         GdkEventKey *event)
933 {
934   GtkButton *button = GTK_BUTTON (widget);
935
936   if (button->activate_timeout)
937     {
938       gtk_button_finish_activate (button, TRUE);
939       return TRUE;
940     }
941   else if (GTK_WIDGET_CLASS (parent_class)->key_release_event)
942     return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
943   else
944     return FALSE;
945 }
946
947 static gboolean
948 gtk_button_enter_notify (GtkWidget        *widget,
949                          GdkEventCrossing *event)
950 {
951   GtkButton *button;
952   GtkWidget *event_widget;
953
954   g_return_val_if_fail (widget != NULL, FALSE);
955   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
956   g_return_val_if_fail (event != NULL, FALSE);
957
958   button = GTK_BUTTON (widget);
959   event_widget = gtk_get_event_widget ((GdkEvent*) event);
960
961   if ((event_widget == widget) &&
962       (event->detail != GDK_NOTIFY_INFERIOR))
963     {
964       button->in_button = TRUE;
965       gtk_button_enter (button);
966     }
967
968   return FALSE;
969 }
970
971 static gboolean
972 gtk_button_leave_notify (GtkWidget        *widget,
973                          GdkEventCrossing *event)
974 {
975   GtkButton *button;
976   GtkWidget *event_widget;
977
978   g_return_val_if_fail (widget != NULL, FALSE);
979   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
980   g_return_val_if_fail (event != NULL, FALSE);
981
982   button = GTK_BUTTON (widget);
983   event_widget = gtk_get_event_widget ((GdkEvent*) event);
984
985   if ((event_widget == widget) &&
986       (event->detail != GDK_NOTIFY_INFERIOR))
987     {
988       button->in_button = FALSE;
989       gtk_button_leave (button);
990     }
991
992   return FALSE;
993 }
994
995 static void
996 gtk_real_button_pressed (GtkButton *button)
997 {
998   if (button->activate_timeout)
999     return;
1000   
1001   button->button_down = TRUE;
1002   gtk_button_update_state (button);
1003 }
1004
1005 static void
1006 gtk_real_button_released (GtkButton *button)
1007 {
1008   if (button->button_down)
1009     {
1010       button->button_down = FALSE;
1011
1012       if (button->activate_timeout)
1013         return;
1014       
1015       if (button->in_button)
1016         gtk_button_clicked (button);
1017
1018       gtk_button_update_state (button);
1019     }
1020 }
1021
1022 static gboolean
1023 button_activate_timeout (gpointer data)
1024 {
1025   GDK_THREADS_ENTER ();
1026   
1027   gtk_button_finish_activate (data, TRUE);
1028
1029   GDK_THREADS_LEAVE ();
1030
1031   return FALSE;
1032 }
1033
1034 static void
1035 gtk_real_button_activate (GtkButton *button)
1036 {
1037   GtkWidget *widget = GTK_WIDGET (button);
1038   
1039   g_return_if_fail (GTK_IS_BUTTON (button));
1040
1041   if (GTK_WIDGET_REALIZED (button) && !button->activate_timeout)
1042     {
1043       if (gdk_keyboard_grab (widget->window, TRUE,
1044                              gtk_get_current_event_time ()) == 0)
1045         {
1046           gtk_grab_add (widget);
1047           
1048           button->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT,
1049                                                     button_activate_timeout,
1050                                                     button);
1051           button->button_down = TRUE;
1052           gtk_button_update_state (button);
1053         }
1054     }
1055 }
1056
1057 static void
1058 gtk_button_finish_activate (GtkButton *button,
1059                             gboolean   do_it)
1060 {
1061   GtkWidget *widget = GTK_WIDGET (button);
1062   
1063   g_source_remove (button->activate_timeout);
1064   button->activate_timeout = 0;
1065
1066   gdk_keyboard_ungrab (gtk_get_current_event_time ());
1067   gtk_grab_remove (widget);
1068
1069   button->button_down = FALSE;
1070
1071   gtk_button_update_state (button);
1072
1073   if (do_it)
1074     gtk_button_clicked (button);
1075 }
1076
1077 /**
1078  * gtk_button_set_label:
1079  * @button: a #GtkButton
1080  * @label: a string
1081  *
1082  * Sets the text of the label of the button to @str. This text is
1083  * also used to select the stock item if gtk_button_set_use_stock()
1084  * is used.
1085  *
1086  * This will also clear any previously set labels.
1087  **/
1088 void
1089 gtk_button_set_label (GtkButton   *button,
1090                       const gchar *label)
1091 {
1092   g_return_if_fail (GTK_IS_BUTTON (button));
1093   
1094   g_free (button->label_text);
1095   button->label_text = g_strdup (label);
1096   
1097   gtk_button_construct_child (button);
1098   
1099   g_object_notify (G_OBJECT (button), "label");
1100 }
1101
1102 /**
1103  * gtk_button_get_label:
1104  * @button: a #GtkButton
1105  *
1106  * Fetches the text from the label of the button, as set by
1107  * gtk_button_set_label().
1108  *
1109  * Return value: the text of the label widget. This string is
1110  *   owned by the widget and must not be modified or freed.
1111  *   If the label text has not been set the return value
1112  *   will be NULL. This will be the case if you create an
1113  *   empty button with gtk_button_new() to use as a container.
1114  **/
1115 G_CONST_RETURN gchar *
1116 gtk_button_get_label (GtkButton *button)
1117 {
1118   g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
1119   
1120   return button->label_text;
1121 }
1122
1123 /**
1124  * gtk_button_set_use_underline:
1125  * @button: a #GtkButton
1126  * @use_underline: %TRUE if underlines in the text indicate mnemonics
1127  *
1128  * If true, an underline in the text of the button label indicates
1129  * the next character should be used for the mnemonic accelerator key.
1130  */
1131 void
1132 gtk_button_set_use_underline (GtkButton *button,
1133                               gboolean   use_underline)
1134 {
1135   g_return_if_fail (GTK_IS_BUTTON (button));
1136
1137   use_underline = use_underline != FALSE;
1138
1139   if (use_underline != button->use_underline)
1140     {
1141       button->use_underline = use_underline;
1142   
1143       gtk_button_construct_child (button);
1144       
1145       g_object_notify (G_OBJECT (button), "use_underline");
1146     }
1147 }
1148
1149 /**
1150  * gtk_button_get_use_underline:
1151  * @button: a #GtkButton
1152  *
1153  * Returns whether an embedded underline in the button label indicates a
1154  * mnemonic. See gtk_button_set_use_underline ().
1155  *
1156  * Return value: %TRUE if an embedded underline in the button label
1157  *               indicates the mnemonic accelerator keys.
1158  **/
1159 gboolean
1160 gtk_button_get_use_underline (GtkButton *button)
1161 {
1162   g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE);
1163   
1164   return button->use_underline;
1165 }
1166
1167 /**
1168  * gtk_button_set_use_stock:
1169  * @button: a #GtkButton
1170  * @use_stock: %TRUE if the button should use a stock item
1171  *
1172  * If true, the label set on the button is used as a
1173  * stock id to select the stock item for the button.
1174  */
1175 void
1176 gtk_button_set_use_stock (GtkButton *button,
1177                           gboolean   use_stock)
1178 {
1179   g_return_if_fail (GTK_IS_BUTTON (button));
1180
1181   use_stock = use_stock != FALSE;
1182
1183   if (use_stock != button->use_stock)
1184     {
1185       button->use_stock = use_stock;
1186   
1187       gtk_button_construct_child (button);
1188       
1189       g_object_notify (G_OBJECT (button), "use_stock");
1190     }
1191 }
1192
1193 /**
1194  * gtk_button_get_use_stock:
1195  * @button: a #GtkButton
1196  *
1197  * Returns whether the button label is a stock item.
1198  *
1199  * Return value: %TRUE if the button label is used to
1200  *               select a stock item instead of being
1201  *               used directly as the label text.
1202  */
1203 gboolean
1204 gtk_button_get_use_stock (GtkButton *button)
1205 {
1206   g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE);
1207   
1208   return button->use_stock;
1209 }
1210
1211 /**
1212  * _gtk_button_set_depressed:
1213  * @button: a #GtkButton
1214  * @depressed: %TRUE if the button should be drawn with a recessed shadow.
1215  * 
1216  * Sets whether the button is currently drawn as down or not. This is 
1217  * purely a visual setting, and is meant only for use by derived widgets
1218  * such as #GtkToggleButton.
1219  **/
1220 void
1221 _gtk_button_set_depressed (GtkButton *button,
1222                            gboolean   depressed)
1223 {
1224   GtkWidget *widget = GTK_WIDGET (button);
1225
1226   depressed = depressed != FALSE;
1227
1228   if (depressed != button->depressed)
1229     {
1230       button->depressed = depressed;
1231       gtk_widget_queue_resize (widget);
1232     }
1233 }
1234
1235 static void
1236 gtk_button_update_state (GtkButton *button)
1237 {
1238   gboolean depressed;
1239   GtkStateType new_state;
1240
1241   depressed = button->button_down && (button->in_button || button->activate_timeout);
1242
1243   if (!button->button_down && button->in_button)
1244     new_state = GTK_STATE_PRELIGHT;
1245   else
1246     new_state = depressed ? GTK_STATE_ACTIVE: GTK_STATE_NORMAL;
1247
1248   _gtk_button_set_depressed (button, depressed); 
1249   gtk_widget_set_state (GTK_WIDGET (button), new_state);
1250 }