/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include /* * GLOAction */ #define G_TYPE_LO_ACTION (g_lo_action_get_type ()) #define G_LO_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ G_TYPE_LO_ACTION, GLOAction)) namespace { struct GLOAction { GObject parent_instance; gint item_id; // Menu item ID. bool submenu; // TRUE if action is a submenu action. bool enabled; // TRUE if action is enabled. GVariantType* parameter_type; // A GVariantType with the action parameter type. GVariantType* state_type; // A GVariantType with item state type GVariant* state_hint; // A GVariant with state hints. GVariant* state; // A GVariant with current item state }; } typedef GObjectClass GLOActionClass; #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #if defined __clang__ #if __has_warning("-Wdeprecated-volatile") #pragma clang diagnostic ignored "-Wdeprecated-volatile" #endif #endif #endif G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif static GLOAction* g_lo_action_new() { return G_LO_ACTION (g_object_new (G_TYPE_LO_ACTION, nullptr)); } static void g_lo_action_init (GLOAction *action) { action->item_id = -1; action->submenu = false; action->enabled = true; action->parameter_type = nullptr; action->state_type = nullptr; action->state_hint = nullptr; action->state = nullptr; } static void g_lo_action_finalize (GObject *object) { GLOAction* action = G_LO_ACTION(object); if (action->parameter_type) g_variant_type_free (action->parameter_type); if (action->state_type) g_variant_type_free (action->state_type); if (action->state_hint) g_variant_unref (action->state_hint); if (action->state) g_variant_unref (action->state); G_OBJECT_CLASS (g_lo_action_parent_class)->finalize (object); } static void g_lo_action_class_init (GLOActionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = g_lo_action_finalize; } /* * GLOActionGroup */ struct GLOActionGroupPrivate { GHashTable *table; /* string -> GLOAction */ }; static void g_lo_action_group_iface_init (GActionGroupInterface *); #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #if defined __clang__ #if __has_warning("-Wdeprecated-volatile") #pragma clang diagnostic ignored "-Wdeprecated-volatile" #endif #endif #endif G_DEFINE_TYPE_WITH_CODE (GLOActionGroup, g_lo_action_group, G_TYPE_OBJECT, G_ADD_PRIVATE(GLOActionGroup) G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_lo_action_group_iface_init)); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif static gchar ** g_lo_action_group_list_actions (GActionGroup *group) { GLOActionGroup *loGroup = G_LO_ACTION_GROUP (group); GHashTableIter iter; gint n, i = 0; gchar **keys; gpointer key; n = g_hash_table_size (loGroup->priv->table); keys = g_new (gchar *, n + 1); g_hash_table_iter_init (&iter, loGroup->priv->table); while (g_hash_table_iter_next (&iter, &key, nullptr)) keys[i++] = g_strdup (static_cast(key)); g_assert_cmpint (i, ==, n); keys[n] = nullptr; return keys; } 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) { //SAL_INFO("vcl.unity", "g_lo_action_group_query_action on " << group); GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group); GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); if (action == nullptr) return FALSE; if (enabled) { *enabled = action->enabled; } if (parameter_type) *parameter_type = action->parameter_type; if (state_type) *state_type = action->state_type; if (state_hint) *state_hint = (action->state_hint) ? g_variant_ref (action->state_hint) : nullptr; if (state) *state = (action->state) ? g_variant_ref (action->state) : nullptr; return true; } static void g_lo_action_group_perform_submenu_action (GLOActionGroup *group, const gchar *action_name, GVariant *state) { bool bState = g_variant_get_boolean (state); SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState); if (bState) GtkSalMenu::Activate(action_name); else GtkSalMenu::Deactivate(action_name); } static void g_lo_action_group_change_state (GActionGroup *group, const gchar *action_name, GVariant *value) { SAL_INFO("vcl.unity", "g_lo_action_group_change_state on " << group ); g_return_if_fail (value != nullptr); g_variant_ref_sink (value); if (action_name != nullptr) { GLOActionGroup* lo_group = G_LO_ACTION_GROUP (group); GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name)); if (action != nullptr) { if (action->submenu) g_lo_action_group_perform_submenu_action (lo_group, action_name, value); else { bool is_new = false; /* If action already exists but has no state, it should be removed and added again. */ if (action->state_type == nullptr) { g_action_group_action_removed (G_ACTION_GROUP (group), action_name); action->state_type = g_variant_type_copy (g_variant_get_type(value)); is_new = true; } if (g_variant_is_of_type (value, action->state_type)) { if (action->state) g_variant_unref(action->state); action->state = g_variant_ref (value); if (is_new) g_action_group_action_added (G_ACTION_GROUP (group), action_name); else g_action_group_action_state_changed (group, action_name, value); } } } } g_variant_unref (value); } static void g_lo_action_group_activate (GActionGroup *group, const gchar *action_name, GVariant *parameter) { if (parameter != nullptr) g_action_group_change_action_state(group, action_name, parameter); GtkSalMenu::DispatchCommand(action_name); } void g_lo_action_group_insert (GLOActionGroup *group, const gchar *action_name, gint item_id, gboolean submenu) { g_lo_action_group_insert_stateful (group, action_name, item_id, submenu, nullptr, nullptr, nullptr, nullptr); } 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) { g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); GLOAction* old_action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); if (old_action != nullptr && old_action->item_id == item_id) return; if (old_action != nullptr) g_lo_action_group_remove (group, action_name); GLOAction* action = g_lo_action_new(); g_hash_table_insert (group->priv->table, g_strdup (action_name), action); action->item_id = item_id; action->submenu = submenu; if (parameter_type) action->parameter_type = const_cast(parameter_type); if (state_type) action->state_type = const_cast(state_type); if (state_hint) action->state_hint = g_variant_ref_sink (state_hint); if (state) action->state = g_variant_ref_sink (state); g_action_group_action_added (G_ACTION_GROUP (group), action_name); } static void g_lo_action_group_finalize (GObject *object) { GLOActionGroup *lo_group = G_LO_ACTION_GROUP (object); g_hash_table_unref (lo_group->priv->table); G_OBJECT_CLASS (g_lo_action_group_parent_class)->finalize (object); } static void g_lo_action_group_init (GLOActionGroup *group) { SAL_INFO("vcl.unity", "g_lo_action_group_init on " << group); group->priv = static_cast(g_lo_action_group_get_instance_private (group)); group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); } static void g_lo_action_group_class_init (GLOActionGroupClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = g_lo_action_group_finalize; } static void g_lo_action_group_iface_init (GActionGroupInterface *iface) { iface->list_actions = g_lo_action_group_list_actions; iface->query_action = g_lo_action_group_query_action; iface->change_action_state = g_lo_action_group_change_state; iface->activate_action = g_lo_action_group_activate; } GLOActionGroup * g_lo_action_group_new() { GLOActionGroup* group = G_LO_ACTION_GROUP (g_object_new (G_TYPE_LO_ACTION_GROUP, nullptr)); return group; } void g_lo_action_group_set_action_enabled (GLOActionGroup *group, const gchar *action_name, gboolean enabled) { SAL_INFO("vcl.unity", "g_lo_action_group_set_action_enabled on " << group); g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); g_return_if_fail (action_name != nullptr); GLOAction* action = G_LO_ACTION (g_hash_table_lookup (group->priv->table, action_name)); if (action == nullptr) return; action->enabled = enabled; g_action_group_action_enabled_changed (G_ACTION_GROUP (group), action_name, enabled); } void g_lo_action_group_remove (GLOActionGroup *group, const gchar *action_name) { SAL_INFO("vcl.unity", "g_lo_action_group_remove on " << group); g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); if (action_name != nullptr) { g_action_group_action_removed (G_ACTION_GROUP (group), action_name); g_hash_table_remove (group->priv->table, action_name); } } void g_lo_action_group_clear (GLOActionGroup *group) { SAL_INFO("vcl.unity", "g_lo_action_group_clear on " << group); g_return_if_fail (G_IS_LO_ACTION_GROUP (group)); GList* keys = g_hash_table_get_keys (group->priv->table); for (GList* element = g_list_first (keys); element != nullptr; element = g_list_next (element)) { g_lo_action_group_remove (group, static_cast(element->data)); } g_list_free (keys); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */