]> Pileus Git - ~andy/gtk/blob - gtk/gtkbutton.c
Fix G_VALUE_NO_COPY_CONTENTS instead of G_SIGNAL_TYPE_STATIC_SCOPE
[~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 "gtkbutton.h"
29 #include "gtklabel.h"
30 #include "gtkmain.h"
31 #include "gtksignal.h"
32 #include "gtkimage.h"
33 #include "gtkhbox.h"
34 #include "gtkstock.h"
35 #include "gtkiconfactory.h"
36 #include "gtkintl.h"
37
38 #define CHILD_SPACING     1
39 #define DEFAULT_SPACING   7
40
41 /* Time out before giving up on getting a key release when animatng
42  * the close button.
43  */
44 #define ACTIVATE_TIMEOUT 250
45
46 enum {
47   PRESSED,
48   RELEASED,
49   CLICKED,
50   ENTER,
51   LEAVE,
52   ACTIVATE,
53   LAST_SIGNAL
54 };
55
56 enum {
57   PROP_0,
58   PROP_LABEL,
59   PROP_RELIEF
60 };
61
62 static void gtk_button_class_init     (GtkButtonClass   *klass);
63 static void gtk_button_init           (GtkButton        *button);
64 static void gtk_button_set_property   (GObject         *object,
65                                        guint            prop_id,
66                                        const GValue    *value,
67                                        GParamSpec      *pspec);
68 static void gtk_button_get_property   (GObject         *object,
69                                        guint            prop_id,
70                                        GValue          *value,
71                                        GParamSpec      *pspec);
72 static void gtk_button_realize        (GtkWidget        *widget);
73 static void gtk_button_unrealize      (GtkWidget        *widget);
74 static void gtk_button_size_request   (GtkWidget        *widget,
75                                        GtkRequisition   *requisition);
76 static void gtk_button_size_allocate  (GtkWidget        *widget,
77                                        GtkAllocation    *allocation);
78 static void gtk_button_paint          (GtkWidget        *widget,
79                                        GdkRectangle     *area);
80 static gint gtk_button_expose         (GtkWidget        *widget,
81                                        GdkEventExpose   *event);
82 static gint gtk_button_button_press   (GtkWidget        *widget,
83                                        GdkEventButton   *event);
84 static gint gtk_button_button_release (GtkWidget        *widget,
85                                        GdkEventButton   *event);
86 static gint gtk_button_key_release    (GtkWidget        *widget,
87                                        GdkEventKey      *event);
88 static gint gtk_button_enter_notify   (GtkWidget        *widget,
89                                        GdkEventCrossing *event);
90 static gint gtk_button_leave_notify   (GtkWidget        *widget,
91                                        GdkEventCrossing *event);
92 static void gtk_button_add            (GtkContainer     *container,
93                                        GtkWidget        *widget);
94 static void gtk_button_remove         (GtkContainer     *container,
95                                        GtkWidget        *widget);
96 static void gtk_real_button_pressed   (GtkButton        *button);
97 static void gtk_real_button_released  (GtkButton        *button);
98 static void gtk_real_button_enter     (GtkButton        *button);
99 static void gtk_real_button_leave     (GtkButton        *button);
100 static void gtk_real_button_activate (GtkButton         *button);
101 static GtkType gtk_button_child_type  (GtkContainer     *container);
102
103 static void gtk_button_finish_activate (GtkButton *button,
104                                         gboolean   do_it);
105
106 static GtkBinClass *parent_class = NULL;
107 static guint button_signals[LAST_SIGNAL] = { 0 };
108
109
110 GtkType
111 gtk_button_get_type (void)
112 {
113   static GtkType button_type = 0;
114
115   if (!button_type)
116     {
117       static const GTypeInfo button_info =
118       {
119         sizeof (GtkButtonClass),
120         NULL,           /* base_init */
121         NULL,           /* base_finalize */
122         (GClassInitFunc) gtk_button_class_init,
123         NULL,           /* class_finalize */
124         NULL,           /* class_data */
125         sizeof (GtkButton),
126         16,             /* n_preallocs */
127         (GInstanceInitFunc) gtk_button_init,
128       };
129
130       button_type = g_type_register_static (GTK_TYPE_BIN, "GtkButton", &button_info, 0);
131     }
132
133   return button_type;
134 }
135
136 static void
137 gtk_button_class_init (GtkButtonClass *klass)
138 {
139   GtkObjectClass *object_class;
140   GtkWidgetClass *widget_class;
141   GtkContainerClass *container_class;
142
143   object_class = (GtkObjectClass*) klass;
144   widget_class = (GtkWidgetClass*) klass;
145   container_class = (GtkContainerClass*) klass;
146   
147   parent_class = g_type_class_peek_parent (klass);
148
149   G_OBJECT_CLASS(object_class)->set_property = gtk_button_set_property;
150   G_OBJECT_CLASS(object_class)->get_property = gtk_button_get_property;
151
152   widget_class->realize = gtk_button_realize;
153   widget_class->unrealize = gtk_button_unrealize;
154   widget_class->size_request = gtk_button_size_request;
155   widget_class->size_allocate = gtk_button_size_allocate;
156   widget_class->expose_event = gtk_button_expose;
157   widget_class->button_press_event = gtk_button_button_press;
158   widget_class->button_release_event = gtk_button_button_release;
159   widget_class->key_release_event = gtk_button_key_release;
160   widget_class->enter_notify_event = gtk_button_enter_notify;
161   widget_class->leave_notify_event = gtk_button_leave_notify;
162
163   container_class->add = gtk_button_add;
164   container_class->remove = gtk_button_remove;
165   container_class->child_type = gtk_button_child_type;
166
167   klass->pressed = gtk_real_button_pressed;
168   klass->released = gtk_real_button_released;
169   klass->clicked = NULL;
170   klass->enter = gtk_real_button_enter;
171   klass->leave = gtk_real_button_leave;
172   klass->activate = gtk_real_button_activate;
173
174   g_object_class_install_property (G_OBJECT_CLASS(object_class),
175                                    PROP_LABEL,
176                                    g_param_spec_string ("label",
177                                                         _("Label"),
178                                                         _("Text of the label widget inside the button, if the button contains a label widget."),
179                                                         NULL,
180                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
181   
182   g_object_class_install_property (G_OBJECT_CLASS(object_class),
183                                    PROP_RELIEF,
184                                    g_param_spec_enum ("relief",
185                                                       _("Border relief"),
186                                                       _("The border relief style."),
187                                                       GTK_TYPE_RELIEF_STYLE,
188                                                       GTK_RELIEF_NORMAL,
189                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
190
191   button_signals[PRESSED] =
192     gtk_signal_new ("pressed",
193                     GTK_RUN_FIRST,
194                     GTK_CLASS_TYPE (object_class),
195                     GTK_SIGNAL_OFFSET (GtkButtonClass, pressed),
196                     gtk_marshal_VOID__VOID,
197                     GTK_TYPE_NONE, 0);
198   button_signals[RELEASED] =
199     gtk_signal_new ("released",
200                     GTK_RUN_FIRST,
201                     GTK_CLASS_TYPE (object_class),
202                     GTK_SIGNAL_OFFSET (GtkButtonClass, released),
203                     gtk_marshal_VOID__VOID,
204                     GTK_TYPE_NONE, 0);
205   button_signals[CLICKED] =
206     gtk_signal_new ("clicked",
207                     GTK_RUN_FIRST | GTK_RUN_ACTION,
208                     GTK_CLASS_TYPE (object_class),
209                     GTK_SIGNAL_OFFSET (GtkButtonClass, clicked),
210                     gtk_marshal_VOID__VOID,
211                     GTK_TYPE_NONE, 0);
212   button_signals[ENTER] =
213     gtk_signal_new ("enter",
214                     GTK_RUN_FIRST,
215                     GTK_CLASS_TYPE (object_class),
216                     GTK_SIGNAL_OFFSET (GtkButtonClass, enter),
217                     gtk_marshal_VOID__VOID,
218                     GTK_TYPE_NONE, 0);
219   button_signals[LEAVE] =
220     gtk_signal_new ("leave",
221                     GTK_RUN_FIRST,
222                     GTK_CLASS_TYPE (object_class),
223                     GTK_SIGNAL_OFFSET (GtkButtonClass, leave),
224                     gtk_marshal_VOID__VOID,
225                     GTK_TYPE_NONE, 0);
226   button_signals[ACTIVATE] =
227     gtk_signal_new ("activate",
228                     GTK_RUN_FIRST,
229                     GTK_CLASS_TYPE (object_class),
230                     GTK_SIGNAL_OFFSET (GtkButtonClass, activate),
231                     gtk_marshal_VOID__VOID,
232                     GTK_TYPE_NONE, 0);
233   widget_class->activate_signal = button_signals[ACTIVATE];
234
235   gtk_widget_class_install_style_property (widget_class,
236                                            g_param_spec_int ("default_spacing",
237                                                              _("Default Spacing"),
238                                                              _("Extra space to add for CAN_DEFAULT buttons"),
239                                                              0,
240                                                              G_MAXINT,
241                                                              DEFAULT_SPACING,
242                                                              G_PARAM_READABLE));
243 }
244
245 static void
246 gtk_button_init (GtkButton *button)
247 {
248   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
249   GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);
250
251   button->child = NULL;
252   button->in_button = FALSE;
253   button->button_down = FALSE;
254   button->relief = GTK_RELIEF_NORMAL;
255 }
256
257 static GtkType
258 gtk_button_child_type  (GtkContainer     *container)
259 {
260   if (!GTK_BIN (container)->child)
261     return GTK_TYPE_WIDGET;
262   else
263     return GTK_TYPE_NONE;
264 }
265
266 static void
267 gtk_button_set_property (GObject         *object,
268                          guint            prop_id,
269                          const GValue    *value,
270                          GParamSpec      *pspec)
271 {
272   GtkButton *button;
273
274   button = GTK_BUTTON (object);
275
276   switch (prop_id)
277     {
278       GtkWidget *child;
279
280     case PROP_LABEL:
281       child = GTK_BIN (button)->child;
282       if (!child)
283         child = gtk_widget_new (GTK_TYPE_LABEL,
284                                 "visible", TRUE,
285                                 "parent", button,
286                                 NULL);
287       if (GTK_IS_LABEL (child))
288         {
289           gtk_label_set_text (GTK_LABEL (child),
290                               g_value_get_string (value) ? g_value_get_string (value) : "");
291         }
292       break;
293     case PROP_RELIEF:
294       gtk_button_set_relief (button, g_value_get_enum (value));
295       break;
296     default:
297       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298       break;
299     }
300 }
301
302 static void
303 gtk_button_get_property (GObject         *object,
304                          guint            prop_id,
305                          GValue          *value,
306                          GParamSpec      *pspec)
307 {
308   GtkButton *button;
309
310   button = GTK_BUTTON (object);
311
312   switch (prop_id)
313     {
314     case PROP_LABEL:
315       if (GTK_BIN (button)->child && GTK_IS_LABEL (GTK_BIN (button)->child))
316          g_value_set_string (value, GTK_LABEL (GTK_BIN (button)->child)->label); 
317       else
318          g_value_set_string (value, NULL);
319       break;
320     case PROP_RELIEF:
321       g_value_set_enum (value, gtk_button_get_relief (button));
322       break;
323     default:
324       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
325       break;
326     }
327 }
328
329 GtkWidget*
330 gtk_button_new (void)
331 {
332   return GTK_WIDGET (gtk_type_new (gtk_button_get_type ()));
333 }
334
335 GtkWidget*
336 gtk_button_new_with_label (const gchar *label)
337 {
338   GtkWidget *button;
339   GtkWidget *label_widget;
340
341   button = gtk_button_new ();
342   label_widget = gtk_label_new (label);
343   gtk_misc_set_alignment (GTK_MISC (label_widget), 0.5, 0.5);
344
345   gtk_container_add (GTK_CONTAINER (button), label_widget);
346   gtk_widget_show (label_widget);
347
348   return button;
349 }
350
351 /**
352  * gtk_button_new_from_stock:
353  * @stock_id: the name of the stock item 
354  * @returns: a new #GtkButton
355  *
356  * Creates a new #GtkButton containing the image and text from a stock item.
357  * Some stock ids have preprocessor macros like #GTK_STOCK_BUTTON_OK and
358  * #GTK_STOCK_BUTTON_APPLY.
359  **/
360 GtkWidget*
361 gtk_button_new_from_stock (const gchar   *stock_id)
362 {
363   GtkWidget *button;
364   GtkStockItem item;
365
366   if (gtk_stock_lookup (stock_id, &item))
367     {
368       GtkWidget *label;
369       GtkWidget *image;
370       GtkWidget *hbox;
371       
372       button = gtk_button_new ();
373
374       label = gtk_label_new_with_mnemonic (item.label);
375
376       gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);
377
378       image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
379       hbox = gtk_hbox_new (FALSE, 0);
380
381       gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 2);
382       gtk_box_pack_end (GTK_BOX (hbox), label, TRUE, TRUE, 2);
383       
384       gtk_container_add (GTK_CONTAINER (button), hbox);
385       gtk_widget_show_all (hbox);
386     }
387   else
388     {
389       button = gtk_button_new_with_mnemonic (stock_id);
390     }
391   
392   return button;
393 }
394
395 /**
396  * gtk_button_new_with_mnemonic:
397  * @label: The text of the button, with an underscore in front of the
398  *         mnemonic character
399  * @returns: a new #GtkButton
400  *
401  * Creates a new #GtkButton containing a label.
402  * If characters in @label are preceded by an underscore, they are underlined
403  * indicating that they represent a keyboard accelerator called a mnemonic.
404  * Pressing Alt and that key activates the button.
405  **/
406 GtkWidget*
407 gtk_button_new_with_mnemonic (const gchar *label)
408 {
409   GtkWidget *button;
410   GtkWidget *label_widget;
411
412   button = gtk_button_new ();
413   
414   label_widget = gtk_label_new_with_mnemonic (label);
415
416   gtk_label_set_mnemonic_widget (GTK_LABEL (label_widget), button);
417   
418   gtk_container_add (GTK_CONTAINER (button), label_widget);
419   gtk_widget_show (label_widget);
420
421   return button;
422 }
423
424 void
425 gtk_button_pressed (GtkButton *button)
426 {
427   g_return_if_fail (button != NULL);
428   g_return_if_fail (GTK_IS_BUTTON (button));
429
430   gtk_signal_emit (GTK_OBJECT (button), button_signals[PRESSED]);
431 }
432
433 void
434 gtk_button_released (GtkButton *button)
435 {
436   g_return_if_fail (button != NULL);
437   g_return_if_fail (GTK_IS_BUTTON (button));
438
439   gtk_signal_emit (GTK_OBJECT (button), button_signals[RELEASED]);
440 }
441
442 void
443 gtk_button_clicked (GtkButton *button)
444 {
445   g_return_if_fail (button != NULL);
446   g_return_if_fail (GTK_IS_BUTTON (button));
447
448   gtk_signal_emit (GTK_OBJECT (button), button_signals[CLICKED]);
449 }
450
451 void
452 gtk_button_enter (GtkButton *button)
453 {
454   g_return_if_fail (button != NULL);
455   g_return_if_fail (GTK_IS_BUTTON (button));
456
457   gtk_signal_emit (GTK_OBJECT (button), button_signals[ENTER]);
458 }
459
460 void
461 gtk_button_leave (GtkButton *button)
462 {
463   g_return_if_fail (button != NULL);
464   g_return_if_fail (GTK_IS_BUTTON (button));
465
466   gtk_signal_emit (GTK_OBJECT (button), button_signals[LEAVE]);
467 }
468
469 void
470 gtk_button_set_relief (GtkButton *button,
471                        GtkReliefStyle newrelief)
472 {
473   g_return_if_fail (button != NULL);
474   g_return_if_fail (GTK_IS_BUTTON (button));
475
476   button->relief = newrelief;
477   g_object_notify(G_OBJECT(button), "relief");
478   gtk_widget_queue_draw (GTK_WIDGET (button));
479 }
480
481 GtkReliefStyle
482 gtk_button_get_relief (GtkButton *button)
483 {
484   g_return_val_if_fail (button != NULL, GTK_RELIEF_NORMAL);
485   g_return_val_if_fail (GTK_IS_BUTTON (button), GTK_RELIEF_NORMAL);
486
487   return button->relief;
488 }
489
490 static void
491 gtk_button_realize (GtkWidget *widget)
492 {
493   GtkButton *button;
494   GdkWindowAttr attributes;
495   gint attributes_mask;
496   gint border_width;
497
498   g_return_if_fail (widget != NULL);
499   g_return_if_fail (GTK_IS_BUTTON (widget));
500
501   button = GTK_BUTTON (widget);
502   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
503
504   border_width = GTK_CONTAINER (widget)->border_width;
505
506   attributes.window_type = GDK_WINDOW_CHILD;
507   attributes.x = widget->allocation.x + border_width;
508   attributes.y = widget->allocation.y + border_width;
509   attributes.width = widget->allocation.width - border_width * 2;
510   attributes.height = widget->allocation.height - border_width * 2;
511   attributes.wclass = GDK_INPUT_OUTPUT;
512   attributes.visual = gtk_widget_get_visual (widget);
513   attributes.colormap = gtk_widget_get_colormap (widget);
514   attributes.event_mask = gtk_widget_get_events (widget);
515   attributes.event_mask |= (GDK_EXPOSURE_MASK |
516                             GDK_BUTTON_PRESS_MASK |
517                             GDK_BUTTON_RELEASE_MASK |
518                             GDK_ENTER_NOTIFY_MASK |
519                             GDK_LEAVE_NOTIFY_MASK);
520
521   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
522
523   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
524   gdk_window_set_user_data (widget->window, button);
525
526   widget->style = gtk_style_attach (widget->style, widget->window);
527   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
528 }
529
530 static void
531 gtk_button_unrealize (GtkWidget *widget)
532 {
533   GtkButton *button = GTK_BUTTON (widget);
534
535   if (button->activate_timeout)
536     gtk_button_finish_activate (button, FALSE);
537     
538   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
539 }
540
541 static void
542 gtk_button_get_props (GtkButton *button,
543                       gint      *default_spacing,
544                       gboolean  *interior_focus)
545 {
546   GtkWidget *widget =  GTK_WIDGET (button);
547
548   if (default_spacing)
549     gtk_widget_style_get (widget, "default_spacing", default_spacing, NULL);
550
551   if (interior_focus)
552     gtk_widget_style_get (widget, "interior_focus", interior_focus, NULL);
553 }
554         
555 static void
556 gtk_button_size_request (GtkWidget      *widget,
557                          GtkRequisition *requisition)
558 {
559   GtkButton *button = GTK_BUTTON (widget);
560   gint default_spacing;
561   gboolean interior_focus;
562
563   gtk_button_get_props (button, &default_spacing, &interior_focus);
564   
565   requisition->width = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
566                         GTK_WIDGET (widget)->style->xthickness) * 2;
567   requisition->height = (GTK_CONTAINER (widget)->border_width + CHILD_SPACING +
568                          GTK_WIDGET (widget)->style->ythickness) * 2;
569
570   if (GTK_WIDGET_CAN_DEFAULT (widget))
571     {
572       requisition->width += (GTK_WIDGET (widget)->style->xthickness * 2 +
573                              default_spacing);
574       requisition->height += (GTK_WIDGET (widget)->style->ythickness * 2 +
575                               default_spacing);
576     }
577
578   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
579     {
580       GtkRequisition child_requisition;
581
582       gtk_widget_size_request (GTK_BIN (button)->child, &child_requisition);
583
584       requisition->width += child_requisition.width;
585       requisition->height += child_requisition.height;
586     }
587
588   if (interior_focus)
589     {
590       requisition->width += 2;
591       requisition->height += 2;
592     }
593 }
594
595 static void
596 gtk_button_size_allocate (GtkWidget     *widget,
597                           GtkAllocation *allocation)
598 {
599   GtkButton *button = GTK_BUTTON (widget);
600   GtkAllocation child_allocation;
601
602   gint border_width = GTK_CONTAINER (widget)->border_width;
603   gint xthickness = GTK_WIDGET (widget)->style->xthickness;
604   gint ythickness = GTK_WIDGET (widget)->style->ythickness;
605   gint default_spacing;
606
607   gtk_button_get_props (button, &default_spacing, NULL);
608   
609   widget->allocation = *allocation;
610
611   if (GTK_WIDGET_REALIZED (widget))
612     gdk_window_move_resize (widget->window,
613                             widget->allocation.x + border_width,
614                             widget->allocation.y + border_width,
615                             widget->allocation.width - border_width * 2,
616                             widget->allocation.height - border_width * 2);
617
618   if (GTK_BIN (button)->child && GTK_WIDGET_VISIBLE (GTK_BIN (button)->child))
619     {
620       child_allocation.x = (CHILD_SPACING + xthickness);
621       child_allocation.y = (CHILD_SPACING + ythickness);
622
623       child_allocation.width = MAX (1, (gint)widget->allocation.width - child_allocation.x * 2 -
624                                  border_width * 2);
625       child_allocation.height = MAX (1, (gint)widget->allocation.height - child_allocation.y * 2 -
626                                   border_width * 2);
627
628       if (GTK_WIDGET_CAN_DEFAULT (button))
629         {
630           child_allocation.x += (GTK_WIDGET (widget)->style->xthickness +
631                                  (1 + default_spacing) / 2);
632           child_allocation.y += (GTK_WIDGET (widget)->style->ythickness +
633                                  (1 + default_spacing) / 2);
634           child_allocation.width =  MAX (1, (gint)child_allocation.width -
635                                          (gint)(GTK_WIDGET (widget)->style->xthickness * 2 + default_spacing));
636           child_allocation.height = MAX (1, (gint)child_allocation.height -
637                                          (gint)(GTK_WIDGET (widget)->style->xthickness * 2 + default_spacing));
638         }
639
640       gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
641     }
642 }
643
644 /*
645  * +------------------------------------------------+
646  * |                   BORDER                       |
647  * |  +------------------------------------------+  |
648  * |  |\\\\\\\\\\\\\\\\DEFAULT\\\\\\\\\\\\\\\\\  |  |
649  * |  |\\+------------------------------------+  |  |
650  * |  |\\| |           SPACING       3      | |  |  |
651  * |  |\\| +--------------------------------+ |  |  |
652  * |  |\\| |########## FOCUS ###############| |  |  |
653  * |  |\\| |#+----------------------------+#| |  |  |
654  * |  |\\| |#|         RELIEF            \|#| |  |  |
655  * |  |\\| |#|  +-----------------------+\|#| |  |  |
656  * |  |\\|1|#|  +     THE TEXT          +\|#|2|  |  |
657  * |  |\\| |#|  +-----------------------+\|#| |  |  |
658  * |  |\\| |#| \\\\\ ythickness \\\\\\\\\\|#| |  |  |
659  * |  |\\| |#+----------------------------+#| |  |  |
660  * |  |\\| |########### 1 ##################| |  |  |
661  * |  |\\| +--------------------------------+ |  |  |
662  * |  |\\| |        default spacing   4     | |  |  |
663  * |  |\\+------------------------------------+  |  |
664  * |  |\            ythickness                   |  |
665  * |  +------------------------------------------+  |
666  * |                border_width                    |
667  * +------------------------------------------------+
668  */
669
670 static void
671 gtk_button_paint (GtkWidget    *widget,
672                   GdkRectangle *area)
673 {
674   GtkButton *button;
675   GtkShadowType shadow_type;
676   gint width, height;
677   gint x, y;
678   gint default_spacing;
679   gboolean interior_focus;
680    
681   if (GTK_WIDGET_DRAWABLE (widget))
682     {
683       button = GTK_BUTTON (widget);
684
685       gtk_button_get_props (button, &default_spacing, &interior_focus);
686         
687       x = 0;
688       y = 0;
689       width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
690       height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
691
692       gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
693       gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
694
695       if (GTK_WIDGET_HAS_DEFAULT (widget) &&
696           GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL)
697         {
698           gtk_paint_box (widget->style, widget->window,
699                          GTK_STATE_NORMAL, GTK_SHADOW_IN,
700                          area, widget, "buttondefault",
701                          x, y, width, height);
702         }
703
704       if (GTK_WIDGET_CAN_DEFAULT (widget))
705         {
706           x += widget->style->xthickness;
707           y += widget->style->ythickness;
708           width -= 2 * x + default_spacing;
709           height -= 2 * y + default_spacing;
710           x += (1 + default_spacing) / 2;
711           y += (1 + default_spacing) / 2;
712         }
713        
714       if (!interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
715         {
716           x += 1;
717           y += 1;
718           width -= 2;
719           height -= 2;
720         }
721         
722       if (GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE)
723         shadow_type = GTK_SHADOW_IN;
724       else
725         shadow_type = GTK_SHADOW_OUT;
726
727       if ((button->relief != GTK_RELIEF_NONE) ||
728           ((GTK_WIDGET_STATE(widget) != GTK_STATE_NORMAL) &&
729            (GTK_WIDGET_STATE(widget) != GTK_STATE_INSENSITIVE)))
730         gtk_paint_box (widget->style, widget->window,
731                        GTK_WIDGET_STATE (widget),
732                        shadow_type, area, widget, "button",
733                        x, y, width, height);
734        
735       if (GTK_WIDGET_HAS_FOCUS (widget))
736         {
737           if (interior_focus)
738             {
739               x += widget->style->xthickness + 1;
740               y += widget->style->ythickness + 1;
741               width -= 2 * (widget->style->xthickness + 1);
742               height -=  2 * (widget->style->xthickness + 1);
743             }
744           else
745             {
746               x -= 1;
747               y -= 1;
748               width += 2;
749               height += 2;
750             }
751
752           gtk_paint_focus (widget->style, widget->window,
753                            area, widget, "button",
754                            x, y, width - 1, height - 1);
755         }
756     }
757 }
758
759 static gboolean
760 gtk_button_expose (GtkWidget      *widget,
761                    GdkEventExpose *event)
762 {
763   GtkBin *bin;
764
765   g_return_val_if_fail (widget != NULL, FALSE);
766   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
767   g_return_val_if_fail (event != NULL, FALSE);
768
769   if (GTK_WIDGET_DRAWABLE (widget))
770     {
771       bin = GTK_BIN (widget);
772       
773       gtk_button_paint (widget, &event->area);
774       
775       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
776     }
777   
778   return FALSE;
779 }
780
781 static gboolean
782 gtk_button_button_press (GtkWidget      *widget,
783                          GdkEventButton *event)
784 {
785   GtkButton *button;
786
787   g_return_val_if_fail (widget != NULL, FALSE);
788   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
789   g_return_val_if_fail (event != NULL, FALSE);
790
791   if (event->type == GDK_BUTTON_PRESS)
792     {
793       button = GTK_BUTTON (widget);
794
795       if (!GTK_WIDGET_HAS_FOCUS (widget))
796         gtk_widget_grab_focus (widget);
797
798       if (event->button == 1)
799         gtk_button_pressed (button);
800     }
801
802   return TRUE;
803 }
804
805 static gboolean
806 gtk_button_button_release (GtkWidget      *widget,
807                            GdkEventButton *event)
808 {
809   GtkButton *button;
810
811   g_return_val_if_fail (widget != NULL, FALSE);
812   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
813   g_return_val_if_fail (event != NULL, FALSE);
814
815   if (event->button == 1)
816     {
817       button = GTK_BUTTON (widget);
818       gtk_button_released (button);
819     }
820
821   return TRUE;
822 }
823
824 static gboolean
825 gtk_button_key_release (GtkWidget   *widget,
826                         GdkEventKey *event)
827 {
828   GtkButton *button = GTK_BUTTON (widget);
829
830   if (button->activate_timeout)
831     {
832       gtk_button_finish_activate (button, TRUE);
833       return TRUE;
834     }
835   else if (GTK_WIDGET_CLASS (parent_class)->key_release_event)
836     return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
837   else
838     return FALSE;
839 }
840
841 static gboolean
842 gtk_button_enter_notify (GtkWidget        *widget,
843                          GdkEventCrossing *event)
844 {
845   GtkButton *button;
846   GtkWidget *event_widget;
847
848   g_return_val_if_fail (widget != NULL, FALSE);
849   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
850   g_return_val_if_fail (event != NULL, FALSE);
851
852   button = GTK_BUTTON (widget);
853   event_widget = gtk_get_event_widget ((GdkEvent*) event);
854
855   if ((event_widget == widget) &&
856       (event->detail != GDK_NOTIFY_INFERIOR))
857     {
858       button->in_button = TRUE;
859       gtk_button_enter (button);
860     }
861
862   return FALSE;
863 }
864
865 static gboolean
866 gtk_button_leave_notify (GtkWidget        *widget,
867                          GdkEventCrossing *event)
868 {
869   GtkButton *button;
870   GtkWidget *event_widget;
871
872   g_return_val_if_fail (widget != NULL, FALSE);
873   g_return_val_if_fail (GTK_IS_BUTTON (widget), FALSE);
874   g_return_val_if_fail (event != NULL, FALSE);
875
876   button = GTK_BUTTON (widget);
877   event_widget = gtk_get_event_widget ((GdkEvent*) event);
878
879   if ((event_widget == widget) &&
880       (event->detail != GDK_NOTIFY_INFERIOR))
881     {
882       button->in_button = FALSE;
883       gtk_button_leave (button);
884     }
885
886   return FALSE;
887 }
888
889 static void
890 gtk_button_add (GtkContainer *container,
891                 GtkWidget    *widget)
892 {
893   g_return_if_fail (container != NULL);
894   g_return_if_fail (widget != NULL);
895
896   if (GTK_CONTAINER_CLASS (parent_class)->add)
897     GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
898
899   GTK_BUTTON (container)->child = GTK_BIN (container)->child;
900 }
901
902 static void
903 gtk_button_remove (GtkContainer *container,
904                    GtkWidget    *widget)
905 {
906   g_return_if_fail (container != NULL);
907   g_return_if_fail (widget != NULL);
908
909   if (GTK_CONTAINER_CLASS (parent_class)->remove)
910     GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
911
912   GTK_BUTTON (container)->child = GTK_BIN (container)->child;
913 }
914
915 static void
916 gtk_real_button_pressed (GtkButton *button)
917 {
918   GtkStateType new_state;
919
920   g_return_if_fail (button != NULL);
921   g_return_if_fail (GTK_IS_BUTTON (button));
922
923   if (button->activate_timeout)
924     return;
925   
926   button->button_down = TRUE;
927
928   new_state = (button->in_button ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL);
929
930   if (GTK_WIDGET_STATE (button) != new_state)
931     {
932       gtk_widget_set_state (GTK_WIDGET (button), new_state);
933       gtk_widget_queue_draw (GTK_WIDGET (button));
934     }
935 }
936
937 static void
938 gtk_real_button_released (GtkButton *button)
939 {
940   GtkStateType new_state;
941
942   g_return_if_fail (button != NULL);
943   g_return_if_fail (GTK_IS_BUTTON (button));
944
945   if (button->button_down)
946     {
947       button->button_down = FALSE;
948
949       if (button->activate_timeout)
950         return;
951   
952       if (button->in_button)
953         gtk_button_clicked (button);
954
955       new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
956
957       if (GTK_WIDGET_STATE (button) != new_state)
958         {
959           gtk_widget_set_state (GTK_WIDGET (button), new_state);
960           /* We _draw () instead of queue_draw so that if the operation
961            * blocks, the label doesn't vanish.
962            */
963           gtk_widget_draw (GTK_WIDGET (button), NULL);
964         }
965     }
966 }
967
968 static void
969 gtk_real_button_enter (GtkButton *button)
970 {
971   GtkStateType new_state;
972
973   g_return_if_fail (button != NULL);
974   g_return_if_fail (GTK_IS_BUTTON (button));
975
976   new_state = (button->button_down ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
977
978   if (button->activate_timeout)
979     return;
980   
981   if (GTK_WIDGET_STATE (button) != new_state)
982     {
983       gtk_widget_set_state (GTK_WIDGET (button), new_state);
984       gtk_widget_queue_draw (GTK_WIDGET (button));
985     }
986 }
987
988 static void
989 gtk_real_button_leave (GtkButton *button)
990 {
991   g_return_if_fail (button != NULL);
992   g_return_if_fail (GTK_IS_BUTTON (button));
993   
994   if (button->activate_timeout)
995     return;
996   
997   if (GTK_WIDGET_STATE (button) != GTK_STATE_NORMAL)
998     {
999       gtk_widget_set_state (GTK_WIDGET (button), GTK_STATE_NORMAL);
1000       gtk_widget_queue_draw (GTK_WIDGET (button));
1001     }
1002 }
1003
1004 static gboolean
1005 button_activate_timeout (gpointer data)
1006 {
1007   gtk_button_finish_activate (data, TRUE);
1008
1009   return FALSE;
1010 }
1011
1012 static void
1013 gtk_real_button_activate (GtkButton *button)
1014 {
1015   GtkWidget *widget = GTK_WIDGET (button);
1016   
1017   g_return_if_fail (button != NULL);
1018   g_return_if_fail (GTK_IS_BUTTON (button));
1019
1020   if (GTK_WIDGET_REALIZED (button) && !button->activate_timeout)
1021     {
1022       if (gdk_keyboard_grab (widget->window, TRUE,
1023                              gtk_get_current_event_time ()) == 0)
1024         {
1025           gtk_grab_add (widget);
1026           
1027           button->activate_timeout = g_timeout_add (ACTIVATE_TIMEOUT,
1028                                                     button_activate_timeout,
1029                                                     button);
1030           button->button_down = TRUE;
1031           gtk_widget_set_state (widget, GTK_STATE_ACTIVE);
1032         }
1033     }
1034 }
1035
1036 static void
1037 gtk_button_finish_activate (GtkButton *button,
1038                             gboolean   do_it)
1039 {
1040   GtkWidget *widget = GTK_WIDGET (button);
1041   
1042   g_source_remove (button->activate_timeout);
1043   button->activate_timeout = 0;
1044
1045   gdk_keyboard_ungrab (gtk_get_current_event_time ());
1046   gtk_grab_remove (widget);
1047
1048   button->button_down = FALSE;
1049   gtk_widget_set_state (GTK_WIDGET (button),
1050                         button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
1051
1052   if (do_it)
1053     gtk_button_clicked (button);
1054 }
1055