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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 #include "gtkbuiltiniconprivate.h"
27 #include "gtkcheckmenuitem.h"
28 #include "gtkmenuitemprivate.h"
29 #include "gtkaccellabel.h"
30 #include "deprecated/gtkactivatable.h"
31 #include "deprecated/gtktoggleaction.h"
32 #include "gtkmarshalers.h"
33 #include "gtkprivate.h"
34 #include "gtkintl.h"
35 #include "a11y/gtkcheckmenuitemaccessible.h"
36 #include "gtkcssnodeprivate.h"
37 #include "gtkcssstylepropertyprivate.h"
38 #include "gtkwidgetprivate.h"
39 
40 /**
41  * SECTION:gtkcheckmenuitem
42  * @Short_description: A menu item with a check box
43  * @Title: GtkCheckMenuItem
44  *
45  * A #GtkCheckMenuItem is a menu item that maintains the state of a boolean
46  * value in addition to a #GtkMenuItem usual role in activating application
47  * code.
48  *
49  * A check box indicating the state of the boolean value is displayed
50  * at the left side of the #GtkMenuItem.  Activating the #GtkMenuItem
51  * toggles the value.
52  *
53  * # CSS nodes
54  *
55  * |[<!-- language="plain" -->
56  * menuitem
57  * ├── check.left
58  * ╰── <child>
59  * ]|
60  *
61  * GtkCheckMenuItem has a main CSS node with name menuitem, and a subnode
62  * with name check, which gets the .left or .right style class.
63  */
64 
65 
66 #define INDICATOR_SIZE 16
67 
68 struct _GtkCheckMenuItemPrivate
69 {
70   GtkCssGadget *indicator_gadget;
71 
72   guint active             : 1;
73   guint draw_as_radio      : 1;
74   guint inconsistent       : 1;
75 };
76 
77 enum {
78   TOGGLED,
79   LAST_SIGNAL
80 };
81 
82 enum {
83   PROP_0,
84   PROP_ACTIVE,
85   PROP_INCONSISTENT,
86   PROP_DRAW_AS_RADIO
87 };
88 
89 static gint gtk_check_menu_item_draw                 (GtkWidget             *widget,
90                                                       cairo_t               *cr);
91 static void gtk_check_menu_item_activate             (GtkMenuItem           *menu_item);
92 static void gtk_check_menu_item_toggle_size_request  (GtkMenuItem           *menu_item,
93                                                       gint                  *requisition);
94 static void gtk_real_check_menu_item_draw_indicator  (GtkCheckMenuItem      *check_menu_item,
95                                                       cairo_t               *cr);
96 static void gtk_check_menu_item_set_property         (GObject               *object,
97                                                       guint                  prop_id,
98                                                       const GValue          *value,
99                                                       GParamSpec            *pspec);
100 static void gtk_check_menu_item_get_property         (GObject               *object,
101                                                       guint                  prop_id,
102                                                       GValue                *value,
103                                                       GParamSpec            *pspec);
104 
105 static void gtk_check_menu_item_state_flags_changed (GtkWidget        *widget,
106                                                      GtkStateFlags     previous_state);
107 static void gtk_check_menu_item_direction_changed   (GtkWidget        *widget,
108                                                      GtkTextDirection  previous_dir);
109 
110 static void gtk_check_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
111 static void gtk_check_menu_item_update                     (GtkActivatable       *activatable,
112                                                             GtkAction            *action,
113                                                             const gchar          *property_name);
114 static void gtk_check_menu_item_sync_action_properties     (GtkActivatable       *activatable,
115                                                             GtkAction            *action);
116 
117 static GtkActivatableIface *parent_activatable_iface;
118 static guint                check_menu_item_signals[LAST_SIGNAL] = { 0 };
119 
120 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
121 G_DEFINE_TYPE_WITH_CODE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_ITEM,
122                          G_ADD_PRIVATE (GtkCheckMenuItem)
123                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
124                                                 gtk_check_menu_item_activatable_interface_init))
125 G_GNUC_END_IGNORE_DEPRECATIONS;
126 
127 static void
gtk_check_menu_item_size_allocate(GtkWidget * widget,GtkAllocation * allocation)128 gtk_check_menu_item_size_allocate (GtkWidget     *widget,
129                                    GtkAllocation *allocation)
130 {
131   GtkAllocation clip, widget_clip;
132   GtkAllocation content_alloc, indicator_alloc;
133   GtkCssGadget *menu_item_gadget;
134   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget);
135   GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
136   gint content_baseline, toggle_size;
137 
138   GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->size_allocate
139     (widget, allocation);
140 
141   menu_item_gadget = _gtk_menu_item_get_gadget (GTK_MENU_ITEM (widget));
142   gtk_css_gadget_get_content_allocation (menu_item_gadget,
143                                          &content_alloc, &content_baseline);
144 
145   gtk_css_gadget_get_preferred_size (priv->indicator_gadget,
146                                      GTK_ORIENTATION_HORIZONTAL,
147                                      -1,
148                                      &indicator_alloc.width, NULL,
149                                      NULL, NULL);
150   gtk_css_gadget_get_preferred_size (priv->indicator_gadget,
151                                      GTK_ORIENTATION_VERTICAL,
152                                      -1,
153                                      &indicator_alloc.height, NULL,
154                                      NULL, NULL);
155   toggle_size = GTK_MENU_ITEM (check_menu_item)->priv->toggle_size;
156 
157   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
158     indicator_alloc.x = content_alloc.x +
159       (toggle_size - indicator_alloc.width) / 2;
160   else
161     indicator_alloc.x = content_alloc.x + content_alloc.width - toggle_size +
162       (toggle_size - indicator_alloc.width) / 2;
163 
164   indicator_alloc.y = content_alloc.y +
165     (content_alloc.height - indicator_alloc.height) / 2;
166 
167   gtk_css_gadget_allocate (check_menu_item->priv->indicator_gadget,
168                            &indicator_alloc,
169                            content_baseline,
170                            &clip);
171 
172   gtk_widget_get_clip (widget, &widget_clip);
173   gdk_rectangle_union (&widget_clip, &clip, &widget_clip);
174   gtk_widget_set_clip (widget, &widget_clip);
175 }
176 
177 static void
gtk_check_menu_item_finalize(GObject * object)178 gtk_check_menu_item_finalize (GObject *object)
179 {
180   GtkCheckMenuItemPrivate *priv = GTK_CHECK_MENU_ITEM (object)->priv;
181 
182   g_clear_object (&priv->indicator_gadget);
183 
184   G_OBJECT_CLASS (gtk_check_menu_item_parent_class)->finalize (object);
185 }
186 
187 static void
gtk_check_menu_item_class_init(GtkCheckMenuItemClass * klass)188 gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass)
189 {
190   GObjectClass *gobject_class;
191   GtkWidgetClass *widget_class;
192   GtkMenuItemClass *menu_item_class;
193 
194   gobject_class = G_OBJECT_CLASS (klass);
195   widget_class = (GtkWidgetClass*) klass;
196   menu_item_class = (GtkMenuItemClass*) klass;
197 
198   gobject_class->set_property = gtk_check_menu_item_set_property;
199   gobject_class->get_property = gtk_check_menu_item_get_property;
200   gobject_class->finalize = gtk_check_menu_item_finalize;
201 
202   widget_class->size_allocate = gtk_check_menu_item_size_allocate;
203   widget_class->state_flags_changed = gtk_check_menu_item_state_flags_changed;
204   widget_class->direction_changed = gtk_check_menu_item_direction_changed;
205 
206   g_object_class_install_property (gobject_class,
207                                    PROP_ACTIVE,
208                                    g_param_spec_boolean ("active",
209                                                          P_("Active"),
210                                                          P_("Whether the menu item is checked"),
211                                                          FALSE,
212                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
213 
214   g_object_class_install_property (gobject_class,
215                                    PROP_INCONSISTENT,
216                                    g_param_spec_boolean ("inconsistent",
217                                                          P_("Inconsistent"),
218                                                          P_("Whether to display an \"inconsistent\" state"),
219                                                          FALSE,
220                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
221 
222   g_object_class_install_property (gobject_class,
223                                    PROP_DRAW_AS_RADIO,
224                                    g_param_spec_boolean ("draw-as-radio",
225                                                          P_("Draw as radio menu item"),
226                                                          P_("Whether the menu item looks like a radio menu item"),
227                                                          FALSE,
228                                                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
229 
230   /**
231    * GtkCheckMenuItem:indicator-size:
232    *
233    * The size of the check or radio indicator.
234    *
235    * Deprecated: 3.20: Use the standard CSS property min-width on the check or
236    *   radio nodes; the value of this style property is ignored.
237    */
238   gtk_widget_class_install_style_property (widget_class,
239                                            g_param_spec_int ("indicator-size",
240                                                              P_("Indicator Size"),
241                                                              P_("Size of check or radio indicator"),
242                                                              0,
243                                                              G_MAXINT,
244                                                              INDICATOR_SIZE,
245                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
246 
247   widget_class->draw = gtk_check_menu_item_draw;
248 
249   menu_item_class->activate = gtk_check_menu_item_activate;
250   menu_item_class->hide_on_activate = FALSE;
251   menu_item_class->toggle_size_request = gtk_check_menu_item_toggle_size_request;
252 
253   klass->toggled = NULL;
254   klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
255 
256   /**
257    * GtkCheckMenuItem::toggled:
258    * @checkmenuitem: the object which received the signal.
259    *
260    * This signal is emitted when the state of the check box is changed.
261    *
262    * A signal handler can use gtk_check_menu_item_get_active()
263    * to discover the new state.
264    */
265   check_menu_item_signals[TOGGLED] =
266     g_signal_new (I_("toggled"),
267                   G_OBJECT_CLASS_TYPE (gobject_class),
268                   G_SIGNAL_RUN_FIRST,
269                   G_STRUCT_OFFSET (GtkCheckMenuItemClass, toggled),
270                   NULL, NULL,
271                   NULL,
272                   G_TYPE_NONE, 0);
273 
274   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CHECK_MENU_ITEM_ACCESSIBLE);
275   gtk_widget_class_set_css_name (widget_class, "menuitem");
276 }
277 
278 static void
gtk_check_menu_item_activatable_interface_init(GtkActivatableIface * iface)279 gtk_check_menu_item_activatable_interface_init (GtkActivatableIface  *iface)
280 {
281   parent_activatable_iface = g_type_interface_peek_parent (iface);
282   iface->update = gtk_check_menu_item_update;
283   iface->sync_action_properties = gtk_check_menu_item_sync_action_properties;
284 }
285 
286 static void
gtk_check_menu_item_update(GtkActivatable * activatable,GtkAction * action,const gchar * property_name)287 gtk_check_menu_item_update (GtkActivatable *activatable,
288                             GtkAction      *action,
289                             const gchar    *property_name)
290 {
291   GtkCheckMenuItem *check_menu_item;
292   gboolean use_action_appearance;
293 
294   check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
295 
296   parent_activatable_iface->update (activatable, action, property_name);
297 
298   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
299 
300   if (strcmp (property_name, "active") == 0)
301     {
302       gtk_action_block_activate (action);
303       gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
304       gtk_action_unblock_activate (action);
305     }
306 
307   use_action_appearance = gtk_activatable_get_use_action_appearance (activatable);
308   G_GNUC_END_IGNORE_DEPRECATIONS;
309 
310   if (!use_action_appearance)
311     return;
312 
313   if (strcmp (property_name, "draw-as-radio") == 0)
314     {
315       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
316       gtk_check_menu_item_set_draw_as_radio (check_menu_item,
317                                              gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
318       G_GNUC_END_IGNORE_DEPRECATIONS;
319     }
320 }
321 
322 static void
gtk_check_menu_item_sync_action_properties(GtkActivatable * activatable,GtkAction * action)323 gtk_check_menu_item_sync_action_properties (GtkActivatable *activatable,
324                                             GtkAction      *action)
325 {
326   GtkCheckMenuItem *check_menu_item;
327   gboolean use_action_appearance;
328   gboolean is_toggle_action;
329 
330   check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
331 
332   parent_activatable_iface->sync_action_properties (activatable, action);
333 
334   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
335   is_toggle_action = GTK_IS_TOGGLE_ACTION (action);
336   G_GNUC_END_IGNORE_DEPRECATIONS;
337 
338   if (!is_toggle_action)
339     return;
340 
341   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
342   gtk_action_block_activate (action);
343 
344   gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
345   gtk_action_unblock_activate (action);
346   use_action_appearance = gtk_activatable_get_use_action_appearance (activatable);
347   G_GNUC_END_IGNORE_DEPRECATIONS;
348 
349   if (!use_action_appearance)
350     return;
351 
352   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
353   gtk_check_menu_item_set_draw_as_radio (check_menu_item,
354                                          gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
355   G_GNUC_END_IGNORE_DEPRECATIONS;
356 }
357 
358 /**
359  * gtk_check_menu_item_new:
360  *
361  * Creates a new #GtkCheckMenuItem.
362  *
363  * Returns: a new #GtkCheckMenuItem.
364  */
365 GtkWidget*
gtk_check_menu_item_new(void)366 gtk_check_menu_item_new (void)
367 {
368   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, NULL);
369 }
370 
371 /**
372  * gtk_check_menu_item_new_with_label:
373  * @label: the string to use for the label.
374  *
375  * Creates a new #GtkCheckMenuItem with a label.
376  *
377  * Returns: a new #GtkCheckMenuItem.
378  */
379 GtkWidget*
gtk_check_menu_item_new_with_label(const gchar * label)380 gtk_check_menu_item_new_with_label (const gchar *label)
381 {
382   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM,
383                        "label", label,
384                        NULL);
385 }
386 
387 
388 /**
389  * gtk_check_menu_item_new_with_mnemonic:
390  * @label: The text of the button, with an underscore in front of the
391  *     character
392  *
393  * Creates a new #GtkCheckMenuItem containing a label. The label
394  * will be created using gtk_label_new_with_mnemonic(), so underscores
395  * in @label indicate the mnemonic for the menu item.
396  *
397  * Returns: a new #GtkCheckMenuItem
398  */
399 GtkWidget*
gtk_check_menu_item_new_with_mnemonic(const gchar * label)400 gtk_check_menu_item_new_with_mnemonic (const gchar *label)
401 {
402   return g_object_new (GTK_TYPE_CHECK_MENU_ITEM,
403                        "label", label,
404                        "use-underline", TRUE,
405                        NULL);
406 }
407 
408 /**
409  * gtk_check_menu_item_set_active:
410  * @check_menu_item: a #GtkCheckMenuItem.
411  * @is_active: boolean value indicating whether the check box is active.
412  *
413  * Sets the active state of the menu item’s check box.
414  */
415 void
gtk_check_menu_item_set_active(GtkCheckMenuItem * check_menu_item,gboolean is_active)416 gtk_check_menu_item_set_active (GtkCheckMenuItem *check_menu_item,
417                                 gboolean          is_active)
418 {
419   GtkCheckMenuItemPrivate *priv;
420 
421   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
422 
423   priv = check_menu_item->priv;
424 
425   is_active = is_active != 0;
426 
427   if (priv->active != is_active)
428     gtk_menu_item_activate (GTK_MENU_ITEM (check_menu_item));
429 }
430 
431 /**
432  * gtk_check_menu_item_get_active:
433  * @check_menu_item: a #GtkCheckMenuItem
434  *
435  * Returns whether the check menu item is active. See
436  * gtk_check_menu_item_set_active ().
437  *
438  * Returns: %TRUE if the menu item is checked.
439  */
440 gboolean
gtk_check_menu_item_get_active(GtkCheckMenuItem * check_menu_item)441 gtk_check_menu_item_get_active (GtkCheckMenuItem *check_menu_item)
442 {
443   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
444 
445   return check_menu_item->priv->active;
446 }
447 
448 static void
gtk_check_menu_item_toggle_size_request(GtkMenuItem * menu_item,gint * requisition)449 gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
450                                          gint        *requisition)
451 {
452   GtkCheckMenuItem *check_menu_item;
453 
454   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (menu_item));
455 
456   check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
457   gtk_css_gadget_get_preferred_size (check_menu_item->priv->indicator_gadget,
458                                      GTK_ORIENTATION_HORIZONTAL,
459                                      -1,
460                                      requisition, NULL,
461                                      NULL, NULL);
462 }
463 
464 /**
465  * gtk_check_menu_item_toggled:
466  * @check_menu_item: a #GtkCheckMenuItem.
467  *
468  * Emits the #GtkCheckMenuItem::toggled signal.
469  */
470 void
gtk_check_menu_item_toggled(GtkCheckMenuItem * check_menu_item)471 gtk_check_menu_item_toggled (GtkCheckMenuItem *check_menu_item)
472 {
473   g_signal_emit (check_menu_item, check_menu_item_signals[TOGGLED], 0);
474 }
475 
476 static void
update_node_state(GtkCheckMenuItem * check_menu_item)477 update_node_state (GtkCheckMenuItem *check_menu_item)
478 {
479   GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
480   GtkStateFlags state;
481 
482   state = gtk_widget_get_state_flags (GTK_WIDGET (check_menu_item));
483 
484   if (priv->inconsistent)
485     state |= GTK_STATE_FLAG_INCONSISTENT;
486   if (priv->active)
487     state |= GTK_STATE_FLAG_CHECKED;
488 
489   gtk_css_gadget_set_state (priv->indicator_gadget, state);
490 }
491 
492 /**
493  * gtk_check_menu_item_set_inconsistent:
494  * @check_menu_item: a #GtkCheckMenuItem
495  * @setting: %TRUE to display an “inconsistent” third state check
496  *
497  * If the user has selected a range of elements (such as some text or
498  * spreadsheet cells) that are affected by a boolean setting, and the
499  * current values in that range are inconsistent, you may want to
500  * display the check in an “in between” state. This function turns on
501  * “in between” display.  Normally you would turn off the inconsistent
502  * state again if the user explicitly selects a setting. This has to be
503  * done manually, gtk_check_menu_item_set_inconsistent() only affects
504  * visual appearance, it doesn’t affect the semantics of the widget.
505  *
506  **/
507 void
gtk_check_menu_item_set_inconsistent(GtkCheckMenuItem * check_menu_item,gboolean setting)508 gtk_check_menu_item_set_inconsistent (GtkCheckMenuItem *check_menu_item,
509                                       gboolean          setting)
510 {
511   GtkCheckMenuItemPrivate *priv;
512 
513   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
514 
515   priv = check_menu_item->priv;
516 
517   setting = setting != FALSE;
518 
519   if (setting != priv->inconsistent)
520     {
521       priv->inconsistent = setting;
522       update_node_state (check_menu_item);
523       gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
524       g_object_notify (G_OBJECT (check_menu_item), "inconsistent");
525     }
526 }
527 
528 /**
529  * gtk_check_menu_item_get_inconsistent:
530  * @check_menu_item: a #GtkCheckMenuItem
531  *
532  * Retrieves the value set by gtk_check_menu_item_set_inconsistent().
533  *
534  * Returns: %TRUE if inconsistent
535  **/
536 gboolean
gtk_check_menu_item_get_inconsistent(GtkCheckMenuItem * check_menu_item)537 gtk_check_menu_item_get_inconsistent (GtkCheckMenuItem *check_menu_item)
538 {
539   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
540 
541   return check_menu_item->priv->inconsistent;
542 }
543 
544 /**
545  * gtk_check_menu_item_set_draw_as_radio:
546  * @check_menu_item: a #GtkCheckMenuItem
547  * @draw_as_radio: whether @check_menu_item is drawn like a #GtkRadioMenuItem
548  *
549  * Sets whether @check_menu_item is drawn like a #GtkRadioMenuItem
550  *
551  * Since: 2.4
552  **/
553 void
gtk_check_menu_item_set_draw_as_radio(GtkCheckMenuItem * check_menu_item,gboolean draw_as_radio)554 gtk_check_menu_item_set_draw_as_radio (GtkCheckMenuItem *check_menu_item,
555                                        gboolean          draw_as_radio)
556 {
557   GtkCheckMenuItemPrivate *priv;
558   GtkCssNode *indicator_node;
559 
560   g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
561 
562   priv = check_menu_item->priv;
563 
564   draw_as_radio = draw_as_radio != FALSE;
565 
566   if (draw_as_radio != priv->draw_as_radio)
567     {
568       priv->draw_as_radio = draw_as_radio;
569       indicator_node = gtk_css_gadget_get_node (priv->indicator_gadget);
570       if (draw_as_radio)
571         gtk_css_node_set_name (indicator_node, I_("radio"));
572       else
573         gtk_css_node_set_name (indicator_node, I_("check"));
574 
575       gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
576 
577       g_object_notify (G_OBJECT (check_menu_item), "draw-as-radio");
578     }
579 }
580 
581 /**
582  * gtk_check_menu_item_get_draw_as_radio:
583  * @check_menu_item: a #GtkCheckMenuItem
584  *
585  * Returns whether @check_menu_item looks like a #GtkRadioMenuItem
586  *
587  * Returns: Whether @check_menu_item looks like a #GtkRadioMenuItem
588  *
589  * Since: 2.4
590  **/
591 gboolean
gtk_check_menu_item_get_draw_as_radio(GtkCheckMenuItem * check_menu_item)592 gtk_check_menu_item_get_draw_as_radio (GtkCheckMenuItem *check_menu_item)
593 {
594   g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
595 
596   return check_menu_item->priv->draw_as_radio;
597 }
598 
599 static void
gtk_check_menu_item_init(GtkCheckMenuItem * check_menu_item)600 gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item)
601 {
602   GtkCheckMenuItemPrivate *priv;
603 
604   priv = check_menu_item->priv = gtk_check_menu_item_get_instance_private (check_menu_item);
605   priv->active = FALSE;
606 
607   priv->indicator_gadget =
608     gtk_builtin_icon_new ("check",
609                           GTK_WIDGET (check_menu_item),
610                           _gtk_menu_item_get_gadget (GTK_MENU_ITEM (check_menu_item)),
611                           NULL);
612   update_node_state (check_menu_item);
613 }
614 
615 static gint
gtk_check_menu_item_draw(GtkWidget * widget,cairo_t * cr)616 gtk_check_menu_item_draw (GtkWidget *widget,
617                           cairo_t   *cr)
618 {
619   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget);
620 
621   if (GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->draw)
622     GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->draw (widget, cr);
623 
624   if (GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator)
625     GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator (check_menu_item, cr);
626 
627   return FALSE;
628 }
629 
630 static void
gtk_check_menu_item_activate(GtkMenuItem * menu_item)631 gtk_check_menu_item_activate (GtkMenuItem *menu_item)
632 {
633   GtkCheckMenuItemPrivate *priv;
634 
635   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
636   priv = check_menu_item->priv;
637 
638   priv->active = !priv->active;
639 
640   gtk_check_menu_item_toggled (check_menu_item);
641   update_node_state (check_menu_item);
642   gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
643 
644   GTK_MENU_ITEM_CLASS (gtk_check_menu_item_parent_class)->activate (menu_item);
645 
646   g_object_notify (G_OBJECT (check_menu_item), "active");
647 }
648 
649 static void
gtk_check_menu_item_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)650 gtk_check_menu_item_state_flags_changed (GtkWidget     *widget,
651                                          GtkStateFlags  previous_state)
652 
653 {
654   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget);
655 
656   update_node_state (check_menu_item);
657 
658   GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->state_flags_changed (widget, previous_state);
659 }
660 
661 static void
gtk_check_menu_item_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)662 gtk_check_menu_item_direction_changed (GtkWidget        *widget,
663                                        GtkTextDirection  previous_dir)
664 {
665   GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget);
666   GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
667   GtkCssNode *indicator_node, *widget_node, *node;
668 
669   indicator_node = gtk_css_gadget_get_node (priv->indicator_gadget);
670   widget_node = gtk_widget_get_css_node (widget);
671 
672   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
673     {
674       gtk_css_node_remove_class (indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT));
675       gtk_css_node_add_class (indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT));
676 
677       node = gtk_css_node_get_last_child (widget_node);
678       if (node != indicator_node)
679         gtk_css_node_insert_after (widget_node, indicator_node, node);
680     }
681   else
682     {
683       gtk_css_node_add_class (indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_LEFT));
684       gtk_css_node_remove_class (indicator_node, g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT));
685 
686       node = gtk_css_node_get_first_child (widget_node);
687       if (node != indicator_node)
688         gtk_css_node_insert_before (widget_node, indicator_node, node);
689     }
690 
691   GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->direction_changed (widget, previous_dir);
692 }
693 
694 static void
gtk_real_check_menu_item_draw_indicator(GtkCheckMenuItem * check_menu_item,cairo_t * cr)695 gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
696                                          cairo_t          *cr)
697 {
698   gtk_css_gadget_draw (check_menu_item->priv->indicator_gadget, cr);
699 }
700 
701 static void
gtk_check_menu_item_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)702 gtk_check_menu_item_get_property (GObject     *object,
703                                   guint        prop_id,
704                                   GValue      *value,
705                                   GParamSpec  *pspec)
706 {
707   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
708   GtkCheckMenuItemPrivate *priv = checkitem->priv;
709 
710   switch (prop_id)
711     {
712     case PROP_ACTIVE:
713       g_value_set_boolean (value, priv->active);
714       break;
715     case PROP_INCONSISTENT:
716       g_value_set_boolean (value, priv->inconsistent);
717       break;
718     case PROP_DRAW_AS_RADIO:
719       g_value_set_boolean (value, priv->draw_as_radio);
720       break;
721     default:
722       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
723       break;
724     }
725 }
726 
727 
728 static void
gtk_check_menu_item_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)729 gtk_check_menu_item_set_property (GObject      *object,
730                                   guint         prop_id,
731                                   const GValue *value,
732                                   GParamSpec   *pspec)
733 {
734   GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
735 
736   switch (prop_id)
737     {
738     case PROP_ACTIVE:
739       gtk_check_menu_item_set_active (checkitem, g_value_get_boolean (value));
740       break;
741     case PROP_INCONSISTENT:
742       gtk_check_menu_item_set_inconsistent (checkitem, g_value_get_boolean (value));
743       break;
744     case PROP_DRAW_AS_RADIO:
745       gtk_check_menu_item_set_draw_as_radio (checkitem, g_value_get_boolean (value));
746       break;
747     default:
748       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
749       break;
750     }
751 }
752 
753 /* Private */
754 
755 /*
756  * _gtk_check_menu_item_set_active:
757  * @check_menu_item: a #GtkCheckMenuItem
758  * @is_active: whether the action is active or not
759  *
760  * Sets the #GtkCheckMenuItem:active property directly. This function does
761  * not emit signals or notifications: it is left to the caller to do so.
762  */
763 void
_gtk_check_menu_item_set_active(GtkCheckMenuItem * check_menu_item,gboolean is_active)764 _gtk_check_menu_item_set_active (GtkCheckMenuItem *check_menu_item,
765                                  gboolean          is_active)
766 {
767   GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
768 
769   priv->active = is_active;
770   update_node_state (check_menu_item);
771 }
772 
773 GtkCssGadget *
_gtk_check_menu_item_get_indicator_gadget(GtkCheckMenuItem * check_menu_item)774 _gtk_check_menu_item_get_indicator_gadget (GtkCheckMenuItem *check_menu_item)
775 {
776   return check_menu_item->priv->indicator_gadget;
777 }
778