[gnome-builder] egg: add EggWidgetActionGroup
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] egg: add EggWidgetActionGroup
- Date: Mon, 21 Dec 2015 07:57:39 +0000 (UTC)
commit 576b87300bb4b8030fdb794b9cda0dd2ed245463
Author: Christian Hergert <chergert redhat com>
Date: Thu Dec 3 21:07:45 2015 -0800
egg: add EggWidgetActionGroup
This is an action group that contains actions for each activatable
G_SIGNAL_ACTION for which the parameters can be encoded in a GVariant
action target.
It simplifies the process of activating signal actions from a menu
using GAction items.
contrib/egg/Makefile.am | 2 +
contrib/egg/egg-widget-action-group.c | 368 +++++++++++++++++++++++++++++++++
contrib/egg/egg-widget-action-group.h | 32 +++
3 files changed, 402 insertions(+), 0 deletions(-)
---
diff --git a/contrib/egg/Makefile.am b/contrib/egg/Makefile.am
index 24470ec..49daac5 100644
--- a/contrib/egg/Makefile.am
+++ b/contrib/egg/Makefile.am
@@ -50,6 +50,8 @@ libegg_private_la_SOURCES = \
egg-state-machine-buildable.h \
egg-task-cache.c \
egg-task-cache.h \
+ egg-widget-action-group.c \
+ egg-widget-action-group.h \
$(NULL)
nodist_libegg_private_la_SOURCES = \
diff --git a/contrib/egg/egg-widget-action-group.c b/contrib/egg/egg-widget-action-group.c
new file mode 100644
index 0000000..a49bafe
--- /dev/null
+++ b/contrib/egg/egg-widget-action-group.c
@@ -0,0 +1,368 @@
+/* egg-widget-action-group.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "egg-widget-action-group.h"
+
+static gboolean
+supports_types (const GType *types,
+ guint n_types)
+{
+ guint i;
+
+ g_assert (types != NULL || n_types == 0);
+
+ for (i = 0; i < n_types; i++)
+ {
+ switch (types [i])
+ {
+ case G_TYPE_STRING:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_INT64:
+ case G_TYPE_UINT64:
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_DOUBLE:
+ case G_TYPE_FLOAT:
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_ENUM:
+ case G_TYPE_FLAGS:
+ case G_TYPE_VARIANT:
+ case G_TYPE_NONE:
+ break;
+
+ default:
+ if (G_TYPE_IS_FLAGS (types [i]) || G_TYPE_IS_ENUM (types [i]))
+ break;
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static GVariantType *
+create_variant_type (const GType *types,
+ guint n_types)
+{
+ GString *str;
+ gint i;
+
+ g_assert (types != NULL || n_types == 0);
+
+ str = g_string_new ("(");
+
+ for (i = 0; i < n_types; i++)
+ {
+ switch (types [i])
+ {
+ case G_TYPE_STRING:
+ g_string_append_c (str, 's');
+ break;
+
+ case G_TYPE_INT:
+ g_string_append_c (str, 'i');
+ break;
+
+ case G_TYPE_UINT:
+ g_string_append_c (str, 'u');
+ break;
+
+ case G_TYPE_INT64:
+ g_string_append_c (str, 'x');
+ break;
+
+ case G_TYPE_UINT64:
+ g_string_append_c (str, 't');
+ break;
+
+ case G_TYPE_BOOLEAN:
+ g_string_append_c (str, 'b');
+ break;
+
+ case G_TYPE_DOUBLE:
+ case G_TYPE_FLOAT:
+ g_string_append_c (str, 'd');
+ break;
+
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ g_string_append_c (str, 'y');
+ break;
+
+ case G_TYPE_VARIANT:
+ g_string_append_c (str, 'v');
+ break;
+
+ case G_TYPE_NONE:
+ break;
+
+ default:
+ if (G_TYPE_IS_ENUM (types [i]) || G_TYPE_IS_FLAGS (types [i]))
+ {
+ g_string_append_c (str, 'u');
+ break;
+ }
+
+ return FALSE;
+ }
+ }
+
+ g_string_append_c (str, ')');
+
+ return (GVariantType *)g_string_free (str, (str->len == 2));
+}
+
+static void
+egg_widget_action_group_activate (GSimpleAction *action,
+ GVariant *params,
+ GtkWidget *widget)
+{
+ const GSignalQuery *query;
+ g_auto(GValue) return_value = G_VALUE_INIT;
+ g_auto(GValue) instance = G_VALUE_INIT;
+ GArray *ar;
+ GVariantIter iter;
+ gsize n_children;
+ gint i;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (GTK_IS_WIDGET (widget));
+
+ query = g_object_get_data (G_OBJECT (action), "EGG_SIGNAL_INFO");
+
+ if (query == NULL)
+ {
+ g_critical ("EGG_SIGNAL_INFO is missing, cannot emit signal.");
+ return;
+ }
+
+ if (params)
+ g_print ("Activating %s with %s\n", query->signal_name, g_variant_print (params, TRUE));
+
+ if (params == NULL && query->n_params != 0)
+ {
+ g_critical ("%s::%s() requires %d parameters",
+ G_OBJECT_TYPE_NAME (widget), query->signal_name, query->n_params);
+ return;
+ }
+
+ if (query->return_type != G_TYPE_NONE)
+ g_value_init (&return_value, query->return_type);
+
+ g_value_init (&instance, query->itype);
+ g_value_set_object (&instance, widget);
+
+ if (params == NULL)
+ {
+ g_signal_emitv (&instance, query->signal_id, 0, &return_value);
+ return;
+ }
+
+ g_assert (g_variant_is_container (params));
+ g_assert (params != NULL);
+
+ n_children = g_variant_iter_init (&iter, params);
+
+ if (n_children != query->n_params)
+ {
+ g_critical ("%s::%s() requires %d params, got %d",
+ G_OBJECT_TYPE_NAME (widget), query->signal_name,
+ (gint)n_children, query->n_params);
+ return;
+ }
+
+ ar = g_array_new (FALSE, FALSE, sizeof (GValue));
+
+ g_array_append_val (ar, instance);
+
+ g_variant_iter_init (&iter, params);
+
+ for (i = 0; i < query->n_params; i++)
+ {
+ g_autoptr(GVariant) param = NULL;
+ GValue value = G_VALUE_INIT;
+
+ param = g_variant_iter_next_value (&iter);
+
+#define CONVERT_PARAM(TYPE, VARIANT_TYPE, setter, getter, ...) \
+ case G_TYPE_##TYPE: \
+ { \
+ if (!g_variant_is_of_type (param, G_VARIANT_TYPE_##VARIANT_TYPE)) \
+ { \
+ g_critical ("parameter type mismatch for signal %s", \
+ query->signal_name); \
+ goto skip_emit; \
+ } \
+ g_value_init (&value, G_TYPE_##TYPE); \
+ g_value_set_##setter (&value, g_variant_get_##getter (param, ##__VA_ARGS__)); \
+ g_array_append_val (ar, value); \
+ } \
+ break
+
+ switch (query->param_types [i])
+ {
+ CONVERT_PARAM(STRING, STRING, string, string, NULL);
+ CONVERT_PARAM(INT, INT32, int, int32);
+ CONVERT_PARAM(UINT, UINT32, uint, uint32);
+ CONVERT_PARAM(INT64, INT64, int64, int64);
+ CONVERT_PARAM(UINT64, UINT64, uint64, uint64);
+ CONVERT_PARAM(BOOLEAN, BOOLEAN, boolean, boolean);
+ CONVERT_PARAM(DOUBLE, DOUBLE, double, double);
+ CONVERT_PARAM(FLOAT, DOUBLE, float, double);
+ CONVERT_PARAM(CHAR, BYTE, schar, byte);
+ CONVERT_PARAM(UCHAR, BYTE, uchar, byte);
+ CONVERT_PARAM(VARIANT, VARIANT, variant, variant);
+
+ default:
+ if (G_TYPE_IS_ENUM(query->param_types [i]))
+ {
+ if (!g_variant_is_of_type (param, G_VARIANT_TYPE_UINT32))
+ goto skip_emit;
+ g_value_init (&value, query->param_types [i]);
+ g_value_set_enum (&value, g_variant_get_uint32 (param));
+ g_array_append_val (ar, value);
+ break;
+ }
+ else if (G_TYPE_IS_FLAGS (query->param_types [i]))
+ {
+ if (!g_variant_is_of_type (param, G_VARIANT_TYPE_UINT32))
+ goto skip_emit;
+ g_value_init (&value, query->param_types [i]);
+ g_value_set_flags (&value, g_variant_get_uint32 (param));
+ g_array_append_val (ar, value);
+ break;
+ }
+
+ g_critical ("Unknown param type: %s", g_type_name (query->param_types [i]));
+ goto skip_emit;
+ }
+
+#undef CONVERT_PARAM
+ }
+
+ g_signal_emitv ((GValue *)ar->data, query->signal_id, 0, &return_value);
+
+skip_emit:
+ /* ignore instance */
+ for (i = 1; i < ar->len; i++)
+ g_value_unset (&g_array_index (ar, GValue, i));
+
+ g_array_unref (ar);
+}
+
+static void
+query_free (gpointer data)
+{
+ g_slice_free (GSignalQuery, data);
+}
+
+GAction *
+create_action (const GSignalQuery *query,
+ GtkWidget *widget)
+{
+ GSimpleAction *action;
+ GVariantType *param_type;
+ GSignalQuery *query_copy;
+
+ g_assert (query != NULL);
+ g_assert (query->signal_id != 0);
+ g_assert (GTK_IS_WIDGET (widget));
+
+ param_type = create_variant_type (query->param_types, query->n_params);
+ action = g_simple_action_new (query->signal_name, param_type);
+
+ /* Save signal info for marshalling upon callback */
+ query_copy = g_slice_new0 (GSignalQuery);
+ memcpy (query_copy, query, sizeof *query_copy);
+ g_object_set_data_full (G_OBJECT (action), "EGG_SIGNAL_INFO", query_copy, query_free);
+
+ /* connect our marshaller to the action */
+ g_signal_connect_object (action,
+ "activate",
+ G_CALLBACK (egg_widget_action_group_activate),
+ widget,
+ 0);
+
+ g_free (param_type);
+
+ return G_ACTION (action);
+}
+
+GActionGroup *
+egg_widget_action_group_new (GtkWidget *widget)
+{
+ GSimpleActionGroup *self;
+ GType type;
+ guint *signals;
+ guint n_signals = 0;
+ guint i;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ self = g_simple_action_group_new ();
+
+ for (type = G_OBJECT_TYPE (widget);
+ type != G_TYPE_INITIALLY_UNOWNED;
+ type = g_type_parent (type))
+ {
+ signals = g_signal_list_ids (type, &n_signals);
+
+ for (i = 0; i < n_signals; i++)
+ {
+ GSignalQuery query;
+ GAction *action;
+
+ g_signal_query (signals [i], &query);
+
+ if ((query.signal_flags & G_SIGNAL_ACTION) == 0)
+ continue;
+
+ if (!supports_types (&query.return_type, 1))
+ continue;
+
+ if (!supports_types (query.param_types, query.n_params))
+ continue;
+
+ action = create_action (&query, widget);
+ g_action_map_add_action (G_ACTION_MAP (self), action);
+ g_object_unref (action);
+ }
+
+ g_free (signals);
+ }
+
+ return G_ACTION_GROUP (self);
+}
+
+void
+egg_widget_action_group_attach (gpointer instance,
+ const gchar *name)
+{
+ GActionGroup *group;
+
+ g_return_if_fail (GTK_IS_WIDGET (instance));
+ g_return_if_fail (name != NULL);
+
+ group = egg_widget_action_group_new (instance);
+ gtk_widget_insert_action_group (instance, name, group);
+ g_object_unref (group);
+}
diff --git a/contrib/egg/egg-widget-action-group.h b/contrib/egg/egg-widget-action-group.h
new file mode 100644
index 0000000..b4cdb15
--- /dev/null
+++ b/contrib/egg/egg-widget-action-group.h
@@ -0,0 +1,32 @@
+/* egg-widget-action-group.h
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_WIDGET_ACTION_GROUP_H
+#define EGG_WIDGET_ACTION_GROUP_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GActionGroup *egg_widget_action_group_new (GtkWidget *widget);
+void egg_widget_action_group_attach (gpointer instance,
+ const gchar *source_view);
+
+G_END_DECLS
+
+#endif /* EGG_WIDGET_ACTION_GROUP_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]