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