Bug Summary

File:ctk/ctkmenutrackeritem.c
Warning:line 838, column 44
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctkmenutrackeritem.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-09-19-171836-43636-1 -x c ctkmenutrackeritem.c
1/*
2 * Copyright © 2013 Canonical Limited
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 licence, 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 * Author: Ryan Lortie <desrt@desrt.ca>
18 */
19
20#include "config.h"
21
22#include "ctkmenutrackeritem.h"
23#include "ctkactionmuxer.h"
24#include "ctkdebug.h"
25
26#include "ctkactionmuxer.h"
27
28#include <string.h>
29
30/*< private >
31 * SECTION:ctkmenutrackeritem
32 * @Title: CtkMenuTrackerItem
33 * @Short_description: Small helper for model menu items
34 *
35 * A #CtkMenuTrackerItem is a small helper class used by #CtkMenuTracker to
36 * represent menu items. It has one of three classes: normal item, separator,
37 * or submenu.
38 *
39 * If an item is one of the non-normal classes (submenu, separator), only the
40 * label of the item needs to be respected. Otherwise, all the properties
41 * of the item contribute to the item’s appearance and state.
42 *
43 * Implementing the appearance of the menu item is up to toolkits, and certain
44 * toolkits may choose to ignore certain properties, like icon or accel. The
45 * role of the item determines its accessibility role, along with its
46 * decoration if the CtkMenuTrackerItem::toggled property is true. As an
47 * example, if the item has the role %CTK_MENU_TRACKER_ITEM_ROLE_CHECK and
48 * CtkMenuTrackerItem::toggled is %FALSE, its accessible role should be that of
49 * a check menu item, and no decoration should be drawn. But if
50 * CtkMenuTrackerItem::toggled is %TRUE, a checkmark should be drawn.
51 *
52 * All properties except for the two class-determining properties,
53 * CtkMenuTrackerItem::is-separator and CtkMenuTrackerItem::has-submenu are
54 * allowed to change, so listen to the notify signals to update your item's
55 * appearance. When using a GObject library, this can conveniently be done
56 * with g_object_bind_property() and #GBinding, and this is how this is
57 * implemented in CTK+; the appearance side is implemented in #CtkModelMenuItem.
58 *
59 * When an item is clicked, simply call ctk_menu_tracker_item_activated() in
60 * response. The #CtkMenuTrackerItem will take care of everything related to
61 * activating the item and will itself update the state of all items in
62 * response.
63 *
64 * Submenus are a special case of menu item. When an item is a submenu, you
65 * should create a submenu for it with ctk_menu_tracker_new_item_for_submenu(),
66 * and apply the same menu tracking logic you would for a toplevel menu.
67 * Applications using submenus may want to lazily build their submenus in
68 * response to the user clicking on it, as building a submenu may be expensive.
69 *
70 * Thus, the submenu has two special controls -- the submenu’s visibility
71 * should be controlled by the CtkMenuTrackerItem::submenu-shown property,
72 * and if a user clicks on the submenu, do not immediately show the menu,
73 * but call ctk_menu_tracker_item_request_submenu_shown() and wait for the
74 * CtkMenuTrackerItem::submenu-shown property to update. If the user navigates,
75 * the application may want to be notified so it can cancel the expensive
76 * operation that it was using to build the submenu. Thus,
77 * ctk_menu_tracker_item_request_submenu_shown() takes a boolean parameter.
78 * Use %TRUE when the user wants to open the submenu, and %FALSE when the
79 * user wants to close the submenu.
80 */
81
82typedef GObjectClass CtkMenuTrackerItemClass;
83
84struct _CtkMenuTrackerItem
85{
86 GObject parent_instance;
87
88 CtkActionObservable *observable;
89 gchar *action_namespace;
90 gchar *action_and_target;
91 GMenuItem *item;
92 CtkMenuTrackerItemRole role : 4;
93 guint is_separator : 1;
94 guint can_activate : 1;
95 guint sensitive : 1;
96 guint toggled : 1;
97 guint submenu_shown : 1;
98 guint submenu_requested : 1;
99 guint hidden_when : 2;
100 guint is_visible : 1;
101};
102
103#define HIDDEN_NEVER0 0
104#define HIDDEN_WHEN_MISSING1 1
105#define HIDDEN_WHEN_DISABLED2 2
106#define HIDDEN_WHEN_ALWAYS3 3
107
108enum {
109 PROP_0,
110 PROP_IS_SEPARATOR,
111 PROP_LABEL,
112 PROP_ICON,
113 PROP_VERB_ICON,
114 PROP_SENSITIVE,
115 PROP_ROLE,
116 PROP_TOGGLED,
117 PROP_ACCEL,
118 PROP_SUBMENU_SHOWN,
119 PROP_IS_VISIBLE,
120 N_PROPS
121};
122
123static GParamSpec *ctk_menu_tracker_item_pspecs[N_PROPS];
124
125static void ctk_menu_tracker_item_init_observer_iface (CtkActionObserverInterface *iface);
126G_DEFINE_TYPE_WITH_CODE (CtkMenuTrackerItem, ctk_menu_tracker_item, G_TYPE_OBJECT,static void ctk_menu_tracker_item_init (CtkMenuTrackerItem *self
); static void ctk_menu_tracker_item_class_init (CtkMenuTrackerItemClass
*klass); static GType ctk_menu_tracker_item_get_type_once (void
); static gpointer ctk_menu_tracker_item_parent_class = ((void
*)0); static gint CtkMenuTrackerItem_private_offset; static void
ctk_menu_tracker_item_class_intern_init (gpointer klass) { ctk_menu_tracker_item_parent_class
= g_type_class_peek_parent (klass); if (CtkMenuTrackerItem_private_offset
!= 0) g_type_class_adjust_private_offset (klass, &CtkMenuTrackerItem_private_offset
); ctk_menu_tracker_item_class_init ((CtkMenuTrackerItemClass
*) klass); } __attribute__ ((__unused__)) static inline gpointer
ctk_menu_tracker_item_get_instance_private (CtkMenuTrackerItem
*self) { return (((gpointer) ((guint8*) (self) + (glong) (CtkMenuTrackerItem_private_offset
)))); } GType ctk_menu_tracker_item_get_type (void) { static GType
static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); (void) (0 ? (gpointer) * (
&static_g_define_type_id) : ((void*)0)); (!(__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_menu_tracker_item_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_menu_tracker_item_get_type_once
(void) { GType g_define_type_id = g_type_register_static_simple
(((GType) ((20) << (2))), g_intern_static_string ("CtkMenuTrackerItem"
), sizeof (CtkMenuTrackerItemClass), (GClassInitFunc)(void (*
)(void)) ctk_menu_tracker_item_class_intern_init, sizeof (CtkMenuTrackerItem
), (GInstanceInitFunc)(void (*)(void)) ctk_menu_tracker_item_init
, (GTypeFlags) 0); { {{ const GInterfaceInfo g_implement_interface_info
= { (GInterfaceInitFunc)(void (*)(void)) ctk_menu_tracker_item_init_observer_iface
, ((void*)0), ((void*)0) }; g_type_add_interface_static (g_define_type_id
, (ctk_action_observer_get_type ()), &g_implement_interface_info
); };} } return g_define_type_id; }
127 G_IMPLEMENT_INTERFACE (CTK_TYPE_ACTION_OBSERVER, ctk_menu_tracker_item_init_observer_iface))static void ctk_menu_tracker_item_init (CtkMenuTrackerItem *self
); static void ctk_menu_tracker_item_class_init (CtkMenuTrackerItemClass
*klass); static GType ctk_menu_tracker_item_get_type_once (void
); static gpointer ctk_menu_tracker_item_parent_class = ((void
*)0); static gint CtkMenuTrackerItem_private_offset; static void
ctk_menu_tracker_item_class_intern_init (gpointer klass) { ctk_menu_tracker_item_parent_class
= g_type_class_peek_parent (klass); if (CtkMenuTrackerItem_private_offset
!= 0) g_type_class_adjust_private_offset (klass, &CtkMenuTrackerItem_private_offset
); ctk_menu_tracker_item_class_init ((CtkMenuTrackerItemClass
*) klass); } __attribute__ ((__unused__)) static inline gpointer
ctk_menu_tracker_item_get_instance_private (CtkMenuTrackerItem
*self) { return (((gpointer) ((guint8*) (self) + (glong) (CtkMenuTrackerItem_private_offset
)))); } GType ctk_menu_tracker_item_get_type (void) { static GType
static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); (void) (0 ? (gpointer) * (
&static_g_define_type_id) : ((void*)0)); (!(__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_menu_tracker_item_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_menu_tracker_item_get_type_once
(void) { GType g_define_type_id = g_type_register_static_simple
(((GType) ((20) << (2))), g_intern_static_string ("CtkMenuTrackerItem"
), sizeof (CtkMenuTrackerItemClass), (GClassInitFunc)(void (*
)(void)) ctk_menu_tracker_item_class_intern_init, sizeof (CtkMenuTrackerItem
), (GInstanceInitFunc)(void (*)(void)) ctk_menu_tracker_item_init
, (GTypeFlags) 0); { {{ const GInterfaceInfo g_implement_interface_info
= { (GInterfaceInitFunc)(void (*)(void)) ctk_menu_tracker_item_init_observer_iface
, ((void*)0), ((void*)0) }; g_type_add_interface_static (g_define_type_id
, (ctk_action_observer_get_type ()), &g_implement_interface_info
); };} } return g_define_type_id; }
128
129GType
130ctk_menu_tracker_item_role_get_type (void)
131{
132 static gsize ctk_menu_tracker_item_role_type;
133
134 if (g_once_init_enter (&ctk_menu_tracker_item_role_type)(__extension__ ({ _Static_assert (sizeof *(&ctk_menu_tracker_item_role_type
) == sizeof (gpointer), "Expression evaluates to false"); (void
) (0 ? (gpointer) *(&ctk_menu_tracker_item_role_type) : (
(void*)0)); (!(__extension__ ({ _Static_assert (sizeof *(&
ctk_menu_tracker_item_role_type) == sizeof (gpointer), "Expression evaluates to false"
); __typeof__ (*(&ctk_menu_tracker_item_role_type)) gapg_temp_newval
; __typeof__ ((&ctk_menu_tracker_item_role_type)) gapg_temp_atomic
= (&ctk_menu_tracker_item_role_type); __atomic_load (gapg_temp_atomic
, &gapg_temp_newval, 5); gapg_temp_newval; })) &&
g_once_init_enter (&ctk_menu_tracker_item_role_type)); }
))
)
135 {
136 static const GEnumValue values[] = {
137 { CTK_MENU_TRACKER_ITEM_ROLE_NORMAL, "CTK_MENU_TRACKER_ITEM_ROLE_NORMAL", "normal" },
138 { CTK_MENU_TRACKER_ITEM_ROLE_CHECK, "CTK_MENU_TRACKER_ITEM_ROLE_CHECK", "check" },
139 { CTK_MENU_TRACKER_ITEM_ROLE_RADIO, "CTK_MENU_TRACKER_ITEM_ROLE_RADIO", "radio" },
140 { 0, NULL((void*)0), NULL((void*)0) }
141 };
142 GType type;
143
144 type = g_enum_register_static ("CtkMenuTrackerItemRole", values);
145
146 g_once_init_leave (&ctk_menu_tracker_item_role_type, type)(__extension__ ({ _Static_assert (sizeof *(&ctk_menu_tracker_item_role_type
) == sizeof (gpointer), "Expression evaluates to false"); 0 ?
(void) (*(&ctk_menu_tracker_item_role_type) = (type)) : (
void) 0; g_once_init_leave ((&ctk_menu_tracker_item_role_type
), (gsize) (type)); }))
;
147 }
148
149 return ctk_menu_tracker_item_role_type;
150}
151
152static void
153ctk_menu_tracker_item_get_property (GObject *object,
154 guint prop_id,
155 GValue *value,
156 GParamSpec *pspec)
157{
158 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (object)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((object)), ((ctk_menu_tracker_item_get_type
()))))))
;
159
160 switch (prop_id)
161 {
162 case PROP_IS_SEPARATOR:
163 g_value_set_boolean (value, ctk_menu_tracker_item_get_is_separator (self));
164 break;
165 case PROP_LABEL:
166 g_value_set_string (value, ctk_menu_tracker_item_get_label (self));
167 break;
168 case PROP_ICON:
169 g_value_take_object (value, ctk_menu_tracker_item_get_icon (self));
170 break;
171 case PROP_VERB_ICON:
172 g_value_take_object (value, ctk_menu_tracker_item_get_verb_icon (self));
173 break;
174 case PROP_SENSITIVE:
175 g_value_set_boolean (value, ctk_menu_tracker_item_get_sensitive (self));
176 break;
177 case PROP_ROLE:
178 g_value_set_enum (value, ctk_menu_tracker_item_get_role (self));
179 break;
180 case PROP_TOGGLED:
181 g_value_set_boolean (value, ctk_menu_tracker_item_get_toggled (self));
182 break;
183 case PROP_ACCEL:
184 g_value_set_string (value, ctk_menu_tracker_item_get_accel (self));
185 break;
186 case PROP_SUBMENU_SHOWN:
187 g_value_set_boolean (value, ctk_menu_tracker_item_get_submenu_shown (self));
188 break;
189 case PROP_IS_VISIBLE:
190 g_value_set_boolean (value, ctk_menu_tracker_item_get_is_visible (self));
191 break;
192 default:
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkmenutrackeritem.c", 193, ("property"), _glib__property_id
, _glib__pspec->name, g_type_name ((((((GTypeClass*) (((GTypeInstance
*) (_glib__pspec))->g_class))->g_type)))), (g_type_name
((((((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
194 break;
195 }
196}
197
198static void
199ctk_menu_tracker_item_finalize (GObject *object)
200{
201 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (object)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((object)), ((ctk_menu_tracker_item_get_type
()))))))
;
202
203 g_free (self->action_namespace);
204 g_free (self->action_and_target);
205
206 if (self->observable)
207 g_object_unref (self->observable);
208
209 g_object_unref (self->item);
210
211 G_OBJECT_CLASS (ctk_menu_tracker_item_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_tracker_item_parent_class)), (((GType) ((20) <<
(2))))))))
->finalize (object);
212}
213
214static void
215ctk_menu_tracker_item_init (CtkMenuTrackerItem * self G_GNUC_UNUSED__attribute__ ((__unused__)))
216{
217}
218
219static void
220ctk_menu_tracker_item_class_init (CtkMenuTrackerItemClass *class)
221{
222 class->get_property = ctk_menu_tracker_item_get_property;
223 class->finalize = ctk_menu_tracker_item_finalize;
224
225 ctk_menu_tracker_item_pspecs[PROP_IS_SEPARATOR] =
226 g_param_spec_boolean ("is-separator", "", "", FALSE(0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
227 ctk_menu_tracker_item_pspecs[PROP_LABEL] =
228 g_param_spec_string ("label", "", "", NULL((void*)0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
229 ctk_menu_tracker_item_pspecs[PROP_ICON] =
230 g_param_spec_object ("icon", "", "", G_TYPE_ICON(g_icon_get_type ()), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
231 ctk_menu_tracker_item_pspecs[PROP_VERB_ICON] =
232 g_param_spec_object ("verb-icon", "", "", G_TYPE_ICON(g_icon_get_type ()), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
233 ctk_menu_tracker_item_pspecs[PROP_SENSITIVE] =
234 g_param_spec_boolean ("sensitive", "", "", FALSE(0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
235 ctk_menu_tracker_item_pspecs[PROP_ROLE] =
236 g_param_spec_enum ("role", "", "",
237 CTK_TYPE_MENU_TRACKER_ITEM_ROLE(ctk_menu_tracker_item_role_get_type ()), CTK_MENU_TRACKER_ITEM_ROLE_NORMAL,
238 G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
239 ctk_menu_tracker_item_pspecs[PROP_TOGGLED] =
240 g_param_spec_boolean ("toggled", "", "", FALSE(0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
241 ctk_menu_tracker_item_pspecs[PROP_ACCEL] =
242 g_param_spec_string ("accel", "", "", NULL((void*)0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
243 ctk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN] =
244 g_param_spec_boolean ("submenu-shown", "", "", FALSE(0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
245 ctk_menu_tracker_item_pspecs[PROP_IS_VISIBLE] =
246 g_param_spec_boolean ("is-visible", "", "", FALSE(0), G_PARAM_STATIC_STRINGS(G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB
)
| G_PARAM_READABLE);
247
248 g_object_class_install_properties (class, N_PROPS, ctk_menu_tracker_item_pspecs);
249}
250
251/* This syncs up the visibility for the hidden-when='' case. We call it
252 * from the action observer functions on changes to the action group and
253 * on initialisation (via the action observer functions that are invoked
254 * at that time).
255 */
256static void
257ctk_menu_tracker_item_update_visibility (CtkMenuTrackerItem *self)
258{
259 gboolean visible;
260
261 switch (self->hidden_when)
262 {
263 case HIDDEN_NEVER0:
264 visible = TRUE(!(0));
265 break;
266
267 case HIDDEN_WHEN_MISSING1:
268 visible = self->can_activate;
269 break;
270
271 case HIDDEN_WHEN_DISABLED2:
272 visible = self->sensitive;
273 break;
274
275 case HIDDEN_WHEN_ALWAYS3:
276 visible = FALSE(0);
277 break;
278
279 default:
280 g_assert_not_reached ()do { g_assertion_message_expr ("Ctk", "ctkmenutrackeritem.c",
280, ((const char*) (__func__)), ((void*)0)); } while (0)
;
281 }
282
283 if (visible != self->is_visible)
284 {
285 self->is_visible = visible;
286 g_object_notify (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, "is-visible");
287 }
288}
289
290static void
291ctk_menu_tracker_item_action_added (CtkActionObserver *observer,
292 CtkActionObservable *observable G_GNUC_UNUSED__attribute__ ((__unused__)),
293 const gchar *action_name,
294 const GVariantType *parameter_type,
295 gboolean enabled,
296 GVariant *state)
297{
298 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (observer)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((observer)), ((ctk_menu_tracker_item_get_type
()))))))
;
299 GVariant *action_target;
300
301 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s added", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s added", action_name); }; } while (0
)
;
302
303 action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET"target", NULL((void*)0));
304
305 self->can_activate = (action_target == NULL((void*)0) && parameter_type == NULL((void*)0)) ||
306 (action_target != NULL((void*)0) && parameter_type != NULL((void*)0) &&
307 g_variant_is_of_type (action_target, parameter_type));
308
309 if (!self->can_activate)
310 {
311 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s can't be activated due to parameter type mismatch "do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can't be activated due to parameter type mismatch "
"(parameter type %s, target type %s)", action_name, parameter_type
? g_variant_type_peek_string (parameter_type) : "NULL", action_target
? g_variant_get_type_string (action_target) : "NULL"); }; } while
(0)
312 "(parameter type %s, target type %s)",do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can't be activated due to parameter type mismatch "
"(parameter type %s, target type %s)", action_name, parameter_type
? g_variant_type_peek_string (parameter_type) : "NULL", action_target
? g_variant_get_type_string (action_target) : "NULL"); }; } while
(0)
313 action_name,do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can't be activated due to parameter type mismatch "
"(parameter type %s, target type %s)", action_name, parameter_type
? g_variant_type_peek_string (parameter_type) : "NULL", action_target
? g_variant_get_type_string (action_target) : "NULL"); }; } while
(0)
314 parameter_type ? g_variant_type_peek_string (parameter_type) : "NULL",do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can't be activated due to parameter type mismatch "
"(parameter type %s, target type %s)", action_name, parameter_type
? g_variant_type_peek_string (parameter_type) : "NULL", action_target
? g_variant_get_type_string (action_target) : "NULL"); }; } while
(0)
315 action_target ? g_variant_get_type_string (action_target) : "NULL"))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can't be activated due to parameter type mismatch "
"(parameter type %s, target type %s)", action_name, parameter_type
? g_variant_type_peek_string (parameter_type) : "NULL", action_target
? g_variant_get_type_string (action_target) : "NULL"); }; } while
(0)
;
316
317 if (action_target)
318 g_variant_unref (action_target);
319 return;
320 }
321
322 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s can be activated", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s can be activated", action_name); };
} while (0)
;
323
324 self->sensitive = enabled;
325
326 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s is %s", action_name, enabled ? "enabled" : "disabled"))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s is %s", action_name, enabled ? "enabled"
: "disabled"); }; } while (0)
;
327
328 if (action_target != NULL((void*)0) && state != NULL((void*)0))
329 {
330 self->toggled = g_variant_equal (state, action_target);
331 self->role = CTK_MENU_TRACKER_ITEM_ROLE_RADIO;
332 }
333
334 else if (state != NULL((void*)0) && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN((const GVariantType *) "b")))
335 {
336 self->toggled = g_variant_get_boolean (state);
337 self->role = CTK_MENU_TRACKER_ITEM_ROLE_CHECK;
338 }
339
340 g_object_freeze_notify (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
);
341
342 if (self->sensitive)
343 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
344
345 if (self->toggled)
346 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_TOGGLED]);
347
348 if (self->role != CTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
349 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_ROLE]);
350
351 g_object_thaw_notify (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
);
352
353 if (action_target)
354 g_variant_unref (action_target);
355
356 /* In case of hidden-when='', we want to Wait until after refreshing
357 * all of the properties to emit the signal that will cause the
358 * tracker to expose us (to prevent too much thrashing).
359 */
360 ctk_menu_tracker_item_update_visibility (self);
361}
362
363static void
364ctk_menu_tracker_item_action_enabled_changed (CtkActionObserver *observer,
365 CtkActionObservable *observable G_GNUC_UNUSED__attribute__ ((__unused__)),
366 const gchar *action_name,
367 gboolean enabled)
368{
369 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (observer)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((observer)), ((ctk_menu_tracker_item_get_type
()))))))
;
370
371 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s: enabled changed to %d", action_name, enabled))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s: enabled changed to %d", action_name
, enabled); }; } while (0)
;
372
373 if (!self->can_activate)
374 return;
375
376 if (self->sensitive == enabled)
377 return;
378
379 self->sensitive = enabled;
380
381 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
382
383 ctk_menu_tracker_item_update_visibility (self);
384}
385
386static void
387ctk_menu_tracker_item_action_state_changed (CtkActionObserver *observer,
388 CtkActionObservable *observable G_GNUC_UNUSED__attribute__ ((__unused__)),
389 const gchar *action_name,
390 GVariant *state)
391{
392 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (observer)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((observer)), ((ctk_menu_tracker_item_get_type
()))))))
;
393 GVariant *action_target;
394 gboolean was_toggled;
395
396 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s: state changed", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s: state changed", action_name); }; }
while (0)
;
397
398 if (!self->can_activate)
399 return;
400
401 action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET"target", NULL((void*)0));
402 was_toggled = self->toggled;
403
404 if (action_target)
405 {
406 self->toggled = g_variant_equal (state, action_target);
407 g_variant_unref (action_target);
408 }
409
410 else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN((const GVariantType *) "b")))
411 self->toggled = g_variant_get_boolean (state);
412
413 else
414 self->toggled = FALSE(0);
415
416 if (self->toggled != was_toggled)
417 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_TOGGLED]);
418}
419
420static void
421ctk_menu_tracker_item_action_removed (CtkActionObserver *observer,
422 CtkActionObservable *observable G_GNUC_UNUSED__attribute__ ((__unused__)),
423 const gchar *action_name)
424{
425 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (observer)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((observer)), ((ctk_menu_tracker_item_get_type
()))))))
;
426 gboolean was_sensitive, was_toggled;
427 CtkMenuTrackerItemRole old_role;
428
429 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s was removed", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s was removed", action_name); }; } while
(0)
;
430
431 if (!self->can_activate)
432 return;
433
434 was_sensitive = self->sensitive;
435 was_toggled = self->toggled;
436 old_role = self->role;
437
438 self->can_activate = FALSE(0);
439 self->sensitive = FALSE(0);
440 self->toggled = FALSE(0);
441 self->role = CTK_MENU_TRACKER_ITEM_ROLE_NORMAL;
442
443 /* Backwards from adding: we want to remove ourselves from the menu
444 * -before- thrashing the properties.
445 */
446 ctk_menu_tracker_item_update_visibility (self);
447
448 g_object_freeze_notify (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
);
449
450 if (was_sensitive)
451 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_SENSITIVE]);
452
453 if (was_toggled)
454 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_TOGGLED]);
455
456 if (old_role != CTK_MENU_TRACKER_ITEM_ROLE_NORMAL)
457 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_ROLE]);
458
459 g_object_thaw_notify (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
);
460}
461
462static void
463ctk_menu_tracker_item_primary_accel_changed (CtkActionObserver *observer,
464 CtkActionObservable *observable G_GNUC_UNUSED__attribute__ ((__unused__)),
465 const gchar *action_name G_GNUC_UNUSED__attribute__ ((__unused__)),
466 const gchar *action_and_target)
467{
468 CtkMenuTrackerItem *self = CTK_MENU_TRACKER_ITEM (observer)((((CtkMenuTrackerItem*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((observer)), ((ctk_menu_tracker_item_get_type
()))))))
;
469
470 if (g_str_equal (action_and_target, self->action_and_target)(strcmp ((const char *) (action_and_target), (const char *) (
self->action_and_target)) == 0)
)
471 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_ACCEL]);
472}
473
474static void
475ctk_menu_tracker_item_init_observer_iface (CtkActionObserverInterface *iface)
476{
477 iface->action_added = ctk_menu_tracker_item_action_added;
478 iface->action_enabled_changed = ctk_menu_tracker_item_action_enabled_changed;
479 iface->action_state_changed = ctk_menu_tracker_item_action_state_changed;
480 iface->action_removed = ctk_menu_tracker_item_action_removed;
481 iface->primary_accel_changed = ctk_menu_tracker_item_primary_accel_changed;
482}
483
484CtkMenuTrackerItem *
485_ctk_menu_tracker_item_new (CtkActionObservable *observable,
486 GMenuModel *model,
487 gint item_index,
488 gboolean mac_os_mode,
489 const gchar *action_namespace,
490 gboolean is_separator)
491{
492 CtkMenuTrackerItem *self;
493 const gchar *action_name;
494 const gchar *hidden_when;
495
496 g_return_val_if_fail (CTK_IS_ACTION_OBSERVABLE (observable), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((observable)); GType __t = ((ctk_action_observable_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_ACTION_OBSERVABLE (observable)")
; return (((void*)0)); } } while (0)
;
497 g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((model)); GType __t = ((g_menu_model_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "G_IS_MENU_MODEL (model)"); return (((void*)0)); } } while
(0)
;
498
499 self = g_object_new (CTK_TYPE_MENU_TRACKER_ITEM(ctk_menu_tracker_item_get_type ()), NULL((void*)0));
500 self->item = g_menu_item_new_from_model (model, item_index);
501 self->action_namespace = g_strdup (action_namespace)g_strdup_inline (action_namespace);
502 self->observable = g_object_ref (observable)((__typeof__ (observable)) (g_object_ref) (observable));
503 self->is_separator = is_separator;
504
505 if (!is_separator && g_menu_item_get_attribute (self->item, "hidden-when", "&s", &hidden_when))
506 {
507 if (g_str_equal (hidden_when, "action-disabled")(strcmp ((const char *) (hidden_when), (const char *) ("action-disabled"
)) == 0)
)
508 self->hidden_when = HIDDEN_WHEN_DISABLED2;
509 else if (g_str_equal (hidden_when, "action-missing")(strcmp ((const char *) (hidden_when), (const char *) ("action-missing"
)) == 0)
)
510 self->hidden_when = HIDDEN_WHEN_MISSING1;
511 else if (mac_os_mode && g_str_equal (hidden_when, "macos-menubar")(strcmp ((const char *) (hidden_when), (const char *) ("macos-menubar"
)) == 0)
)
512 self->hidden_when = HIDDEN_WHEN_ALWAYS3;
513
514 /* Ignore other values -- this code may be running in context of a
515 * desktop shell or the like and should not spew criticals due to
516 * application bugs...
517 *
518 * Note: if we just set a hidden-when state, but don't get the
519 * action_name below then our visibility will be FALSE forever.
520 * That's to be expected since the action is missing...
521 */
522 }
523
524 if (!is_separator && g_menu_item_get_attribute (self->item, "action", "&s", &action_name))
525 {
526 GActionGroup *group = G_ACTION_GROUP (observable)((((GActionGroup*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((observable)), ((g_action_group_get_type ()))))))
;
527 const GVariantType *parameter_type;
528 GVariant *target;
529 gboolean enabled;
530 GVariant *state;
531 gboolean found;
532
533 target = g_menu_item_get_attribute_value (self->item, "target", NULL((void*)0));
534
535 self->action_and_target = ctk_print_action_and_target (action_namespace, action_name, target);
536
537 if (target)
538 g_variant_unref (target);
539
540 action_name = strrchr (self->action_and_target, '|') + 1;
541
542 CTK_NOTE(ACTIONS,do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { if
(!strchr (action_name, '.')) g_message ("menutracker: action name %s doesn't look like 'app.' or 'win.'; "
"it is unlikely to work", action_name); }; } while (0)
543 if (!strchr (action_name, '.'))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { if
(!strchr (action_name, '.')) g_message ("menutracker: action name %s doesn't look like 'app.' or 'win.'; "
"it is unlikely to work", action_name); }; } while (0)
544 g_message ("menutracker: action name %s doesn't look like 'app.' or 'win.'; "do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { if
(!strchr (action_name, '.')) g_message ("menutracker: action name %s doesn't look like 'app.' or 'win.'; "
"it is unlikely to work", action_name); }; } while (0)
545 "it is unlikely to work", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { if
(!strchr (action_name, '.')) g_message ("menutracker: action name %s doesn't look like 'app.' or 'win.'; "
"it is unlikely to work", action_name); }; } while (0)
;
546
547 state = NULL((void*)0);
548
549 ctk_action_observable_register_observer (self->observable, action_name, CTK_ACTION_OBSERVER (self)((((CtkActionObserver*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((self)), ((ctk_action_observer_get_type ())
)))))
);
550 found = g_action_group_query_action (group, action_name, &enabled, &parameter_type, NULL((void*)0), NULL((void*)0), &state);
551
552 if (found)
553 {
554 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s existed from the start", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s existed from the start", action_name
); }; } while (0)
;
555 ctk_menu_tracker_item_action_added (CTK_ACTION_OBSERVER (self)((((CtkActionObserver*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((self)), ((ctk_action_observer_get_type ())
)))))
, observable, NULL((void*)0), parameter_type, enabled, state);
556 }
557 else
558 {
559 CTK_NOTE(ACTIONS, g_message ("menutracker: action %s missing from the start", action_name))do { if ((ctk_get_debug_flags () & CTK_DEBUG_ACTIONS)) { g_message
("menutracker: action %s missing from the start", action_name
); }; } while (0)
;
560 ctk_menu_tracker_item_update_visibility (self);
561 }
562
563 if (state)
564 g_variant_unref (state);
565 }
566 else
567 {
568 ctk_menu_tracker_item_update_visibility (self);
569 self->sensitive = TRUE(!(0));
570 }
571
572 return self;
573}
574
575CtkActionObservable *
576_ctk_menu_tracker_item_get_observable (CtkMenuTrackerItem *self)
577{
578 return self->observable;
579}
580
581/*< private >
582 * ctk_menu_tracker_item_get_is_separator:
583 * @self: A #CtkMenuTrackerItem instance
584 *
585 * Returns: whether the menu item is a separator. If so, only
586 * certain properties may need to be obeyed. See the documentation
587 * for #CtkMenuTrackerItem.
588 */
589gboolean
590ctk_menu_tracker_item_get_is_separator (CtkMenuTrackerItem *self)
591{
592 return self->is_separator;
593}
594
595/*< private >
596 * ctk_menu_tracker_item_get_has_submenu:
597 * @self: A #CtkMenuTrackerItem instance
598 *
599 * Returns: whether the menu item has a submenu. If so, only
600 * certain properties may need to be obeyed. See the documentation
601 * for #CtkMenuTrackerItem.
602 */
603gboolean
604ctk_menu_tracker_item_get_has_link (CtkMenuTrackerItem *self,
605 const gchar *link_name)
606{
607 GMenuModel *link;
608
609 link = g_menu_item_get_link (self->item, link_name);
610
611 if (link)
612 {
613 g_object_unref (link);
614 return TRUE(!(0));
615 }
616 else
617 return FALSE(0);
618}
619
620const gchar *
621ctk_menu_tracker_item_get_label (CtkMenuTrackerItem *self)
622{
623 const gchar *label = NULL((void*)0);
624
625 g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_LABEL"label", "&s", &label);
626
627 return label;
628}
629
630/*< private >
631 * ctk_menu_tracker_item_get_icon:
632 *
633 * Returns: (transfer full):
634 */
635GIcon *
636ctk_menu_tracker_item_get_icon (CtkMenuTrackerItem *self)
637{
638 GVariant *icon_data;
639 GIcon *icon;
640
641 icon_data = g_menu_item_get_attribute_value (self->item, "icon", NULL((void*)0));
642
643 if (icon_data == NULL((void*)0))
644 return NULL((void*)0);
645
646 icon = g_icon_deserialize (icon_data);
647 g_variant_unref (icon_data);
648
649 return icon;
650}
651
652/*< private >
653 * ctk_menu_tracker_item_get_verb_icon:
654 *
655 * Returns: (transfer full):
656 */
657GIcon *
658ctk_menu_tracker_item_get_verb_icon (CtkMenuTrackerItem *self)
659{
660 GVariant *icon_data;
661 GIcon *icon;
662
663 icon_data = g_menu_item_get_attribute_value (self->item, "verb-icon", NULL((void*)0));
664
665 if (icon_data == NULL((void*)0))
666 return NULL((void*)0);
667
668 icon = g_icon_deserialize (icon_data);
669 g_variant_unref (icon_data);
670
671 return icon;
672}
673
674gboolean
675ctk_menu_tracker_item_get_sensitive (CtkMenuTrackerItem *self)
676{
677 return self->sensitive;
678}
679
680CtkMenuTrackerItemRole
681ctk_menu_tracker_item_get_role (CtkMenuTrackerItem *self)
682{
683 return self->role;
684}
685
686gboolean
687ctk_menu_tracker_item_get_toggled (CtkMenuTrackerItem *self)
688{
689 return self->toggled;
690}
691
692const gchar *
693ctk_menu_tracker_item_get_accel (CtkMenuTrackerItem *self)
694{
695 const gchar *accel;
696
697 if (!self->action_and_target)
698 return NULL((void*)0);
699
700 if (g_menu_item_get_attribute (self->item, "accel", "&s", &accel))
701 return accel;
702
703 if (!CTK_IS_ACTION_MUXER (self->observable)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(self->observable)); GType __t = ((ctk_action_muxer_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
)
704 return NULL((void*)0);
705
706 return ctk_action_muxer_get_primary_accel (CTK_ACTION_MUXER (self->observable)((((CtkActionMuxer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self->observable)), ((ctk_action_muxer_get_type ())))
)))
, self->action_and_target);
707}
708
709const gchar *
710ctk_menu_tracker_item_get_special (CtkMenuTrackerItem *self)
711{
712 const gchar *special = NULL((void*)0);
713
714 g_menu_item_get_attribute (self->item, "x-ctk-private-special", "&s", &special);
715
716 return special;
717}
718
719const gchar *
720ctk_menu_tracker_item_get_display_hint (CtkMenuTrackerItem *self)
721{
722 const gchar *display_hint = NULL((void*)0);
723
724 g_menu_item_get_attribute (self->item, "display-hint", "&s", &display_hint);
725
726 return display_hint;
727}
728
729const gchar *
730ctk_menu_tracker_item_get_text_direction (CtkMenuTrackerItem *self)
731{
732 const gchar *text_direction = NULL((void*)0);
733
734 g_menu_item_get_attribute (self->item, "text-direction", "&s", &text_direction);
735
736 return text_direction;
737}
738
739GMenuModel *
740_ctk_menu_tracker_item_get_link (CtkMenuTrackerItem *self,
741 const gchar *link_name)
742{
743 return g_menu_item_get_link (self->item, link_name);
744}
745
746gchar *
747_ctk_menu_tracker_item_get_link_namespace (CtkMenuTrackerItem *self)
748{
749 const gchar *namespace;
750
751 if (g_menu_item_get_attribute (self->item, "action-namespace", "&s", &namespace))
752 {
753 if (self->action_namespace)
754 return g_strjoin (".", self->action_namespace, namespace, NULL((void*)0));
755 else
756 return g_strdup (namespace)g_strdup_inline (namespace);
757 }
758 else
759 return g_strdup (self->action_namespace)g_strdup_inline (self->action_namespace);
760}
761
762gboolean
763ctk_menu_tracker_item_get_should_request_show (CtkMenuTrackerItem *self)
764{
765 return g_menu_item_get_attribute (self->item, "submenu-action", "&s", NULL((void*)0));
766}
767
768gboolean
769ctk_menu_tracker_item_get_submenu_shown (CtkMenuTrackerItem *self)
770{
771 return self->submenu_shown;
772}
773
774static void
775ctk_menu_tracker_item_set_submenu_shown (CtkMenuTrackerItem *self,
776 gboolean submenu_shown)
777{
778 if (submenu_shown == self->submenu_shown)
779 return;
780
781 self->submenu_shown = submenu_shown;
782 g_object_notify_by_pspec (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, ctk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN]);
783}
784
785void
786ctk_menu_tracker_item_activated (CtkMenuTrackerItem *self)
787{
788 const gchar *action_name;
789 GVariant *action_target;
790
791 g_return_if_fail (CTK_IS_MENU_TRACKER_ITEM (self))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((self)); GType __t = ((ctk_menu_tracker_item_get_type ())
); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "CTK_IS_MENU_TRACKER_ITEM (self)"); return
; } } while (0)
;
792
793 if (!self->can_activate)
794 return;
795
796 action_name = strrchr (self->action_and_target, '|') + 1;
797 action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET"target", NULL((void*)0));
798
799 g_action_group_activate_action (G_ACTION_GROUP (self->observable)((((GActionGroup*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self->observable)), ((g_action_group_get_type ())))))
)
, action_name, action_target);
800
801 if (action_target)
802 g_variant_unref (action_target);
803}
804
805typedef struct
806{
807 CtkMenuTrackerItem *item;
808 gchar *submenu_action;
809 gboolean first_time;
810} CtkMenuTrackerOpener;
811
812static void
813ctk_menu_tracker_opener_update (CtkMenuTrackerOpener *opener)
814{
815 GActionGroup *group = G_ACTION_GROUP (opener->item->observable)((((GActionGroup*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((opener->item->observable)), ((g_action_group_get_type
()))))))
;
816 gboolean is_open = TRUE(!(0));
817
818 /* We consider the menu as being "open" if the action does not exist
819 * or if there is another problem (no state, wrong state type, etc.).
820 * If the action exists, with the correct state then we consider it
821 * open if we have ever seen this state equal to TRUE.
822 *
823 * In the event that we see the state equal to FALSE, we force it back
824 * to TRUE. We do not signal that the menu was closed because this is
825 * likely to create UI thrashing.
826 *
827 * The only way the menu can have a true-to-false submenu-shown
828 * transition is if the user calls _request_submenu_shown (FALSE).
829 * That is handled in _free() below.
830 */
831
832 if (g_action_group_has_action (group, opener->submenu_action))
833 {
834 GVariant *state = g_action_group_get_action_state (group, opener->submenu_action);
835
836 if (state)
837 {
838 if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN((const GVariantType *) "b")))
Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption
839 is_open = g_variant_get_boolean (state);
840 g_variant_unref (state);
841 }
842 }
843
844 /* If it is already open, signal that.
845 *
846 * If it is not open, ask it to open.
847 */
848 if (is_open)
849 ctk_menu_tracker_item_set_submenu_shown (opener->item, TRUE(!(0)));
850
851 if (!is_open || opener->first_time)
852 {
853 g_action_group_change_action_state (group, opener->submenu_action, g_variant_new_boolean (TRUE(!(0))));
854 opener->first_time = FALSE(0);
855 }
856}
857
858static void
859ctk_menu_tracker_opener_added (GActionGroup *group G_GNUC_UNUSED__attribute__ ((__unused__)),
860 const gchar *action_name,
861 gpointer user_data)
862{
863 CtkMenuTrackerOpener *opener = user_data;
864
865 if (g_str_equal (action_name, opener->submenu_action)(strcmp ((const char *) (action_name), (const char *) (opener
->submenu_action)) == 0)
)
866 ctk_menu_tracker_opener_update (opener);
867}
868
869static void
870ctk_menu_tracker_opener_removed (GActionGroup *action_group G_GNUC_UNUSED__attribute__ ((__unused__)),
871 const gchar *action_name,
872 gpointer user_data)
873{
874 CtkMenuTrackerOpener *opener = user_data;
875
876 if (g_str_equal (action_name, opener->submenu_action)(strcmp ((const char *) (action_name), (const char *) (opener
->submenu_action)) == 0)
)
877 ctk_menu_tracker_opener_update (opener);
878}
879
880static void
881ctk_menu_tracker_opener_changed (GActionGroup *action_group G_GNUC_UNUSED__attribute__ ((__unused__)),
882 const gchar *action_name,
883 GVariant *new_state G_GNUC_UNUSED__attribute__ ((__unused__)),
884 gpointer user_data)
885{
886 CtkMenuTrackerOpener *opener = user_data;
887
888 if (g_str_equal (action_name, opener->submenu_action)(strcmp ((const char *) (action_name), (const char *) (opener
->submenu_action)) == 0)
)
889 ctk_menu_tracker_opener_update (opener);
890}
891
892static void
893ctk_menu_tracker_opener_free (gpointer data)
894{
895 CtkMenuTrackerOpener *opener = data;
896
897 g_signal_handlers_disconnect_by_func (opener->item->observable, ctk_menu_tracker_opener_added, opener)g_signal_handlers_disconnect_matched ((opener->item->observable
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), (ctk_menu_tracker_opener_added), (opener
))
;
898 g_signal_handlers_disconnect_by_func (opener->item->observable, ctk_menu_tracker_opener_removed, opener)g_signal_handlers_disconnect_matched ((opener->item->observable
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), (ctk_menu_tracker_opener_removed), (opener
))
;
899 g_signal_handlers_disconnect_by_func (opener->item->observable, ctk_menu_tracker_opener_changed, opener)g_signal_handlers_disconnect_matched ((opener->item->observable
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), (ctk_menu_tracker_opener_changed), (opener
))
;
900
901 g_action_group_change_action_state (G_ACTION_GROUP (opener->item->observable)((((GActionGroup*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((opener->item->observable)), ((g_action_group_get_type
()))))))
,
902 opener->submenu_action,
903 g_variant_new_boolean (FALSE(0)));
904
905 ctk_menu_tracker_item_set_submenu_shown (opener->item, FALSE(0));
906
907 g_free (opener->submenu_action);
908
909 g_slice_free (CtkMenuTrackerOpener, opener)do { if (1) g_slice_free1 (sizeof (CtkMenuTrackerOpener), (opener
)); else (void) ((CtkMenuTrackerOpener*) 0 == (opener)); } while
(0)
;
910}
911
912static CtkMenuTrackerOpener *
913ctk_menu_tracker_opener_new (CtkMenuTrackerItem *item,
914 const gchar *submenu_action)
915{
916 CtkMenuTrackerOpener *opener;
917
918 opener = g_slice_new (CtkMenuTrackerOpener)((CtkMenuTrackerOpener*) g_slice_alloc (sizeof (CtkMenuTrackerOpener
)))
;
919 opener->first_time = TRUE(!(0));
920 opener->item = item;
921
922 if (item->action_namespace)
923 opener->submenu_action = g_strjoin (".", item->action_namespace, submenu_action, NULL((void*)0));
924 else
925 opener->submenu_action = g_strdup (submenu_action)g_strdup_inline (submenu_action);
926
927 g_signal_connect (item->observable, "action-added", G_CALLBACK (ctk_menu_tracker_opener_added), opener)g_signal_connect_data ((item->observable), ("action-added"
), (((GCallback) (ctk_menu_tracker_opener_added))), (opener),
((void*)0), (GConnectFlags) 0)
;
928 g_signal_connect (item->observable, "action-removed", G_CALLBACK (ctk_menu_tracker_opener_removed), opener)g_signal_connect_data ((item->observable), ("action-removed"
), (((GCallback) (ctk_menu_tracker_opener_removed))), (opener
), ((void*)0), (GConnectFlags) 0)
;
929 g_signal_connect (item->observable, "action-state-changed", G_CALLBACK (ctk_menu_tracker_opener_changed), opener)g_signal_connect_data ((item->observable), ("action-state-changed"
), (((GCallback) (ctk_menu_tracker_opener_changed))), (opener
), ((void*)0), (GConnectFlags) 0)
;
930
931 ctk_menu_tracker_opener_update (opener);
932
933 return opener;
934}
935
936void
937ctk_menu_tracker_item_request_submenu_shown (CtkMenuTrackerItem *self,
938 gboolean shown)
939{
940 const gchar *submenu_action;
941 gboolean has_submenu_action;
942
943 if (shown == self->submenu_requested)
944 return;
945
946 has_submenu_action = g_menu_item_get_attribute (self->item, "submenu-action", "&s", &submenu_action);
947
948 self->submenu_requested = shown;
949
950 /* If we have a submenu action, start a submenu opener and wait
951 * for the reply from the client. Otherwise, simply open the
952 * submenu immediately.
953 */
954 if (has_submenu_action)
955 {
956 if (shown)
957 g_object_set_data_full (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, "submenu-opener",
958 ctk_menu_tracker_opener_new (self, submenu_action),
959 ctk_menu_tracker_opener_free);
960 else
961 g_object_set_data (G_OBJECT (self)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((self)), (((GType) ((20) << (2))))))))
, "submenu-opener", NULL((void*)0));
962 }
963 else
964 ctk_menu_tracker_item_set_submenu_shown (self, shown);
965}
966
967/**
968 * ctk_menu_tracker_item_get_is_visible:
969 * @self: A #CtkMenuTrackerItem instance
970 *
971 * Don't use this unless you're tracking items for yourself -- normally
972 * the tracker will emit add/remove automatically when this changes.
973 *
974 * Returns: if the item should currently be shown
975 */
976gboolean
977ctk_menu_tracker_item_get_is_visible (CtkMenuTrackerItem *self)
978{
979 return self->is_visible;
980}
981
982/**
983 * ctk_menu_tracker_item_may_disappear:
984 * @self: A #CtkMenuTrackerItem instance
985 *
986 * Returns: if the item may disappear (ie: is-visible property may change)
987 */
988gboolean
989ctk_menu_tracker_item_may_disappear (CtkMenuTrackerItem *self)
990{
991 return self->hidden_when != HIDDEN_NEVER0;
992}