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