]> Pileus Git - ~andy/gtk/blob - gtk/gtkcheckbutton.c
checkbutton: Remove redundant is_drawable() check
[~andy/gtk] / gtk / gtkcheckbutton.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-2000.  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 "config.h"
28
29 #include "gtkcheckbutton.h"
30
31 #include "gtklabel.h"
32 #include "gtksizerequest.h"
33
34 #include "gtkprivate.h"
35 #include "gtkintl.h"
36
37
38 #define INDICATOR_SIZE     13
39 #define INDICATOR_SPACING  2
40
41
42 static void gtk_check_button_size_request        (GtkWidget           *widget,
43                                                   GtkRequisition      *requisition);
44 static void gtk_check_button_size_allocate       (GtkWidget           *widget,
45                                                   GtkAllocation       *allocation);
46 static gint gtk_check_button_expose              (GtkWidget           *widget,
47                                                   GdkEventExpose      *event);
48 static void gtk_check_button_paint               (GtkWidget           *widget,
49                                                   GdkRectangle        *area);
50 static void gtk_check_button_draw_indicator      (GtkCheckButton      *check_button,
51                                                   GdkRectangle        *area);
52 static void gtk_real_check_button_draw_indicator (GtkCheckButton      *check_button,
53                                                   GdkRectangle        *area);
54
55 G_DEFINE_TYPE (GtkCheckButton, gtk_check_button, GTK_TYPE_TOGGLE_BUTTON)
56
57 static void
58 gtk_check_button_class_init (GtkCheckButtonClass *class)
59 {
60   GtkWidgetClass *widget_class;
61   
62   widget_class = (GtkWidgetClass*) class;
63   
64   widget_class->size_request = gtk_check_button_size_request;
65   widget_class->size_allocate = gtk_check_button_size_allocate;
66   widget_class->expose_event = gtk_check_button_expose;
67
68   class->draw_indicator = gtk_real_check_button_draw_indicator;
69
70   gtk_widget_class_install_style_property (widget_class,
71                                            g_param_spec_int ("indicator-size",
72                                                              P_("Indicator Size"),
73                                                              P_("Size of check or radio indicator"),
74                                                              0,
75                                                              G_MAXINT,
76                                                              INDICATOR_SIZE,
77                                                              GTK_PARAM_READABLE));
78   gtk_widget_class_install_style_property (widget_class,
79                                            g_param_spec_int ("indicator-spacing",
80                                                              P_("Indicator Spacing"),
81                                                              P_("Spacing around check or radio indicator"),
82                                                              0,
83                                                              G_MAXINT,
84                                                              INDICATOR_SPACING,
85                                                              GTK_PARAM_READABLE));
86 }
87
88 static void
89 gtk_check_button_init (GtkCheckButton *check_button)
90 {
91   gtk_widget_set_has_window (GTK_WIDGET (check_button), FALSE);
92   gtk_widget_set_receives_default (GTK_WIDGET (check_button), FALSE);
93   GTK_TOGGLE_BUTTON (check_button)->draw_indicator = TRUE;
94   GTK_BUTTON (check_button)->depress_on_activate = FALSE;
95 }
96
97 GtkWidget*
98 gtk_check_button_new (void)
99 {
100   return g_object_new (GTK_TYPE_CHECK_BUTTON, NULL);
101 }
102
103
104 GtkWidget*
105 gtk_check_button_new_with_label (const gchar *label)
106 {
107   return g_object_new (GTK_TYPE_CHECK_BUTTON, "label", label, NULL);
108 }
109
110 /**
111  * gtk_check_button_new_with_mnemonic:
112  * @label: The text of the button, with an underscore in front of the
113  *         mnemonic character
114  * @returns: a new #GtkCheckButton
115  *
116  * Creates a new #GtkCheckButton containing a label. The label
117  * will be created using gtk_label_new_with_mnemonic(), so underscores
118  * in @label indicate the mnemonic for the check button.
119  **/
120 GtkWidget*
121 gtk_check_button_new_with_mnemonic (const gchar *label)
122 {
123   return g_object_new (GTK_TYPE_CHECK_BUTTON, 
124                        "label", label, 
125                        "use-underline", TRUE, 
126                        NULL);
127 }
128
129
130 /* This should only be called when toggle_button->draw_indicator
131  * is true.
132  */
133 static void
134 gtk_check_button_paint (GtkWidget    *widget,
135                         GdkRectangle *area)
136 {
137   GtkCheckButton *check_button = GTK_CHECK_BUTTON (widget);
138   gint border_width;
139   gint interior_focus;
140   gint focus_width;
141   gint focus_pad;
142       
143   gtk_widget_style_get (widget,
144                         "interior-focus", &interior_focus,
145                         "focus-line-width", &focus_width,
146                         "focus-padding", &focus_pad,
147                         NULL);
148
149   gtk_check_button_draw_indicator (check_button, area);
150
151   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
152   if (gtk_widget_has_focus (widget))
153     {
154       GtkStateType state;
155       GtkStyle *style;
156       GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
157       GdkWindow *window;
158
159       style = gtk_widget_get_style (widget);
160       window = gtk_widget_get_window (widget);
161       state = gtk_widget_get_state (widget);
162
163       if (interior_focus && child && gtk_widget_get_visible (child))
164         {
165           GtkAllocation child_allocation;
166
167           gtk_widget_get_allocation (child, &child_allocation);
168           gtk_paint_focus (style, window, state,
169                            area, widget, "checkbutton",
170                            child_allocation.x - focus_width - focus_pad,
171                            child_allocation.y - focus_width - focus_pad,
172                            child_allocation.width + 2 * (focus_width + focus_pad),
173                            child_allocation.height + 2 * (focus_width + focus_pad));
174         }
175       else
176         {
177           GtkAllocation allocation;
178
179           gtk_widget_get_allocation (widget, &allocation);
180           gtk_paint_focus (style, window, state,
181                            area, widget, "checkbutton",
182                            allocation.x + border_width,
183                            allocation.y + border_width,
184                            allocation.width - 2 * border_width,
185                            allocation.height - 2 * border_width);
186         }
187     }
188 }
189
190 void
191 _gtk_check_button_get_props (GtkCheckButton *check_button,
192                              gint           *indicator_size,
193                              gint           *indicator_spacing)
194 {
195   GtkWidget *widget =  GTK_WIDGET (check_button);
196
197   if (indicator_size)
198     gtk_widget_style_get (widget, "indicator-size", indicator_size, NULL);
199
200   if (indicator_spacing)
201     gtk_widget_style_get (widget, "indicator-spacing", indicator_spacing, NULL);
202 }
203
204 static void
205 gtk_check_button_size_request (GtkWidget      *widget,
206                                GtkRequisition *requisition)
207 {
208   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (widget);
209   
210   if (toggle_button->draw_indicator)
211     {
212       GtkWidget *child;
213       gint temp;
214       gint indicator_size;
215       gint indicator_spacing;
216       gint focus_width;
217       gint focus_pad;
218       guint border_width;
219
220       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
221
222       gtk_widget_style_get (GTK_WIDGET (widget),
223                             "focus-line-width", &focus_width,
224                             "focus-padding", &focus_pad,
225                             NULL);
226  
227       requisition->width = border_width * 2;
228       requisition->height = border_width * 2;
229
230       _gtk_check_button_get_props (GTK_CHECK_BUTTON (widget),
231                                    &indicator_size, &indicator_spacing);
232       
233       child = gtk_bin_get_child (GTK_BIN (widget));
234       if (child && gtk_widget_get_visible (child))
235         {
236           GtkRequisition child_requisition;
237
238           gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
239                                      &child_requisition, NULL);
240
241           requisition->width += child_requisition.width + indicator_spacing;
242           requisition->height += child_requisition.height;
243         }
244       
245       requisition->width += (indicator_size + indicator_spacing * 2 + 2 * (focus_width + focus_pad));
246       
247       temp = indicator_size + indicator_spacing * 2;
248       requisition->height = MAX (requisition->height, temp) + 2 * (focus_width + focus_pad);
249     }
250   else
251     GTK_WIDGET_CLASS (gtk_check_button_parent_class)->size_request (widget, requisition);
252 }
253
254 static void
255 gtk_check_button_size_allocate (GtkWidget     *widget,
256                                 GtkAllocation *allocation)
257 {
258   GtkCheckButton *check_button;
259   GtkToggleButton *toggle_button;
260   GtkButton *button;
261   GtkAllocation child_allocation;
262
263   button = GTK_BUTTON (widget);
264   check_button = GTK_CHECK_BUTTON (widget);
265   toggle_button = GTK_TOGGLE_BUTTON (widget);
266
267   if (toggle_button->draw_indicator)
268     {
269       GtkWidget *child;
270       gint indicator_size;
271       gint indicator_spacing;
272       gint focus_width;
273       gint focus_pad;
274       
275       _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
276       gtk_widget_style_get (widget,
277                             "focus-line-width", &focus_width,
278                             "focus-padding", &focus_pad,
279                             NULL);
280
281       gtk_widget_set_allocation (widget, allocation);
282
283       if (gtk_widget_get_realized (widget))
284         gdk_window_move_resize (button->event_window,
285                                 allocation->x, allocation->y,
286                                 allocation->width, allocation->height);
287
288       child = gtk_bin_get_child (GTK_BIN (button));
289       if (child && gtk_widget_get_visible (child))
290         {
291           GtkRequisition child_requisition;
292           guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
293
294           gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
295                                      &child_requisition, NULL);
296
297           child_allocation.width = MIN (child_requisition.width,
298                                         allocation->width -
299                                         ((border_width + focus_width + focus_pad) * 2
300                                          + indicator_size + indicator_spacing * 3));
301           child_allocation.width = MAX (child_allocation.width, 1);
302
303           child_allocation.height = MIN (child_requisition.height,
304                                          allocation->height - (border_width + focus_width + focus_pad) * 2);
305           child_allocation.height = MAX (child_allocation.height, 1);
306           
307           child_allocation.x = (border_width + indicator_size + indicator_spacing * 3 +
308                                 allocation->x + focus_width + focus_pad);
309           child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
310
311           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
312             child_allocation.x = allocation->x + allocation->width
313               - (child_allocation.x - allocation->x + child_allocation.width);
314           
315           gtk_widget_size_allocate (child, &child_allocation);
316         }
317     }
318   else
319     GTK_WIDGET_CLASS (gtk_check_button_parent_class)->size_allocate (widget, allocation);
320 }
321
322 static gint
323 gtk_check_button_expose (GtkWidget      *widget,
324                          GdkEventExpose *event)
325 {
326   GtkToggleButton *toggle_button;
327   GtkBin *bin;
328   GtkWidget *child;
329   
330   toggle_button = GTK_TOGGLE_BUTTON (widget);
331   bin = GTK_BIN (widget);
332   
333   if (gtk_widget_is_drawable (widget))
334     {
335       if (toggle_button->draw_indicator)
336         {
337           gtk_check_button_paint (widget, &event->area);
338
339           child = gtk_bin_get_child (bin);
340           if (child)
341             gtk_container_propagate_expose (GTK_CONTAINER (widget),
342                                             child,
343                                             event);
344         }
345       else if (GTK_WIDGET_CLASS (gtk_check_button_parent_class)->expose_event)
346         GTK_WIDGET_CLASS (gtk_check_button_parent_class)->expose_event (widget, event);
347     }
348   
349   return FALSE;
350 }
351
352
353 static void
354 gtk_check_button_draw_indicator (GtkCheckButton *check_button,
355                                  GdkRectangle   *area)
356 {
357   GtkCheckButtonClass *class;
358   
359   g_return_if_fail (GTK_IS_CHECK_BUTTON (check_button));
360   
361   class = GTK_CHECK_BUTTON_GET_CLASS (check_button);
362
363   if (class->draw_indicator)
364     class->draw_indicator (check_button, area);
365 }
366
367 static void
368 gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
369                                       GdkRectangle   *area)
370 {
371   GtkWidget *widget;
372   GtkWidget *child;
373   GtkButton *button;
374   GtkToggleButton *toggle_button;
375   GtkStateType state_type;
376   GtkShadowType shadow_type;
377   gint x, y;
378   gint indicator_size;
379   gint indicator_spacing;
380   gint focus_width;
381   gint focus_pad;
382   guint border_width;
383   gboolean interior_focus;
384   GtkAllocation allocation;
385   GtkStyle *style;
386   GdkWindow *window;
387
388   widget = GTK_WIDGET (check_button);
389   button = GTK_BUTTON (check_button);
390   toggle_button = GTK_TOGGLE_BUTTON (check_button);
391
392   gtk_widget_get_allocation (widget, &allocation);
393   style = gtk_widget_get_style (widget);
394   window = gtk_widget_get_window (widget);
395
396   gtk_widget_style_get (widget, 
397                         "interior-focus", &interior_focus,
398                         "focus-line-width", &focus_width, 
399                         "focus-padding", &focus_pad, 
400                         NULL);
401
402   _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
403
404   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
405
406   x = allocation.x + indicator_spacing + border_width;
407   y = allocation.y + (allocation.height - indicator_size) / 2;
408
409   child = gtk_bin_get_child (GTK_BIN (check_button));
410   if (!interior_focus || !(child && gtk_widget_get_visible (child)))
411     x += focus_width + focus_pad;      
412
413   if (toggle_button->inconsistent)
414     shadow_type = GTK_SHADOW_ETCHED_IN;
415   else if (toggle_button->active)
416     shadow_type = GTK_SHADOW_IN;
417   else
418     shadow_type = GTK_SHADOW_OUT;
419
420   if (button->activate_timeout || (button->button_down && button->in_button))
421     state_type = GTK_STATE_ACTIVE;
422   else if (button->in_button)
423     state_type = GTK_STATE_PRELIGHT;
424   else if (!gtk_widget_is_sensitive (widget))
425     state_type = GTK_STATE_INSENSITIVE;
426   else
427     state_type = GTK_STATE_NORMAL;
428   
429   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
430     x = allocation.x + allocation.width - (indicator_size + x - allocation.x);
431
432   if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
433     {
434       GdkRectangle restrict_area;
435       GdkRectangle new_area;
436
437       restrict_area.x = allocation.x + border_width;
438       restrict_area.y = allocation.y + border_width;
439       restrict_area.width = allocation.width - (2 * border_width);
440       restrict_area.height = allocation.height - (2 * border_width);
441
442       if (gdk_rectangle_intersect (area, &restrict_area, &new_area))
443         {
444           gtk_paint_flat_box (style, window, GTK_STATE_PRELIGHT,
445                               GTK_SHADOW_ETCHED_OUT, 
446                               area, widget, "checkbutton",
447                               new_area.x, new_area.y,
448                               new_area.width, new_area.height);
449         }
450     }
451
452   gtk_paint_check (style, window,
453                    state_type, shadow_type,
454                    area, widget, "checkbutton",
455                    x, y, indicator_size, indicator_size);
456 }