]> Pileus Git - ~andy/gtk/blob - gtk/gtktogglebutton.c
eliminated some queue_draws when invoking gtk_widget_set_state, since that
[~andy/gtk] / gtk / gtktogglebutton.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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include "gtklabel.h"
20 #include "gtkmain.h"
21 #include "gtksignal.h"
22 #include "gtktogglebutton.h"
23
24
25 #define DEFAULT_LEFT_POS  4
26 #define DEFAULT_TOP_POS   4
27 #define DEFAULT_SPACING   7
28
29 enum {
30   TOGGLED,
31   LAST_SIGNAL
32 };
33
34 enum {
35   ARG_0,
36   ARG_ACTIVE,
37   ARG_DRAW_INDICATOR
38 };
39
40
41 static void gtk_toggle_button_class_init (GtkToggleButtonClass *klass);
42 static void gtk_toggle_button_init       (GtkToggleButton      *toggle_button);
43 static void gtk_toggle_button_paint      (GtkWidget            *widget,
44                                           GdkRectangle         *area);
45 static void gtk_toggle_button_draw       (GtkWidget            *widget,
46                                           GdkRectangle         *area);
47 static void gtk_toggle_button_pressed    (GtkButton            *button);
48 static void gtk_toggle_button_released   (GtkButton            *button);
49 static void gtk_toggle_button_clicked    (GtkButton            *button);
50 static void gtk_toggle_button_enter      (GtkButton            *button);
51 static void gtk_toggle_button_leave      (GtkButton            *button);
52 static void gtk_toggle_button_set_arg    (GtkObject            *object,
53                                           GtkArg               *arg,
54                                           guint                 arg_id);
55 static void gtk_toggle_button_get_arg    (GtkObject            *object,
56                                           GtkArg               *arg,
57                                           guint                 arg_id);
58 static void gtk_toggle_button_leave      (GtkButton            *button);
59 static void gtk_toggle_button_realize    (GtkWidget            *widget);
60 static void gtk_toggle_button_unrealize  (GtkWidget            *widget);
61 static void gtk_toggle_button_map        (GtkWidget            *widget);
62 static void gtk_toggle_button_unmap      (GtkWidget            *widget);
63
64 static guint toggle_button_signals[LAST_SIGNAL] = { 0 };
65 static GtkContainerClass *parent_class = NULL;
66
67 GtkType
68 gtk_toggle_button_get_type (void)
69 {
70   static GtkType toggle_button_type = 0;
71
72   if (!toggle_button_type)
73     {
74       static const GtkTypeInfo toggle_button_info =
75       {
76         "GtkToggleButton",
77         sizeof (GtkToggleButton),
78         sizeof (GtkToggleButtonClass),
79         (GtkClassInitFunc) gtk_toggle_button_class_init,
80         (GtkObjectInitFunc) gtk_toggle_button_init,
81         /* reserved_1 */ NULL,
82         /* reserved_2 */ NULL,
83         (GtkClassInitFunc) NULL,
84       };
85
86       toggle_button_type = gtk_type_unique (GTK_TYPE_BUTTON, &toggle_button_info);
87     }
88
89   return toggle_button_type;
90 }
91
92 static void
93 gtk_toggle_button_class_init (GtkToggleButtonClass *class)
94 {
95   GtkObjectClass *object_class;
96   GtkWidgetClass *widget_class;
97   GtkContainerClass *container_class;
98   GtkButtonClass *button_class;
99
100   object_class = (GtkObjectClass*) class;
101   widget_class = (GtkWidgetClass*) class;
102   container_class = (GtkContainerClass*) class;
103   button_class = (GtkButtonClass*) class;
104
105   parent_class = gtk_type_class (GTK_TYPE_BUTTON);
106
107   gtk_object_add_arg_type ("GtkToggleButton::active", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_ACTIVE);
108   gtk_object_add_arg_type ("GtkToggleButton::draw_indicator", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_DRAW_INDICATOR);
109
110   toggle_button_signals[TOGGLED] =
111     gtk_signal_new ("toggled",
112                     GTK_RUN_FIRST,
113                     object_class->type,
114                     GTK_SIGNAL_OFFSET (GtkToggleButtonClass, toggled),
115                     gtk_marshal_NONE__NONE,
116                     GTK_TYPE_NONE, 0);
117
118   gtk_object_class_add_signals (object_class, toggle_button_signals, LAST_SIGNAL);
119
120   object_class->set_arg = gtk_toggle_button_set_arg;
121   object_class->get_arg = gtk_toggle_button_get_arg;
122
123   widget_class->draw = gtk_toggle_button_draw;
124   widget_class->realize = gtk_toggle_button_realize;
125   widget_class->unrealize = gtk_toggle_button_unrealize;
126   widget_class->map = gtk_toggle_button_map;
127   widget_class->unmap = gtk_toggle_button_unmap;
128
129   button_class->pressed = gtk_toggle_button_pressed;
130   button_class->released = gtk_toggle_button_released;
131   button_class->clicked = gtk_toggle_button_clicked;
132   button_class->enter = gtk_toggle_button_enter;
133   button_class->leave = gtk_toggle_button_leave;
134
135   class->toggled = NULL;
136 }
137
138 static void
139 gtk_toggle_button_init (GtkToggleButton *toggle_button)
140 {
141   toggle_button->active = FALSE;
142   toggle_button->draw_indicator = FALSE;
143 }
144
145
146 GtkWidget*
147 gtk_toggle_button_new (void)
148 {
149   return GTK_WIDGET (gtk_type_new (gtk_toggle_button_get_type ()));
150 }
151
152 GtkWidget*
153 gtk_toggle_button_new_with_label (const gchar *label)
154 {
155   GtkWidget *toggle_button;
156   GtkWidget *label_widget;
157
158   toggle_button = gtk_toggle_button_new ();
159   label_widget = gtk_label_new (label);
160   gtk_misc_set_alignment (GTK_MISC (label_widget), 0.5, 0.5);
161
162   gtk_container_add (GTK_CONTAINER (toggle_button), label_widget);
163   gtk_widget_show (label_widget);
164
165   return toggle_button;
166 }
167
168 static void
169 gtk_toggle_button_set_arg (GtkObject *object,
170                            GtkArg    *arg,
171                            guint      arg_id)
172 {
173   GtkToggleButton *tb;
174
175   tb = GTK_TOGGLE_BUTTON (object);
176
177   switch (arg_id)
178     {
179     case ARG_ACTIVE:
180       gtk_toggle_button_set_active (tb, GTK_VALUE_BOOL (*arg));
181       break;
182     case ARG_DRAW_INDICATOR:
183       gtk_toggle_button_set_mode (tb, GTK_VALUE_BOOL (*arg));
184       break;
185     default:
186       break;
187     }
188 }
189
190 static void
191 gtk_toggle_button_get_arg (GtkObject *object,
192                            GtkArg    *arg,
193                            guint      arg_id)
194 {
195   GtkToggleButton *tb;
196
197   tb = GTK_TOGGLE_BUTTON (object);
198
199   switch (arg_id)
200     {
201     case ARG_ACTIVE:
202       GTK_VALUE_BOOL (*arg) = tb->active;
203       break;
204     case ARG_DRAW_INDICATOR:
205       GTK_VALUE_BOOL (*arg) = tb->draw_indicator;
206       break;
207     default:
208       arg->type = GTK_TYPE_INVALID;
209       break;
210     }
211 }
212
213 void
214 gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
215                             gboolean         draw_indicator)
216 {
217   GtkWidget *widget;
218
219   g_return_if_fail (toggle_button != NULL);
220   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
221
222   widget = GTK_WIDGET (toggle_button);
223
224   draw_indicator = draw_indicator ? TRUE : FALSE;
225
226   if (toggle_button->draw_indicator != draw_indicator)
227     {
228       if (GTK_WIDGET_REALIZED (toggle_button))
229         {
230           gboolean visible = GTK_WIDGET_VISIBLE (toggle_button);
231
232           if (visible)
233             gtk_widget_hide (widget);
234
235           gtk_widget_unrealize (widget);
236           toggle_button->draw_indicator = draw_indicator;
237           gtk_widget_realize (widget);
238
239           if (visible)
240             gtk_widget_show (widget);
241         }
242       else
243         toggle_button->draw_indicator = draw_indicator;
244
245       if (GTK_WIDGET_VISIBLE (toggle_button))
246         gtk_widget_queue_resize (GTK_WIDGET (toggle_button));
247     }
248 }
249
250 void
251 gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
252                               gboolean         is_active)
253 {
254   g_return_if_fail (toggle_button != NULL);
255   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
256
257   is_active = is_active != 0;
258
259   if (toggle_button->active != is_active)
260     gtk_button_clicked (GTK_BUTTON (toggle_button));
261 }
262
263 void
264 gtk_toggle_button_toggled (GtkToggleButton *toggle_button)
265 {
266   g_return_if_fail (toggle_button != NULL);
267   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button));
268
269   gtk_signal_emit (GTK_OBJECT (toggle_button), toggle_button_signals[TOGGLED]);
270 }
271
272
273 static void
274 gtk_toggle_button_paint (GtkWidget    *widget,
275                          GdkRectangle *area)
276 {
277   GtkButton *button;
278   GtkToggleButton *toggle_button;
279   GtkShadowType shadow_type;
280   gint width, height;
281   gint x, y;
282
283   if (GTK_WIDGET_DRAWABLE (widget))
284     {
285       button = GTK_BUTTON (widget);
286       toggle_button = GTK_TOGGLE_BUTTON (widget);
287
288       x = 0;
289       y = 0;
290       width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
291       height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
292
293       gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
294       gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
295
296       if (GTK_WIDGET_HAS_DEFAULT (widget) &&
297           GTK_BUTTON (widget)->relief == GTK_RELIEF_NORMAL)
298         {
299           gtk_paint_box (widget->style, widget->window,
300                          GTK_STATE_NORMAL, GTK_SHADOW_IN,
301                          area, widget, "togglebuttondefault",
302                          x, y, width, height);
303         }
304
305       if (GTK_WIDGET_CAN_DEFAULT (widget))
306         {
307           x += widget->style->klass->xthickness;
308           y += widget->style->klass->ythickness;
309           width -= 2 * x + DEFAULT_SPACING;
310           height -= 2 * y + DEFAULT_SPACING;
311           x += DEFAULT_LEFT_POS;
312           y += DEFAULT_TOP_POS;
313         }
314
315       if (GTK_WIDGET_HAS_FOCUS (widget))
316         {
317           x += 1;
318           y += 1;
319           width -= 2;
320           height -= 2;
321         }
322
323       if ((GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE) ||
324           toggle_button->active)
325         shadow_type = GTK_SHADOW_IN;
326       else
327         shadow_type = GTK_SHADOW_OUT;
328       
329       if (button->relief != GTK_RELIEF_NONE ||
330           (GTK_WIDGET_STATE(widget) != GTK_STATE_NORMAL &&
331            GTK_WIDGET_STATE(widget) != GTK_STATE_INSENSITIVE))
332         gtk_paint_box (widget->style, widget->window,
333                        GTK_WIDGET_STATE (widget),
334                        shadow_type, area, widget, "togglebutton",
335                        x, y, width, height);
336       
337       if (GTK_WIDGET_HAS_FOCUS (widget))
338         {
339           x -= 1;
340           y -= 1;
341           width += 2;
342           height += 2;
343
344           gtk_paint_focus (widget->style, widget->window,
345                            area, widget, "togglebutton",
346                            x, y, width - 1, height - 1);
347         }
348     }
349 }
350
351 static void
352 gtk_toggle_button_draw (GtkWidget    *widget,
353                         GdkRectangle *area)
354 {
355   GdkRectangle child_area;
356   GdkRectangle tmp_area;
357   GtkBin *bin;
358
359   g_return_if_fail (widget != NULL);
360   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
361   g_return_if_fail (area != NULL);
362
363   bin = GTK_BIN (widget);
364
365   if (GTK_WIDGET_DRAWABLE (widget))
366     {
367       tmp_area = *area;
368       tmp_area.x -= GTK_CONTAINER (widget)->border_width;
369       tmp_area.y -= GTK_CONTAINER (widget)->border_width;
370
371       gtk_toggle_button_paint (widget, &tmp_area);
372
373       if (bin->child && gtk_widget_intersect (bin->child, &tmp_area, &child_area))
374         gtk_widget_draw (bin->child, &child_area);
375     }
376 }
377
378 static void
379 gtk_toggle_button_pressed (GtkButton *button)
380 {
381   GtkToggleButton *toggle_button;
382   GtkStateType new_state;
383
384   g_return_if_fail (button != NULL);
385   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
386
387   toggle_button = GTK_TOGGLE_BUTTON (button);
388
389   button->button_down = TRUE;
390
391   if (toggle_button->active)
392     new_state = (button->in_button ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE);
393   else
394     new_state = (button->in_button ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL);
395
396   if (GTK_WIDGET_STATE (button) != new_state)
397     gtk_widget_set_state (GTK_WIDGET (button), new_state);
398 }
399
400 static void
401 gtk_toggle_button_released (GtkButton *button)
402 {
403   GtkToggleButton *toggle_button;
404   GtkStateType new_state;
405
406   g_return_if_fail (button != NULL);
407   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
408
409   if (button->button_down)
410     {
411       toggle_button = GTK_TOGGLE_BUTTON (button);
412
413       button->button_down = FALSE;
414
415       if (button->in_button)
416         {
417           gtk_button_clicked (button);
418         }
419       else
420         {
421           if (toggle_button->active)
422             new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
423           else
424             new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
425
426           if (GTK_WIDGET_STATE (button) != new_state)
427             gtk_widget_set_state (GTK_WIDGET (button), new_state);
428         }
429     }
430 }
431
432 static void
433 gtk_toggle_button_clicked (GtkButton *button)
434 {
435   GtkToggleButton *toggle_button;
436   GtkStateType new_state;
437
438   g_return_if_fail (button != NULL);
439   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
440
441   toggle_button = GTK_TOGGLE_BUTTON (button);
442   toggle_button->active = !toggle_button->active;
443
444   gtk_toggle_button_toggled (toggle_button);
445
446   if (toggle_button->active)
447     new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
448   else
449     new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
450
451   if (GTK_WIDGET_STATE (button) != new_state)
452     gtk_widget_set_state (GTK_WIDGET (button), new_state);
453   else
454     gtk_widget_queue_draw (GTK_WIDGET (button));
455 }
456
457 static void
458 gtk_toggle_button_enter (GtkButton *button)
459 {
460   GtkToggleButton *toggle_button;
461   GtkStateType new_state;
462
463   g_return_if_fail (button != NULL);
464   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
465
466   toggle_button = GTK_TOGGLE_BUTTON (button);
467
468   if (toggle_button->active)
469     new_state = (button->button_down ? GTK_STATE_NORMAL : GTK_STATE_PRELIGHT);
470   else
471     new_state = (button->button_down ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT);
472
473   if (GTK_WIDGET_STATE (button) != new_state)
474     gtk_widget_set_state (GTK_WIDGET (button), new_state);
475 }
476
477 static void
478 gtk_toggle_button_leave (GtkButton *button)
479 {
480   GtkToggleButton *toggle_button;
481   GtkStateType new_state;
482
483   g_return_if_fail (button != NULL);
484   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
485
486   toggle_button = GTK_TOGGLE_BUTTON (button);
487
488   new_state = (toggle_button->active ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL);
489
490   if (GTK_WIDGET_STATE (button) != new_state)
491     gtk_widget_set_state (GTK_WIDGET (button), new_state);
492 }
493
494 static void
495 gtk_toggle_button_realize (GtkWidget *widget)
496 {
497   GtkToggleButton *toggle_button;
498   GdkWindowAttr attributes;
499   gint attributes_mask;
500   gint border_width;
501   
502   g_return_if_fail (widget != NULL);
503   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
504   
505   toggle_button = GTK_TOGGLE_BUTTON (widget);
506   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
507   
508   border_width = GTK_CONTAINER (widget)->border_width;
509   
510   attributes.window_type = GDK_WINDOW_CHILD;
511   attributes.x = widget->allocation.x + border_width;
512   attributes.y = widget->allocation.y + border_width;
513   attributes.width = widget->allocation.width - border_width * 2;
514   attributes.height = widget->allocation.height - border_width * 2;
515   attributes.event_mask = gtk_widget_get_events (widget);
516   attributes.event_mask |= (GDK_EXPOSURE_MASK |
517                             GDK_BUTTON_PRESS_MASK |
518                             GDK_BUTTON_RELEASE_MASK |
519                             GDK_ENTER_NOTIFY_MASK |
520                             GDK_LEAVE_NOTIFY_MASK);
521
522   if (toggle_button->draw_indicator)
523     {
524       GTK_WIDGET_SET_FLAGS (toggle_button, GTK_NO_WINDOW);
525       attributes.wclass = GDK_INPUT_ONLY;
526       attributes_mask = GDK_WA_X | GDK_WA_Y;
527
528       widget->window = gtk_widget_get_parent_window (widget);
529       gdk_window_ref (widget->window);
530       
531       toggle_button->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
532                                                     &attributes, attributes_mask);
533       gdk_window_set_user_data (toggle_button->event_window, toggle_button);
534     }
535   else
536     {
537       GTK_WIDGET_UNSET_FLAGS (toggle_button, GTK_NO_WINDOW);
538       attributes.wclass = GDK_INPUT_OUTPUT;
539       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
540       attributes.visual = gtk_widget_get_visual (widget);
541       attributes.colormap = gtk_widget_get_colormap (widget);
542       widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
543                                        &attributes, attributes_mask);
544       gdk_window_set_user_data (widget->window, toggle_button);
545     }
546
547   widget->style = gtk_style_attach (widget->style, widget->window);
548
549   if (!toggle_button->draw_indicator)
550     gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
551 }
552   
553 static void
554 gtk_toggle_button_unrealize (GtkWidget *widget)
555 {
556   GtkToggleButton *toggle_button;
557   
558   g_return_if_fail (widget != NULL);
559   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
560
561   toggle_button = GTK_TOGGLE_BUTTON (widget);
562   
563   if (toggle_button->draw_indicator)
564     {
565       gdk_window_set_user_data (toggle_button->event_window, NULL);
566       gdk_window_destroy (toggle_button->event_window);
567       toggle_button->event_window = NULL;
568     }
569
570   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
571     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
572 }
573
574 static void
575 gtk_toggle_button_map (GtkWidget *widget)
576 {
577   g_return_if_fail (widget != NULL);
578   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
579
580   if (GTK_TOGGLE_BUTTON (widget)->draw_indicator)
581     gdk_window_show (GTK_TOGGLE_BUTTON (widget)->event_window);
582
583   GTK_WIDGET_CLASS (parent_class)->map (widget);
584 }
585
586 static void
587 gtk_toggle_button_unmap (GtkWidget *widget)
588 {
589   g_return_if_fail (widget != NULL);
590   g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
591
592   if (GTK_TOGGLE_BUTTON (widget)->draw_indicator)
593     gdk_window_hide (GTK_TOGGLE_BUTTON (widget)->event_window);
594
595   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
596 }