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