]> Pileus Git - ~andy/gtk/blob - gtk/gtkcheckmenuitem.c
Use accessor functions to acces GtkContainer
[~andy/gtk] / gtk / gtkcheckmenuitem.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-2001.  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 #include "gtkcheckmenuitem.h"
29 #include "gtkaccellabel.h"
30 #include "gtkactivatable.h"
31 #include "gtktoggleaction.h"
32 #include "gtkmarshalers.h"
33 #include "gtkprivate.h"
34 #include "gtkintl.h"
35
36
37
38 struct _GtkCheckMenuItemPriv
39 {
40   guint active             : 1;
41   guint always_show_toggle : 1;
42   guint draw_as_radio      : 1;
43   guint inconsistent       : 1;
44 };
45
46 enum {
47   TOGGLED,
48   LAST_SIGNAL
49 };
50
51 enum {
52   PROP_0,
53   PROP_ACTIVE,
54   PROP_INCONSISTENT,
55   PROP_DRAW_AS_RADIO
56 };
57
58 static gint gtk_check_menu_item_expose               (GtkWidget             *widget,
59                                                       GdkEventExpose        *event);
60 static void gtk_check_menu_item_activate             (GtkMenuItem           *menu_item);
61 static void gtk_check_menu_item_toggle_size_request  (GtkMenuItem           *menu_item,
62                                                       gint                  *requisition);
63 static void gtk_check_menu_item_draw_indicator       (GtkCheckMenuItem      *check_menu_item,
64                                                       GdkRectangle          *area);
65 static void gtk_real_check_menu_item_draw_indicator  (GtkCheckMenuItem      *check_menu_item,
66                                                       GdkRectangle          *area);
67 static void gtk_check_menu_item_set_property         (GObject               *object,
68                                                       guint                  prop_id,
69                                                       const GValue          *value,
70                                                       GParamSpec            *pspec);
71 static void gtk_check_menu_item_get_property         (GObject               *object,
72                                                       guint                  prop_id,
73                                                       GValue                *value,
74                                                       GParamSpec            *pspec);
75
76 static void gtk_check_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
77 static void gtk_check_menu_item_update                     (GtkActivatable       *activatable,
78                                                             GtkAction            *action,
79                                                             const gchar          *property_name);
80 static void gtk_check_menu_item_sync_action_properties     (GtkActivatable       *activatable,
81                                                             GtkAction            *action);
82
83 static GtkActivatableIface *parent_activatable_iface;
84 static guint                check_menu_item_signals[LAST_SIGNAL] = { 0 };
85
86 G_DEFINE_TYPE_WITH_CODE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_ITEM,
87                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
88                                                 gtk_check_menu_item_activatable_interface_init))
89
90 static void
91 gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass)
92 {
93   GObjectClass *gobject_class;
94   GtkWidgetClass *widget_class;
95   GtkMenuItemClass *menu_item_class;
96   
97   gobject_class = G_OBJECT_CLASS (klass);
98   widget_class = (GtkWidgetClass*) klass;
99   menu_item_class = (GtkMenuItemClass*) klass;
100   
101   gobject_class->set_property = gtk_check_menu_item_set_property;
102   gobject_class->get_property = gtk_check_menu_item_get_property;
103
104   g_object_class_install_property (gobject_class,
105                                    PROP_ACTIVE,
106                                    g_param_spec_boolean ("active",
107                                                          P_("Active"),
108                                                          P_("Whether the menu item is checked"),
109                                                          FALSE,
110                                                          GTK_PARAM_READWRITE));
111   
112   g_object_class_install_property (gobject_class,
113                                    PROP_INCONSISTENT,
114                                    g_param_spec_boolean ("inconsistent",
115                                                          P_("Inconsistent"),
116                                                          P_("Whether to display an \"inconsistent\" state"),
117                                                          FALSE,
118                                                          GTK_PARAM_READWRITE));
119   
120   g_object_class_install_property (gobject_class,
121                                    PROP_DRAW_AS_RADIO,
122                                    g_param_spec_boolean ("draw-as-radio",
123                                                          P_("Draw as radio menu item"),
124                                                          P_("Whether the menu item looks like a radio menu item"),
125                                                          FALSE,
126                                                          GTK_PARAM_READWRITE));
127   
128   gtk_widget_class_install_style_property (widget_class,
129                                            g_param_spec_int ("indicator-size",
130                                                              P_("Indicator Size"),
131                                                              P_("Size of check or radio indicator"),
132                                                              0,
133                                                              G_MAXINT,
134                                                              13,
135                                                              GTK_PARAM_READABLE));
136
137   widget_class->expose_event = gtk_check_menu_item_expose;
138   
139   menu_item_class->activate = gtk_check_menu_item_activate;
140   menu_item_class->hide_on_activate = FALSE;
141   menu_item_class->toggle_size_request = gtk_check_menu_item_toggle_size_request;
142   
143   klass->toggled = NULL;
144   klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
145
146   check_menu_item_signals[TOGGLED] =
147     g_signal_new (I_("toggled"),
148                   G_OBJECT_CLASS_TYPE (gobject_class),
149                   G_SIGNAL_RUN_FIRST,
150                   G_STRUCT_OFFSET (GtkCheckMenuItemClass, toggled),
151                   NULL, NULL,
152                   _gtk_marshal_VOID__VOID,
153                   G_TYPE_NONE, 0);
154
155   g_type_class_add_private (klass, sizeof (GtkCheckMenuItemPriv));
156 }
157
158 static void 
159 gtk_check_menu_item_activatable_interface_init (GtkActivatableIface  *iface)
160 {
161   parent_activatable_iface = g_type_interface_peek_parent (iface);
162   iface->update = gtk_check_menu_item_update;
163   iface->sync_action_properties = gtk_check_menu_item_sync_action_properties;
164 }
165
166 static void
167 gtk_check_menu_item_update (GtkActivatable *activatable,
168                             GtkAction      *action,
169                             const gchar    *property_name)
170 {
171   GtkCheckMenuItem *check_menu_item;
172
173   check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
174
175   parent_activatable_iface->update (activatable, action, property_name);
176
177   if (strcmp (property_name, "active") == 0)
178     {
179       gtk_action_block_activate (action);
180       gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
181       gtk_action_unblock_activate (action);
182     }
183
184   if (!gtk_activatable_get_use_action_appearance (activatable))
185     return;
186
187   if (strcmp (property_name, "draw-as-radio") == 0)
188     gtk_check_menu_item_set_draw_as_radio (check_menu_item,
189                                            gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
190 }
191
192 static void
193 gtk_check_menu_item_sync_action_properties (GtkActivatable *activatable,
194                                             GtkAction      *action)
195 {
196   GtkCheckMenuItem *check_menu_item;
197
198   check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
199
200   parent_activatable_iface->sync_action_properties (activatable, action);
201
202   if (!GTK_IS_TOGGLE_ACTION (action))
203     return;
204
205   gtk_action_block_activate (action);
206   gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
207   gtk_action_unblock_activate (action);
208   
209   if (!gtk_activatable_get_use_action_appearance (activatable))
210     return;
211
212   gtk_check_menu_item_set_draw_as_radio (check_menu_item,
213                                          gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
214 }
215
216 GtkWidget*
217 gtk_check_menu_item_new (void)
218 {
219   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, NULL);
220 }
221
222 GtkWidget*
223 gtk_check_menu_item_new_with_label (const gchar *label)
224 {
225   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, 
226                        "label", label,
227                        NULL);
228 }
229
230
231 /**
232  * gtk_check_menu_item_new_with_mnemonic:
233  * @label: The text of the button, with an underscore in front of the
234  *         mnemonic character
235  * @returns: a new #GtkCheckMenuItem
236  *
237  * Creates a new #GtkCheckMenuItem containing a label. The label
238  * will be created using gtk_label_new_with_mnemonic(), so underscores
239  * in @label indicate the mnemonic for the menu item.
240  **/
241 GtkWidget*
242 gtk_check_menu_item_new_with_mnemonic (const gchar *label)
243 {
244   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, 
245                        "label", label,
246                        "use-underline", TRUE,
247                        NULL);
248 }
249
250 void
251 gtk_check_menu_item_set_active (GtkCheckMenuItem *check_menu_item,
252                                 gboolean          is_active)
253 {
254   GtkCheckMenuItemPriv *priv;
255
256   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
257
258   priv = check_menu_item->priv;
259
260   is_active = is_active != 0;
261
262   if (priv->active != is_active)
263     gtk_menu_item_activate (GTK_MENU_ITEM (check_menu_item));
264 }
265
266 /**
267  * gtk_check_menu_item_get_active:
268  * @check_menu_item: a #GtkCheckMenuItem
269  * 
270  * Returns whether the check menu item is active. See
271  * gtk_check_menu_item_set_active ().
272  * 
273  * Return value: %TRUE if the menu item is checked.
274  */
275 gboolean
276 gtk_check_menu_item_get_active (GtkCheckMenuItem *check_menu_item)
277 {
278   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
279
280   return check_menu_item->priv->active;
281 }
282
283 static void
284 gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
285                                          gint        *requisition)
286 {
287   guint toggle_spacing;
288   guint indicator_size;
289   
290   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (menu_item));
291   
292   gtk_widget_style_get (GTK_WIDGET (menu_item),
293                         "toggle-spacing", &toggle_spacing,
294                         "indicator-size", &indicator_size,
295                         NULL);
296
297   *requisition = indicator_size + toggle_spacing;
298 }
299
300 void
301 gtk_check_menu_item_toggled (GtkCheckMenuItem *check_menu_item)
302 {
303   g_signal_emit (check_menu_item, check_menu_item_signals[TOGGLED], 0);
304 }
305
306 /**
307  * gtk_check_menu_item_set_inconsistent:
308  * @check_menu_item: a #GtkCheckMenuItem
309  * @setting: %TRUE to display an "inconsistent" third state check
310  *
311  * If the user has selected a range of elements (such as some text or
312  * spreadsheet cells) that are affected by a boolean setting, and the
313  * current values in that range are inconsistent, you may want to
314  * display the check in an "in between" state. This function turns on
315  * "in between" display.  Normally you would turn off the inconsistent
316  * state again if the user explicitly selects a setting. This has to be
317  * done manually, gtk_check_menu_item_set_inconsistent() only affects
318  * visual appearance, it doesn't affect the semantics of the widget.
319  * 
320  **/
321 void
322 gtk_check_menu_item_set_inconsistent (GtkCheckMenuItem *check_menu_item,
323                                       gboolean          setting)
324 {
325   GtkCheckMenuItemPriv *priv;
326
327   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
328
329   priv = check_menu_item->priv;
330   
331   setting = setting != FALSE;
332
333   if (setting != priv->inconsistent)
334     {
335       priv->inconsistent = setting;
336       gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
337       g_object_notify (G_OBJECT (check_menu_item), "inconsistent");
338     }
339 }
340
341 /**
342  * gtk_check_menu_item_get_inconsistent:
343  * @check_menu_item: a #GtkCheckMenuItem
344  * 
345  * Retrieves the value set by gtk_check_menu_item_set_inconsistent().
346  * 
347  * Return value: %TRUE if inconsistent
348  **/
349 gboolean
350 gtk_check_menu_item_get_inconsistent (GtkCheckMenuItem *check_menu_item)
351 {
352   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
353
354   return check_menu_item->priv->inconsistent;
355 }
356
357 /**
358  * gtk_check_menu_item_set_draw_as_radio:
359  * @check_menu_item: a #GtkCheckMenuItem
360  * @draw_as_radio: whether @check_menu_item is drawn like a #GtkRadioMenuItem
361  *
362  * Sets whether @check_menu_item is drawn like a #GtkRadioMenuItem
363  *
364  * Since: 2.4
365  **/
366 void
367 gtk_check_menu_item_set_draw_as_radio (GtkCheckMenuItem *check_menu_item,
368                                        gboolean          draw_as_radio)
369 {
370   GtkCheckMenuItemPriv *priv;
371
372   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
373
374   priv = check_menu_item->priv;
375
376   draw_as_radio = draw_as_radio != FALSE;
377
378   if (draw_as_radio != priv->draw_as_radio)
379     {
380       priv->draw_as_radio = draw_as_radio;
381
382       gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
383
384       g_object_notify (G_OBJECT (check_menu_item), "draw-as-radio");
385     }
386 }
387
388 /**
389  * gtk_check_menu_item_get_draw_as_radio:
390  * @check_menu_item: a #GtkCheckMenuItem
391  * 
392  * Returns whether @check_menu_item looks like a #GtkRadioMenuItem
393  * 
394  * Return value: Whether @check_menu_item looks like a #GtkRadioMenuItem
395  * 
396  * Since: 2.4
397  **/
398 gboolean
399 gtk_check_menu_item_get_draw_as_radio (GtkCheckMenuItem *check_menu_item)
400 {
401   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
402   
403   return check_menu_item->priv->draw_as_radio;
404 }
405
406 static void
407 gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item)
408 {
409   GtkCheckMenuItemPriv *priv;
410
411   check_menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (check_menu_item,
412                                                        GTK_TYPE_CHECK_MENU_ITEM,
413                                                        GtkCheckMenuItemPriv);
414   priv = check_menu_item->priv; 
415
416   priv->active = FALSE;
417   priv->always_show_toggle = TRUE;
418 }
419
420 static gint
421 gtk_check_menu_item_expose (GtkWidget      *widget,
422                             GdkEventExpose *event)
423 {
424   if (GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->expose_event)
425     GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->expose_event (widget, event);
426
427   gtk_check_menu_item_draw_indicator (GTK_CHECK_MENU_ITEM (widget), &event->area);
428
429   return FALSE;
430 }
431
432 static void
433 gtk_check_menu_item_activate (GtkMenuItem *menu_item)
434 {
435   GtkCheckMenuItemPriv *priv;
436
437   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
438   priv = check_menu_item->priv;
439
440   priv->active = !priv->active;
441
442   gtk_check_menu_item_toggled (check_menu_item);
443   gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
444
445   GTK_MENU_ITEM_CLASS (gtk_check_menu_item_parent_class)->activate (menu_item);
446
447   g_object_notify (G_OBJECT (check_menu_item), "active");
448 }
449
450 static void
451 gtk_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
452                                     GdkRectangle     *area)
453 {
454   if (GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator)
455     GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator (check_menu_item, area);
456 }
457
458 static void
459 gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
460                                          GdkRectangle     *area)
461 {
462   GtkCheckMenuItemPriv *priv = check_menu_item->priv;
463   GtkWidget *widget;
464   GtkStateType state_type;
465   GtkShadowType shadow_type;
466   gint x, y;
467
468   widget = GTK_WIDGET (check_menu_item);
469
470   if (gtk_widget_is_drawable (widget))
471     {
472       guint border_width;
473       guint offset;
474       guint toggle_size;
475       guint toggle_spacing;
476       guint horizontal_padding;
477       guint indicator_size;
478
479       gtk_widget_style_get (widget,
480                             "toggle-spacing", &toggle_spacing,
481                             "horizontal-padding", &horizontal_padding,
482                             "indicator-size", &indicator_size,
483                             NULL);
484
485       toggle_size = GTK_MENU_ITEM (check_menu_item)->toggle_size;
486       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
487       offset = border_width + widget->style->xthickness + 2;
488
489       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
490         {
491           x = widget->allocation.x + offset + horizontal_padding +
492             (toggle_size - toggle_spacing - indicator_size) / 2;
493         }
494       else 
495         {
496           x = widget->allocation.x + widget->allocation.width -
497             offset - horizontal_padding - toggle_size + toggle_spacing +
498             (toggle_size - toggle_spacing - indicator_size) / 2;
499         }
500       
501       y = widget->allocation.y + (widget->allocation.height - indicator_size) / 2;
502
503       if (priv->active ||
504           priv->always_show_toggle ||
505           (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT))
506         {
507           state_type = gtk_widget_get_state (widget);
508           
509           if (priv->inconsistent)
510             shadow_type = GTK_SHADOW_ETCHED_IN;
511           else if (priv->active)
512             shadow_type = GTK_SHADOW_IN;
513           else 
514             shadow_type = GTK_SHADOW_OUT;
515           
516           if (!gtk_widget_is_sensitive (widget))
517             state_type = GTK_STATE_INSENSITIVE;
518
519           if (priv->draw_as_radio)
520             {
521               gtk_paint_option (widget->style, widget->window,
522                                 state_type, shadow_type,
523                                 area, widget, "option",
524                                 x, y, indicator_size, indicator_size);
525             }
526           else
527             {
528               gtk_paint_check (widget->style, widget->window,
529                                state_type, shadow_type,
530                                area, widget, "check",
531                                x, y, indicator_size, indicator_size);
532             }
533         }
534     }
535 }
536
537
538 static void
539 gtk_check_menu_item_get_property (GObject     *object,
540                                   guint        prop_id,
541                                   GValue      *value,
542                                   GParamSpec  *pspec)
543 {
544   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
545   GtkCheckMenuItemPriv *priv = checkitem->priv;
546   
547   switch (prop_id)
548     {
549     case PROP_ACTIVE:
550       g_value_set_boolean (value, priv->active);
551       break;
552     case PROP_INCONSISTENT:
553       g_value_set_boolean (value, priv->inconsistent);
554       break;
555     case PROP_DRAW_AS_RADIO:
556       g_value_set_boolean (value, priv->draw_as_radio);
557       break;
558     default:
559       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560       break;
561     }
562 }
563
564
565 static void
566 gtk_check_menu_item_set_property (GObject      *object,
567                                   guint         prop_id,
568                                   const GValue *value,
569                                   GParamSpec   *pspec)
570 {
571   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
572   
573   switch (prop_id)
574     {
575     case PROP_ACTIVE:
576       gtk_check_menu_item_set_active (checkitem, g_value_get_boolean (value));
577       break;
578     case PROP_INCONSISTENT:
579       gtk_check_menu_item_set_inconsistent (checkitem, g_value_get_boolean (value));
580       break;
581     case PROP_DRAW_AS_RADIO:
582       gtk_check_menu_item_set_draw_as_radio (checkitem, g_value_get_boolean (value));
583       break;
584     default:
585       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586       break;
587     }
588 }