1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #include <unx/gtk/gtksalmenu.hxx>
11
12 #include <unx/gtk/gloactiongroup.h>
13
14 #include <sal/log.hxx>
15
16 /*
17 * GLOAction
18 */
19
20 #define G_TYPE_LO_ACTION (g_lo_action_get_type ())
21 #define G_LO_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
22 G_TYPE_LO_ACTION, GLOAction))
23 namespace {
24
25 struct GLOAction
26 {
27 GObject parent_instance;
28
29 gint item_id; // Menu item ID.
30 bool submenu; // TRUE if action is a submenu action.
31 bool enabled; // TRUE if action is enabled.
32 GVariantType* parameter_type; // A GVariantType with the action parameter type.
33 GVariantType* state_type; // A GVariantType with item state type
34 GVariant* state_hint; // A GVariant with state hints.
35 GVariant* state; // A GVariant with current item state
36 };
37
38 }
39
40 typedef GObjectClass GLOActionClass;
41
42 #ifdef __GNUC__
43 #pragma GCC diagnostic push
44 #pragma GCC diagnostic ignored "-Wunused-function"
45 #if defined __clang__
46 #if __has_warning("-Wdeprecated-volatile")
47 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
48 #endif
49 #endif
50 #endif
51 G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT);
52 #ifdef __GNUC__
53 #pragma GCC diagnostic pop
54 #endif
55
56 static GLOAction*
g_lo_action_new()57 g_lo_action_new()
58 {
59 return G_LO_ACTION (g_object_new (G_TYPE_LO_ACTION, nullptr));
60 }
61
62 static void
g_lo_action_init(GLOAction * action)63 g_lo_action_init (GLOAction *action)
64 {
65 action->item_id = -1;
66 action->submenu = false;
67 action->enabled = true;
68 action->parameter_type = nullptr;
69 action->state_type = nullptr;
70 action->state_hint = nullptr;
71 action->state = nullptr;
72 }
73
74 static void
g_lo_action_finalize(GObject * object)75 g_lo_action_finalize (GObject *object)
76 {
77 GLOAction* action = G_LO_ACTION(object);
78
79 if (action->parameter_type)
80 g_variant_type_free (action->parameter_type);
81
82 if (action->state_type)
83 g_variant_type_free (action->state_type);
84
85 if (action->state_hint)
86 g_variant_unref (action->state_hint);
87
88 if (action->state)
89 g_variant_unref (action->state);
90
91 G_OBJECT_CLASS (g_lo_action_parent_class)->finalize (object);
92 }
93
94 static void
g_lo_action_class_init(GLOActionClass * klass)95 g_lo_action_class_init (GLOActionClass *klass)
96 {
97 GObjectClass *object_class = G_OBJECT_CLASS(klass);
98
99 object_class->finalize = g_lo_action_finalize;
100 }
101
102 /*
103 * GLOActionGroup
104 */
105
106 struct GLOActionGroupPrivate
107 {
108 GHashTable *table; /* string -> GLOAction */
109 };
110
111 static void g_lo_action_group_iface_init (GActionGroupInterface *);
112
113 #ifdef __GNUC__
114 #pragma GCC diagnostic push
115 #pragma GCC diagnostic ignored "-Wunused-function"
116 #if defined __clang__
117 #if __has_warning("-Wdeprecated-volatile")
118 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
119 #endif
120 #endif
121 #endif
122 G_DEFINE_TYPE_WITH_CODE (GLOActionGroup,
123 g_lo_action_group, G_TYPE_OBJECT,
124 G_ADD_PRIVATE(GLOActionGroup)
125 G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
126 g_lo_action_group_iface_init));
127 #ifdef __GNUC__
128 #pragma GCC diagnostic pop
129 #endif
130
131 static gchar **
g_lo_action_group_list_actions(GActionGroup * group)132 g_lo_action_group_list_actions (GActionGroup *group)
133 {
134 GLOActionGroup *loGroup = G_LO_ACTION_GROUP (group);
135 GHashTableIter iter;
136 gint n, i = 0;
137 gchar **keys;
138 gpointer key;
139
140 n = g_hash_table_size (loGroup->priv->table);
141 keys = g_new (gchar *, n + 1);
142
143 g_hash_table_iter_init (&iter, loGroup->priv->table);
144 while (g_hash_table_iter_next (&iter, &key, nullptr))
145 keys[i++] = g_strdup (static_cast<gchar*>(key));
146 g_assert_cmpint (i, ==, n);
147 keys[n] = nullptr;
148
149 return keys;
150 }
151
152 static gboolean
g_lo_action_group_query_action(GActionGroup * group,const gchar * action_name,gboolean * enabled,const GVariantType ** parameter_type,const GVariantType ** state_type,GVariant ** state_hint,GVariant ** state)153 g_lo_action_group_query_action (GActionGroup *group,
154 const gchar *action_name,
155 gboolean *enabled,
156 const GVariantType **parameter_type,
157 const GVariantType **state_type,
158 GVariant **state_hint,
159 GVariant **state)
160 {
161 //SAL_INFO("vcl.unity", "g_lo_action_group_query_action on " << group);
162 GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group);
163 GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name));
164
165 if (action == nullptr)
166 return FALSE;
167
168 if (enabled)
169 {
170 *enabled = action->enabled;
171 }
172
173 if (parameter_type)
174 *parameter_type = action->parameter_type;
175
176 if (state_type)
177 *state_type = action->state_type;
178
179 if (state_hint)
180 *state_hint = (action->state_hint) ? g_variant_ref (action->state_hint) : nullptr;
181
182 if (state)
183 *state = (action->state) ? g_variant_ref (action->state) : nullptr;
184
185 return true;
186 }
187
188 static void
g_lo_action_group_perform_submenu_action(GLOActionGroup * group,const gchar * action_name,GVariant * state)189 g_lo_action_group_perform_submenu_action (GLOActionGroup *group,
190 const gchar *action_name,
191 GVariant *state)
192 {
193 bool bState = g_variant_get_boolean (state);
194 SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState);
195
196 if (bState)
197 GtkSalMenu::Activate(action_name);
198 else
199 GtkSalMenu::Deactivate(action_name);
200 }
201
202 static void
g_lo_action_group_change_state(GActionGroup * group,const gchar * action_name,GVariant * value)203 g_lo_action_group_change_state (GActionGroup *group,
204 const gchar *action_name,
205 GVariant *value)
206 {
207 SAL_INFO("vcl.unity", "g_lo_action_group_change_state on " << group );
208 g_return_if_fail (value != nullptr);
209
210 g_variant_ref_sink (value);
211
212 if (action_name != nullptr)
213 {
214 GLOActionGroup* lo_group = G_LO_ACTION_GROUP (group);
215 GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name));
216
217 if (action != nullptr)
218 {
219 if (action->submenu)
220 g_lo_action_group_perform_submenu_action (lo_group, action_name, value);
221 else
222 {
223 bool is_new = false;
224
225 /* If action already exists but has no state, it should be removed and added again. */
226 if (action->state_type == nullptr)
227 {
228 g_action_group_action_removed (G_ACTION_GROUP (group), action_name);
229 action->state_type = g_variant_type_copy (g_variant_get_type(value));
230 is_new = true;
231 }
232
233 if (g_variant_is_of_type (value, action->state_type))
234 {
235 if (action->state)
236 g_variant_unref(action->state);
237
238 action->state = g_variant_ref (value);
239
240 if (is_new)
241 g_action_group_action_added (G_ACTION_GROUP (group), action_name);
242 else
243 g_action_group_action_state_changed (group, action_name, value);
244 }
245 }
246 }
247 }
248
249 g_variant_unref (value);
250 }
251
252 static void
g_lo_action_group_activate(GActionGroup * group,const gchar * action_name,GVariant * parameter)253 g_lo_action_group_activate (GActionGroup *group,
254 const gchar *action_name,
255 GVariant *parameter)
256 {
257 if (parameter != nullptr)
258 g_action_group_change_action_state(group, action_name, parameter);
259 GtkSalMenu::DispatchCommand(action_name);
260 }
261
262 void
g_lo_action_group_insert(GLOActionGroup * group,const gchar * action_name,gint item_id,gboolean submenu)263 g_lo_action_group_insert (GLOActionGroup *group,
264 const gchar *action_name,
265 gint item_id,
266 gboolean submenu)
267 {
268 g_lo_action_group_insert_stateful (group, action_name, item_id, submenu, nullptr, nullptr, nullptr, nullptr);
269 }
270
271 void
g_lo_action_group_insert_stateful(GLOActionGroup * group,const gchar * action_name,gint item_id,gboolean submenu,const GVariantType * parameter_type,const GVariantType * state_type,GVariant * state_hint,GVariant * state)272 g_lo_action_group_insert_stateful (GLOActionGroup *group,
273 const gchar *action_name,
274 gint item_id,
275 gboolean submenu,
276 const GVariantType *parameter_type,
277 const GVariantType *state_type,
278 GVariant *state_hint,
279 GVariant *state)
280 {
281 g_return_if_fail (G_IS_LO_ACTION_GROUP (group));
282
283 GLOAction* old_action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name));
284
285 if (old_action != nullptr && old_action->item_id == item_id)
286 return;
287
288 if (old_action != nullptr)
289 g_lo_action_group_remove (group, action_name);
290
291 GLOAction* action = g_lo_action_new();
292
293 g_hash_table_insert (group->priv->table, g_strdup (action_name), action);
294
295 action->item_id = item_id;
296 action->submenu = submenu;
297
298 if (parameter_type)
299 action->parameter_type = const_cast<GVariantType*>(parameter_type);
300
301 if (state_type)
302 action->state_type = const_cast<GVariantType*>(state_type);
303
304 if (state_hint)
305 action->state_hint = g_variant_ref_sink (state_hint);
306
307 if (state)
308 action->state = g_variant_ref_sink (state);
309
310 g_action_group_action_added (G_ACTION_GROUP (group), action_name);
311 }
312
313 static void
g_lo_action_group_finalize(GObject * object)314 g_lo_action_group_finalize (GObject *object)
315 {
316 GLOActionGroup *lo_group = G_LO_ACTION_GROUP (object);
317
318 g_hash_table_unref (lo_group->priv->table);
319
320 G_OBJECT_CLASS (g_lo_action_group_parent_class)->finalize (object);
321 }
322
323 static void
g_lo_action_group_init(GLOActionGroup * group)324 g_lo_action_group_init (GLOActionGroup *group)
325 {
326 SAL_INFO("vcl.unity", "g_lo_action_group_init on " << group);
327 group->priv = static_cast<GLOActionGroupPrivate *>(g_lo_action_group_get_instance_private (group));
328 group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal,
329 g_free, g_object_unref);
330 }
331
332 static void
g_lo_action_group_class_init(GLOActionGroupClass * klass)333 g_lo_action_group_class_init (GLOActionGroupClass *klass)
334 {
335 GObjectClass *object_class = G_OBJECT_CLASS (klass);
336
337 object_class->finalize = g_lo_action_group_finalize;
338 }
339
340 static void
g_lo_action_group_iface_init(GActionGroupInterface * iface)341 g_lo_action_group_iface_init (GActionGroupInterface *iface)
342 {
343 iface->list_actions = g_lo_action_group_list_actions;
344 iface->query_action = g_lo_action_group_query_action;
345 iface->change_action_state = g_lo_action_group_change_state;
346 iface->activate_action = g_lo_action_group_activate;
347 }
348
349 GLOActionGroup *
g_lo_action_group_new()350 g_lo_action_group_new()
351 {
352 GLOActionGroup* group = G_LO_ACTION_GROUP (g_object_new (G_TYPE_LO_ACTION_GROUP, nullptr));
353 return group;
354 }
355
356 void
g_lo_action_group_set_action_enabled(GLOActionGroup * group,const gchar * action_name,gboolean enabled)357 g_lo_action_group_set_action_enabled (GLOActionGroup *group,
358 const gchar *action_name,
359 gboolean enabled)
360 {
361 SAL_INFO("vcl.unity", "g_lo_action_group_set_action_enabled on " << group);
362 g_return_if_fail (G_IS_LO_ACTION_GROUP (group));
363 g_return_if_fail (action_name != nullptr);
364
365 GLOAction* action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name));
366
367 if (action == nullptr)
368 return;
369
370 action->enabled = enabled;
371
372 g_action_group_action_enabled_changed (G_ACTION_GROUP (group), action_name, enabled);
373 }
374
375 void
g_lo_action_group_remove(GLOActionGroup * group,const gchar * action_name)376 g_lo_action_group_remove (GLOActionGroup *group,
377 const gchar *action_name)
378 {
379 SAL_INFO("vcl.unity", "g_lo_action_group_remove on " << group);
380 g_return_if_fail (G_IS_LO_ACTION_GROUP (group));
381
382 if (action_name != nullptr)
383 {
384 g_action_group_action_removed (G_ACTION_GROUP (group), action_name);
385 g_hash_table_remove (group->priv->table, action_name);
386 }
387 }
388
389 void
g_lo_action_group_clear(GLOActionGroup * group)390 g_lo_action_group_clear (GLOActionGroup *group)
391 {
392 SAL_INFO("vcl.unity", "g_lo_action_group_clear on " << group);
393 g_return_if_fail (G_IS_LO_ACTION_GROUP (group));
394
395 GList* keys = g_hash_table_get_keys (group->priv->table);
396
397 for (GList* element = g_list_first (keys); element != nullptr; element = g_list_next (element))
398 {
399 g_lo_action_group_remove (group, static_cast<gchar*>(element->data));
400 }
401
402 g_list_free (keys);
403 }
404
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
406