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