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