aboutsummaryrefslogtreecommitdiff
path: root/libqmenumodel/src/gtk/gtkactionmuxer.c
diff options
context:
space:
mode:
Diffstat (limited to 'libqmenumodel/src/gtk/gtkactionmuxer.c')
-rw-r--r--libqmenumodel/src/gtk/gtkactionmuxer.c211
1 files changed, 192 insertions, 19 deletions
diff --git a/libqmenumodel/src/gtk/gtkactionmuxer.c b/libqmenumodel/src/gtk/gtkactionmuxer.c
index 4618564..3080b4b 100644
--- a/libqmenumodel/src/gtk/gtkactionmuxer.c
+++ b/libqmenumodel/src/gtk/gtkactionmuxer.c
@@ -26,7 +26,7 @@
#include <string.h>
-/**
+/*< private >
* SECTION:gtkactionmuxer
* @short_description: Aggregate and monitor several action groups
*
@@ -37,19 +37,19 @@
* particular context into a single action group, with namespacing.
*
* Consider the case of two action groups -- one containing actions
- * applicable to an entire application (such as 'quit') and one
+ * applicable to an entire application (such as “quit”) and one
* containing actions applicable to a particular window in the
- * application (such as 'fullscreen').
+ * application (such as “fullscreen”).
*
* In this case, each of these action groups could be added to a
- * #GtkActionMuxer with the prefixes "app" and "win", respectively. This
- * would expose the actions as "app.quit" and "win.fullscreen" on the
+ * #GtkActionMuxer with the prefixes “app” and “win”, respectively. This
+ * would expose the actions as “app.quit” and “win.fullscreen” on the
* #GActionGroup interface presented by the #GtkActionMuxer.
*
* Activations and state change requests on the #GtkActionMuxer are wired
* through to the underlying action group in the expected way.
*
- * This class is typically only used at the site of "consumption" of
+ * This class is typically only used at the site of “consumption” of
* actions (eg: when displaying a menu that contains many actions on
* different objects).
*/
@@ -65,6 +65,7 @@ struct _GtkActionMuxer
GHashTable *observed_actions;
GHashTable *groups;
+ GHashTable *primary_accels;
GtkActionMuxer *parent;
};
@@ -81,6 +82,8 @@ enum
static GParamSpec *properties[NUM_PROPERTIES];
+guint accel_signal;
+
typedef struct
{
GtkActionMuxer *muxer;
@@ -134,7 +137,7 @@ gtk_action_muxer_list_actions (GActionGroup *action_group)
actions);
}
- return (gchar **) g_array_free (actions, FALSE);
+ return (gchar **)(void *) g_array_free (actions, FALSE);
}
static Group *
@@ -333,6 +336,39 @@ gtk_action_muxer_action_removed_from_parent (GActionGroup *action_group,
gtk_action_muxer_action_removed (muxer, action_name);
}
+static void
+gtk_action_muxer_primary_accel_changed (GtkActionMuxer *muxer,
+ const gchar *action_name,
+ const gchar *action_and_target)
+{
+ Action *action;
+ GSList *node;
+
+ if (!action_name)
+ action_name = strrchr (action_and_target, '|') + 1;
+
+ action = g_hash_table_lookup (muxer->observed_actions, action_name);
+ for (node = action ? action->watchers : NULL; node; node = node->next)
+ gtk_action_observer_primary_accel_changed (node->data, GTK_ACTION_OBSERVABLE (muxer),
+ action_name, action_and_target);
+ g_signal_emit (muxer, accel_signal, 0, action_name, action_and_target);
+}
+
+static void
+gtk_action_muxer_parent_primary_accel_changed (GtkActionMuxer *parent,
+ const gchar *action_name,
+ const gchar *action_and_target,
+ gpointer user_data)
+{
+ GtkActionMuxer *muxer = user_data;
+
+ /* If it's in our table then don't let the parent one filter through */
+ if (muxer->primary_accels && g_hash_table_lookup (muxer->primary_accels, action_and_target))
+ return;
+
+ gtk_action_muxer_primary_accel_changed (muxer, action_name, action_and_target);
+}
+
static gboolean
gtk_action_muxer_query_action (GActionGroup *action_group,
const gchar *action_name,
@@ -498,6 +534,8 @@ gtk_action_muxer_finalize (GObject *object)
g_assert_cmpint (g_hash_table_size (muxer->observed_actions), ==, 0);
g_hash_table_unref (muxer->observed_actions);
g_hash_table_unref (muxer->groups);
+ if (muxer->primary_accels)
+ g_hash_table_unref (muxer->primary_accels);
G_OBJECT_CLASS (gtk_action_muxer_parent_class)
->finalize (object);
@@ -514,6 +552,7 @@ gtk_action_muxer_dispose (GObject *object)
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_primary_accel_changed, muxer);
g_clear_object (&muxer->parent);
}
@@ -593,6 +632,9 @@ gtk_action_muxer_class_init (GObjectClass *class)
class->finalize = gtk_action_muxer_finalize;
class->dispose = gtk_action_muxer_dispose;
+ accel_signal = g_signal_new ("primary-accel-changed", GTK_TYPE_ACTION_MUXER, G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent",
"The parent muxer",
GTK_TYPE_ACTION_MUXER,
@@ -602,7 +644,7 @@ gtk_action_muxer_class_init (GObjectClass *class)
g_object_class_install_properties (class, NUM_PROPERTIES, properties);
}
-/**
+/*< private >
* gtk_action_muxer_insert:
* @muxer: a #GtkActionMuxer
* @prefix: the prefix string for the action group
@@ -610,15 +652,15 @@ gtk_action_muxer_class_init (GObjectClass *class)
*
* Adds the actions in @action_group to the list of actions provided by
* @muxer. @prefix is prefixed to each action name, such that for each
- * action <varname>x</varname> in @action_group, there is an equivalent
- * action @prefix<literal>.</literal><varname>x</varname> in @muxer.
+ * action `x` in @action_group, there is an equivalent
+ * action @prefix`.x` in @muxer.
*
- * For example, if @prefix is "<literal>app</literal>" and @action_group
- * contains an action called "<literal>quit</literal>", then @muxer will
- * now contain an action called "<literal>app.quit</literal>".
+ * For example, if @prefix is “`app`” and @action_group
+ * contains an action called “`quit`”, then @muxer will
+ * now contain an action called “`app.quit`”.
*
* If any #GtkActionObservers are registered for actions in the group,
- * "action_added" notifications will be emitted, as appropriate.
+ * “action_added” notifications will be emitted, as appropriate.
*
* @prefix must not contain a dot ('.').
*/
@@ -656,7 +698,7 @@ gtk_action_muxer_insert (GtkActionMuxer *muxer,
G_CALLBACK (gtk_action_muxer_group_action_state_changed), group);
}
-/**
+/*< private >
* gtk_action_muxer_remove:
* @muxer: a #GtkActionMuxer
* @prefix: the prefix of the action group to remove
@@ -664,7 +706,7 @@ gtk_action_muxer_insert (GtkActionMuxer *muxer,
* Removes a #GActionGroup from the #GtkActionMuxer.
*
* If any #GtkActionObservers are registered for actions in the group,
- * "action_removed" notifications will be emitted, as appropriate.
+ * “action_removed” notifications will be emitted, as appropriate.
*/
void
gtk_action_muxer_remove (GtkActionMuxer *muxer,
@@ -690,7 +732,27 @@ gtk_action_muxer_remove (GtkActionMuxer *muxer,
}
}
-/**
+const gchar **
+gtk_action_muxer_list_prefixes (GtkActionMuxer *muxer)
+{
+ return (const gchar **) g_hash_table_get_keys_as_array (muxer->groups, NULL);
+}
+
+GActionGroup *
+gtk_action_muxer_lookup (GtkActionMuxer *muxer,
+ const gchar *prefix)
+{
+ Group *group;
+
+ group = g_hash_table_lookup (muxer->groups, prefix);
+
+ if (group != NULL)
+ return group->group;
+
+ return NULL;
+}
+
+/*< private >
* gtk_action_muxer_new:
*
* Creates a new #GtkActionMuxer.
@@ -701,7 +763,7 @@ gtk_action_muxer_new (void)
return g_object_new (GTK_TYPE_ACTION_MUXER, NULL);
}
-/**
+/*< private >
* gtk_action_muxer_get_parent:
* @muxer: a #GtkActionMuxer
*
@@ -715,7 +777,27 @@ gtk_action_muxer_get_parent (GtkActionMuxer *muxer)
return muxer->parent;
}
-/**
+static void
+emit_changed_accels (GtkActionMuxer *muxer,
+ GtkActionMuxer *parent)
+{
+ while (parent)
+ {
+ if (parent->primary_accels)
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, parent->primary_accels);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ gtk_action_muxer_primary_accel_changed (muxer, NULL, key);
+ }
+
+ parent = parent->parent;
+ }
+}
+
+/*< private >
* gtk_action_muxer_set_parent:
* @muxer: a #GtkActionMuxer
* @parent: (allow-none): the new parent #GtkActionMuxer
@@ -742,10 +824,13 @@ gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
gtk_action_muxer_action_removed (muxer, *it);
g_strfreev (actions);
+ emit_changed_accels (muxer, muxer->parent);
+
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer);
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer);
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer);
g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer);
+ g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_primary_accel_changed, muxer);
g_object_unref (muxer->parent);
}
@@ -764,6 +849,8 @@ gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
gtk_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it);
g_strfreev (actions);
+ emit_changed_accels (muxer, muxer->parent);
+
g_signal_connect (muxer->parent, "action-added",
G_CALLBACK (gtk_action_muxer_action_added_to_parent), muxer);
g_signal_connect (muxer->parent, "action-removed",
@@ -772,7 +859,93 @@ gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
G_CALLBACK (gtk_action_muxer_parent_action_enabled_changed), muxer);
g_signal_connect (muxer->parent, "action-state-changed",
G_CALLBACK (gtk_action_muxer_parent_action_state_changed), muxer);
+ g_signal_connect (muxer->parent, "primary-accel-changed",
+ G_CALLBACK (gtk_action_muxer_parent_primary_accel_changed), muxer);
}
g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]);
}
+
+void
+gtk_action_muxer_set_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target,
+ const gchar *primary_accel)
+{
+ if (!muxer->primary_accels)
+ muxer->primary_accels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ if (primary_accel)
+ g_hash_table_insert (muxer->primary_accels, g_strdup (action_and_target), g_strdup (primary_accel));
+ else
+ g_hash_table_remove (muxer->primary_accels, action_and_target);
+
+ gtk_action_muxer_primary_accel_changed (muxer, NULL, action_and_target);
+}
+
+const gchar *
+gtk_action_muxer_get_primary_accel (GtkActionMuxer *muxer,
+ const gchar *action_and_target)
+{
+ if (muxer->primary_accels)
+ {
+ const gchar *primary_accel;
+
+ primary_accel = g_hash_table_lookup (muxer->primary_accels, action_and_target);
+
+ if (primary_accel)
+ return primary_accel;
+ }
+
+ if (!muxer->parent)
+ return NULL;
+
+ return gtk_action_muxer_get_primary_accel (muxer->parent, action_and_target);
+}
+
+gchar *
+gtk_print_action_and_target (const gchar *action_namespace,
+ const gchar *action_name,
+ GVariant *target)
+{
+ GString *result;
+
+ g_return_val_if_fail (strchr (action_name, '|') == NULL, NULL);
+ g_return_val_if_fail (action_namespace == NULL || strchr (action_namespace, '|') == NULL, NULL);
+
+ result = g_string_new (NULL);
+
+ if (target)
+ g_variant_print_string (target, result, TRUE);
+ g_string_append_c (result, '|');
+
+ if (action_namespace)
+ {
+ g_string_append (result, action_namespace);
+ g_string_append_c (result, '.');
+ }
+
+ g_string_append (result, action_name);
+
+ return g_string_free (result, FALSE);
+}
+
+gchar *
+gtk_normalise_detailed_action_name (const gchar *detailed_action_name)
+{
+ GError *error = NULL;
+ gchar *action_and_target;
+ gchar *action_name;
+ GVariant *target;
+
+ g_action_parse_detailed_name (detailed_action_name, &action_name, &target, &error);
+ g_assert_no_error (error);
+
+ action_and_target = gtk_print_action_and_target (NULL, action_name, target);
+
+ if (target)
+ g_variant_unref (target);
+
+ g_free (action_name);
+
+ return action_and_target;
+}