]> Pileus Git - ~andy/gtk/blob - gtk/gtkcheckbutton.c
API: Rename gtk_cairo_paint_*() to gtk_paint_*()
[~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 gboolean gtk_check_button_draw            (GtkWidget           *widget,
47                                                   cairo_t             *cr);
48 static void gtk_check_button_paint               (GtkWidget           *widget,
49                                                   cairo_t             *cr);
50 static void gtk_check_button_draw_indicator      (GtkCheckButton      *check_button,
51                                                   cairo_t             *cr);
52 static void gtk_real_check_button_draw_indicator (GtkCheckButton      *check_button,
53                                                   cairo_t             *cr);
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->draw = gtk_check_button_draw;
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                         cairo_t      *cr)
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, cr);
150
151   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
152   if (gtk_widget_has_focus (widget))
153     {
154       GtkStateType state = gtk_widget_get_state (widget);
155       GtkStyle *style = gtk_widget_get_style (widget);
156       GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
157       GtkAllocation allocation;
158
159       gtk_widget_get_allocation (widget, &allocation);
160
161       if (interior_focus && child && gtk_widget_get_visible (child))
162         {
163           GtkAllocation child_allocation;
164
165           gtk_widget_get_allocation (child, &child_allocation);
166           gtk_paint_focus (style, cr, state,
167                            widget, "checkbutton",
168                            child_allocation.x - allocation.x - focus_width - focus_pad,
169                            child_allocation.y - allocation.y - focus_width - focus_pad,
170                            child_allocation.width + 2 * (focus_width + focus_pad),
171                            child_allocation.height + 2 * (focus_width + focus_pad));
172         }
173       else
174         {
175           gtk_paint_focus (style, cr, state,
176                            widget, "checkbutton",
177                            border_width,
178                            border_width,
179                            allocation.width - 2 * border_width,
180                            allocation.height - 2 * border_width);
181         }
182     }
183 }
184
185 void
186 _gtk_check_button_get_props (GtkCheckButton *check_button,
187                              gint           *indicator_size,
188                              gint           *indicator_spacing)
189 {
190   GtkWidget *widget =  GTK_WIDGET (check_button);
191
192   if (indicator_size)
193     gtk_widget_style_get (widget, "indicator-size", indicator_size, NULL);
194
195   if (indicator_spacing)
196     gtk_widget_style_get (widget, "indicator-spacing", indicator_spacing, NULL);
197 }
198
199 static void
200 gtk_check_button_size_request (GtkWidget      *widget,
201                                GtkRequisition *requisition)
202 {
203   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (widget);
204   
205   if (toggle_button->draw_indicator)
206     {
207       GtkWidget *child;
208       gint temp;
209       gint indicator_size;
210       gint indicator_spacing;
211       gint focus_width;
212       gint focus_pad;
213       guint border_width;
214
215       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
216
217       gtk_widget_style_get (GTK_WIDGET (widget),
218                             "focus-line-width", &focus_width,
219                             "focus-padding", &focus_pad,
220                             NULL);
221  
222       requisition->width = border_width * 2;
223       requisition->height = border_width * 2;
224
225       _gtk_check_button_get_props (GTK_CHECK_BUTTON (widget),
226                                    &indicator_size, &indicator_spacing);
227       
228       child = gtk_bin_get_child (GTK_BIN (widget));
229       if (child && gtk_widget_get_visible (child))
230         {
231           GtkRequisition child_requisition;
232
233           gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
234                                      &child_requisition, NULL);
235
236           requisition->width += child_requisition.width + indicator_spacing;
237           requisition->height += child_requisition.height;
238         }
239       
240       requisition->width += (indicator_size + indicator_spacing * 2 + 2 * (focus_width + focus_pad));
241       
242       temp = indicator_size + indicator_spacing * 2;
243       requisition->height = MAX (requisition->height, temp) + 2 * (focus_width + focus_pad);
244     }
245   else
246     GTK_WIDGET_CLASS (gtk_check_button_parent_class)->size_request (widget, requisition);
247 }
248
249 static void
250 gtk_check_button_size_allocate (GtkWidget     *widget,
251                                 GtkAllocation *allocation)
252 {
253   GtkCheckButton *check_button;
254   GtkToggleButton *toggle_button;
255   GtkButton *button;
256   GtkAllocation child_allocation;
257
258   button = GTK_BUTTON (widget);
259   check_button = GTK_CHECK_BUTTON (widget);
260   toggle_button = GTK_TOGGLE_BUTTON (widget);
261
262   if (toggle_button->draw_indicator)
263     {
264       GtkWidget *child;
265       gint indicator_size;
266       gint indicator_spacing;
267       gint focus_width;
268       gint focus_pad;
269       
270       _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
271       gtk_widget_style_get (widget,
272                             "focus-line-width", &focus_width,
273                             "focus-padding", &focus_pad,
274                             NULL);
275
276       gtk_widget_set_allocation (widget, allocation);
277
278       if (gtk_widget_get_realized (widget))
279         gdk_window_move_resize (button->event_window,
280                                 allocation->x, allocation->y,
281                                 allocation->width, allocation->height);
282
283       child = gtk_bin_get_child (GTK_BIN (button));
284       if (child && gtk_widget_get_visible (child))
285         {
286           GtkRequisition child_requisition;
287           guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
288
289           gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
290                                      &child_requisition, NULL);
291
292           child_allocation.width = MIN (child_requisition.width,
293                                         allocation->width -
294                                         ((border_width + focus_width + focus_pad) * 2
295                                          + indicator_size + indicator_spacing * 3));
296           child_allocation.width = MAX (child_allocation.width, 1);
297
298           child_allocation.height = MIN (child_requisition.height,
299                                          allocation->height - (border_width + focus_width + focus_pad) * 2);
300           child_allocation.height = MAX (child_allocation.height, 1);
301           
302           child_allocation.x = (border_width + indicator_size + indicator_spacing * 3 +
303                                 allocation->x + focus_width + focus_pad);
304           child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
305
306           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
307             child_allocation.x = allocation->x + allocation->width
308               - (child_allocation.x - allocation->x + child_allocation.width);
309           
310           gtk_widget_size_allocate (child, &child_allocation);
311         }
312     }
313   else
314     GTK_WIDGET_CLASS (gtk_check_button_parent_class)->size_allocate (widget, allocation);
315 }
316
317 static gint
318 gtk_check_button_draw (GtkWidget *widget,
319                        cairo_t   *cr)
320 {
321   GtkToggleButton *toggle_button;
322   GtkBin *bin;
323   GtkWidget *child;
324   
325   toggle_button = GTK_TOGGLE_BUTTON (widget);
326   bin = GTK_BIN (widget);
327   
328   if (toggle_button->draw_indicator)
329     {
330       gtk_check_button_paint (widget, cr);
331
332       child = gtk_bin_get_child (bin);
333       if (child)
334         gtk_container_propagate_draw (GTK_CONTAINER (widget),
335                                       child,
336                                       cr);
337     }
338   else if (GTK_WIDGET_CLASS (gtk_check_button_parent_class)->draw)
339     GTK_WIDGET_CLASS (gtk_check_button_parent_class)->draw (widget, cr);
340
341   return FALSE;
342 }
343
344
345 static void
346 gtk_check_button_draw_indicator (GtkCheckButton *check_button,
347                                  cairo_t        *cr)
348 {
349   GtkCheckButtonClass *class = GTK_CHECK_BUTTON_GET_CLASS (check_button);
350
351   if (class->draw_indicator)
352     class->draw_indicator (check_button, cr);
353 }
354
355 static void
356 gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
357                                       cairo_t        *cr)
358 {
359   GtkWidget *widget;
360   GtkWidget *child;
361   GtkButton *button;
362   GtkToggleButton *toggle_button;
363   GtkStateType state_type;
364   GtkShadowType shadow_type;
365   gint x, y;
366   gint indicator_size;
367   gint indicator_spacing;
368   gint focus_width;
369   gint focus_pad;
370   guint border_width;
371   gboolean interior_focus;
372   GtkAllocation allocation;
373   GtkStyle *style;
374   GdkWindow *window;
375
376   widget = GTK_WIDGET (check_button);
377   button = GTK_BUTTON (check_button);
378   toggle_button = GTK_TOGGLE_BUTTON (check_button);
379
380   gtk_widget_get_allocation (widget, &allocation);
381   style = gtk_widget_get_style (widget);
382   window = gtk_widget_get_window (widget);
383
384   gtk_widget_style_get (widget, 
385                         "interior-focus", &interior_focus,
386                         "focus-line-width", &focus_width, 
387                         "focus-padding", &focus_pad, 
388                         NULL);
389
390   _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
391
392   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
393
394   x = indicator_spacing + border_width;
395   y = (allocation.height - indicator_size) / 2;
396
397   child = gtk_bin_get_child (GTK_BIN (check_button));
398   if (!interior_focus || !(child && gtk_widget_get_visible (child)))
399     x += focus_width + focus_pad;      
400
401   if (toggle_button->inconsistent)
402     shadow_type = GTK_SHADOW_ETCHED_IN;
403   else if (toggle_button->active)
404     shadow_type = GTK_SHADOW_IN;
405   else
406     shadow_type = GTK_SHADOW_OUT;
407
408   if (button->activate_timeout || (button->button_down && button->in_button))
409     state_type = GTK_STATE_ACTIVE;
410   else if (button->in_button)
411     state_type = GTK_STATE_PRELIGHT;
412   else if (!gtk_widget_is_sensitive (widget))
413     state_type = GTK_STATE_INSENSITIVE;
414   else
415     state_type = GTK_STATE_NORMAL;
416   
417   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
418     x = allocation.width - (indicator_size + x);
419
420   if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
421     {
422
423       gtk_paint_flat_box (style, cr, GTK_STATE_PRELIGHT,
424                           GTK_SHADOW_ETCHED_OUT, 
425                           widget, "checkbutton",
426                           border_width, border_width,
427                           allocation.width - (2 * border_width),
428                           allocation.height - (2 * border_width));
429     }
430
431   gtk_paint_check (style, cr,
432                    state_type, shadow_type,
433                    widget, "checkbutton",
434                    x, y, indicator_size, indicator_size);
435 }