[glib/gdbus-actions: 3/3] add GDBusActionGroup



commit bebf94f0ff5499fa6d40d3a1cd08b966285c43e8
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Sep 2 03:49:13 2010 +0200

    add GDBusActionGroup
    
    proxy for remote GActionGroup exported via GDBusConnection

 gio/Makefile.am        |    2 +
 gio/gdbusactiongroup.c |  484 ++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gdbusactiongroup.h |   75 ++++++++
 gio/gio.h              |    1 +
 gio/gio.symbols        |    8 +
 gio/gioenums.h         |    7 +
 gio/giotypes.h         |    1 +
 7 files changed, 578 insertions(+), 0 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 78676dd..6ca0ad6 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -69,6 +69,7 @@ gio-marshal.c: gio-marshal.h gio-marshal.list
 	  mv $  tmp $@
 
 gdbus_headers = 			\
+	gdbusactiongroup.h		\
 	gdbusauthobserver.h		\
 	gcredentials.h			\
 	gdbusutils.h			\
@@ -103,6 +104,7 @@ gdbus_sources = 							\
 	gdbusintrospection.h		gdbusintrospection.c		\
 	gdbusmethodinvocation.h		gdbusmethodinvocation.c		\
 	gdbusserver.h			gdbusserver.c			\
+	gdbusactiongroup.h		gdbusactiongroup.c		\
 	$(NULL)
 
 settings_headers = \
diff --git a/gio/gdbusactiongroup.c b/gio/gdbusactiongroup.c
new file mode 100644
index 0000000..34cbc00
--- /dev/null
+++ b/gio/gdbusactiongroup.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * This library 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 Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gdbusactiongroup.h"
+#include "gdbusconnection.h"
+#include "gcontextual.h"
+
+#include <string.h>
+
+/**
+ * SECTION:gdbusactiongroup
+ * @title: GDBusActionGroup
+ * @short_description: present a set of DBus actions as a #GActionGroup
+ *
+ * #GDBusActionGroup is a proxy for a remote set of actions on DBus.
+ **/
+
+typedef GObjectClass GDBusActionGroupClass;
+
+struct _GDBusActionGroup
+{
+  GObject parent_instance;
+
+  GDBusActionGroupFlags  flags;
+  GDBusConnection       *connection;
+  gchar                 *bus_name;
+  gchar                 *object_path;
+  GHashTable            *table;
+  GSList                *context;
+  gboolean               emit_signals;
+  guint                  signal_id;
+};
+
+static void g_dbus_action_group_init_action_iface (GActionGroupInterface *);
+static void g_dbus_action_group_init_context_iface (GContextualInterface *);
+G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup,
+  g_dbus_action_group, G_TYPE_OBJECT,
+  G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
+    g_dbus_action_group_init_action_iface)
+  G_IMPLEMENT_INTERFACE (G_TYPE_CONTEXTUAL,
+    g_dbus_action_group_init_context_iface))
+
+typedef struct
+{
+  GDBusActionGroup *group;
+
+  gchar            *name;
+  GVariantType     *parameter_type;
+  gboolean          enabled;
+  GVariant         *state;
+} GDBusAction;
+
+static void
+g_dbus_action_free (gpointer data)
+{
+  GDBusAction *action = data;
+
+  if (action->group->emit_signals)
+    g_action_group_action_removed (G_ACTION_GROUP (action->group),
+                                   action->name);
+
+  g_free (action->name);
+
+  if (action->parameter_type)
+    g_variant_type_free (action->parameter_type);
+
+  if (action->state)
+    g_variant_unref (action->state);
+
+  g_slice_free (GDBusAction, action);
+}
+
+static gchar **
+g_dbus_action_group_list_actions (GActionGroup *group)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GHashTableIter iter;
+  gint n, i = 0;
+  gchar **keys;
+  gpointer key;
+
+  n = g_hash_table_size (dbus->table);
+  keys = g_new (gchar *, n + 1);
+
+  g_hash_table_iter_init (&iter, dbus->table);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    keys[i++] = g_strdup (key);
+  g_assert_cmpint (i, ==, n);
+  keys[n] = NULL;
+
+  return keys;
+}
+
+static gboolean
+g_dbus_action_group_has_action (GActionGroup *group,
+                                const gchar  *action_name)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+
+  return g_hash_table_lookup (dbus->table, action_name) != NULL;
+}
+
+static const GVariantType *
+g_dbus_action_group_get_parameter_type (GActionGroup *group,
+                                        const gchar  *action_name)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GDBusAction *action;
+
+  action = g_hash_table_lookup (dbus->table, action_name);
+
+  if (action == NULL)
+    return NULL;
+
+  return action->parameter_type;
+}
+
+static const GVariantType *
+g_dbus_action_group_get_state_type (GActionGroup *group,
+                                      const gchar  *action_name)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GDBusAction *action;
+
+  action = g_hash_table_lookup (dbus->table, action_name);
+
+  if (action == NULL)
+    return NULL;
+
+  return action->state ? g_variant_get_type (action->state) : NULL;
+}
+
+static GVariant *
+g_dbus_action_group_get_state_hint (GActionGroup *group,
+                                    const gchar  *action_name)
+{
+  /* XXX tricky... */
+  g_assert_not_reached ();
+}
+
+static gboolean
+g_dbus_action_group_get_enabled (GActionGroup *group,
+                                 const gchar  *action_name)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GDBusAction *action;
+
+  action = g_hash_table_lookup (dbus->table, action_name);
+
+  if (action == NULL)
+    return FALSE;
+
+  return action->enabled;
+}
+
+static GVariant *
+g_dbus_action_group_get_state (GActionGroup *group,
+                               const gchar  *action_name)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GDBusAction *action;
+
+  action = g_hash_table_lookup (dbus->table, action_name);
+
+  if (action == NULL)
+    return NULL;
+
+  return action->state ? g_variant_ref (action->state) : NULL;
+}
+
+static void
+g_dbus_action_group_set_state (GActionGroup *group,
+                               const gchar  *action_name,
+                               GVariant     *value)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+
+  g_dbus_connection_call (dbus->connection, dbus->bus_name, dbus->object_path,
+                          "org.gtk.Actions", "SetState",
+                          g_variant_new ("(sv a{sv})", action_name, value,
+                                         dbus->context->data), NULL,
+                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+g_dbus_action_group_activate (GActionGroup *group,
+                              const gchar  *action_name,
+                              GVariant     *parameter)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (group);
+  GVariant *param;
+
+  if (parameter)
+    parameter = g_variant_new_variant (parameter);
+
+  param = g_variant_new_array (G_VARIANT_TYPE_VARIANT,
+                               &parameter, parameter != NULL);
+
+  g_dbus_connection_call (dbus->connection, dbus->bus_name, dbus->object_path,
+                          "org.gtk.Actions", "Invoke",
+                          g_variant_new ("(s av@a{sv})", action_name, param,
+                                         dbus->context->data), NULL,
+                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+g_dbus_action_group_push_context (GContextual *contextual,
+                                  GVariant    *context)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (contextual);
+
+  if (~dbus->flags & G_DBUS_ACTION_GROUP_FLAGS_IGNORE_CONTEXT)
+    dbus->context = g_slist_prepend (dbus->context,
+                                     g_variant_ref_sink (context));
+}
+
+static void
+g_dbus_action_group_pop_context (GContextual *contextual,
+                                 GVariant    *context)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (contextual);
+
+  if (~dbus->flags & G_DBUS_ACTION_GROUP_FLAGS_IGNORE_CONTEXT)
+    {
+      g_return_if_fail (dbus->context && dbus->context->data == context);
+      dbus->context = g_slist_delete_link (dbus->context, dbus->context);
+      g_variant_unref (context);
+    }
+}
+
+static void
+g_dbus_action_group_finalize (GObject *object)
+{
+  GDBusActionGroup *dbus = G_DBUS_ACTION_GROUP (object);
+
+  dbus->emit_signals = FALSE;
+
+  if (dbus->signal_id)
+    g_dbus_connection_signal_unsubscribe (dbus->connection,
+                                          dbus->signal_id);
+
+  g_variant_unref (dbus->context->data);
+  dbus->context = g_slist_delete_link (dbus->context, dbus->context);
+  g_warn_if_fail (dbus->context == NULL);
+
+  g_object_unref (dbus->connection);
+  g_hash_table_unref (dbus->table);
+
+  G_OBJECT_CLASS (g_dbus_action_group_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_dbus_action_group_init (GDBusActionGroup *dbus)
+{
+  dbus->table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                       NULL, g_dbus_action_free);
+  dbus->context = g_slist_prepend (NULL,
+                                   g_variant_ref_sink (
+                                     g_variant_new_array (
+                                       G_VARIANT_TYPE ("{sv}"), NULL, 0)));
+}
+
+static void
+g_dbus_action_group_class_init (GDBusActionGroupClass *class)
+{
+  class->finalize = g_dbus_action_group_finalize;
+}
+
+static void
+g_dbus_action_group_init_action_iface (GActionGroupInterface *iface)
+{
+  iface->list_actions = g_dbus_action_group_list_actions;
+  iface->has_action = g_dbus_action_group_has_action;
+  iface->get_parameter_type = g_dbus_action_group_get_parameter_type;
+  iface->get_state_type = g_dbus_action_group_get_state_type;
+  iface->get_state_hint = g_dbus_action_group_get_state_hint;
+  iface->get_enabled = g_dbus_action_group_get_enabled;
+  iface->get_state = g_dbus_action_group_get_state;
+  iface->set_state = g_dbus_action_group_set_state;
+  iface->activate = g_dbus_action_group_activate;
+}
+
+static void
+g_dbus_action_group_init_context_iface (GContextualInterface *iface)
+{
+  iface->push_context = g_dbus_action_group_push_context;
+  iface->pop_context = g_dbus_action_group_pop_context;
+}
+
+static void
+g_dbus_action_group_populate (GDBusActionGroup *dbus,
+                              GVariant         *args)
+{
+  GVariant *descriptions;
+  GVariantIter iter;
+  GVariant *param_type;
+  gboolean enabled;
+  GVariant *state;
+  gchar *name;
+
+  descriptions = g_variant_get_child_value (args, 0);
+  g_variant_iter_init (&iter, descriptions);
+
+  while (g_variant_iter_next (&iter, "(s avb@av)", &name,
+                              &param_type, &enabled, &state))
+    {
+      GDBusAction *action;
+
+      action = g_slice_new (GDBusAction);
+      action->name = name;
+      action->parameter_type = g_variant_type_copy (
+                                 g_variant_type_element (
+                                   g_variant_get_type (param_type)));
+      action->enabled = enabled;
+      action->state = state;
+
+      g_hash_table_replace (dbus->table, action->name, action);
+      g_variant_unref (param_type);
+
+      if (dbus->emit_signals)
+        g_action_group_action_added (G_ACTION_GROUP (dbus), name);
+
+    }
+
+  g_variant_unref (descriptions);
+}
+
+static void
+g_dbus_action_group_signal (GDBusConnection *connection,
+                            const gchar     *sender_name,
+                            const gchar     *object_path,
+                            const gchar     *interface_name,
+                            const gchar     *signal_name,
+                            GVariant        *parameters,
+                            gpointer         user_data)
+{
+  GDBusActionGroup *dbus = user_data;
+
+  if (strcmp (signal_name, "Added") == 0)
+    {
+      g_dbus_action_group_populate (dbus, parameters);
+    }
+
+  else if (strcmp (signal_name, "Removed") == 0)
+    {
+      GVariantIter *iter;
+      const gchar *name;
+
+      g_variant_get (parameters, "(as)", &iter);
+      while (g_variant_iter_next (iter, "&s", &name))
+        g_hash_table_remove (dbus->table, name);
+      g_variant_iter_free (iter);
+    }
+
+  else if (strcmp (signal_name, "EnabledChanged") == 0)
+    {
+      GDBusAction *action;
+      const gchar *name;
+      gboolean enabled;
+
+      g_variant_get (parameters, "(&sb)", &name, &enabled);
+      action = g_hash_table_lookup (dbus->table, name);
+
+      if (action != NULL && action->enabled != enabled)
+        {
+          action->enabled = enabled;
+          g_action_group_action_enabled_changed (G_ACTION_GROUP (dbus),
+                                                 name, action->enabled);
+        }
+    }
+
+  else if (strcmp (signal_name, "StateChanged") == 0)
+    {
+      GDBusAction *action;
+      const gchar *name;
+      GVariant *state;
+
+      g_variant_get (parameters, "(&sv)", &name, &state);
+
+      action = g_hash_table_lookup (dbus->table, name);
+
+      if (action != NULL && action->state &&
+          !g_variant_equal (action->state, state) &&
+          g_variant_is_of_type (state, g_variant_get_type (action->state)))
+        {
+          g_variant_unref (action->state);
+          action->state = g_variant_ref (state);
+          g_action_group_action_state_changed (G_ACTION_GROUP (dbus),
+                                               name, action->state);
+        }
+
+      g_variant_unref (state);
+    }
+}
+
+GDBusActionGroup *
+g_dbus_action_group_new_sync (GDBusConnection        *connection,
+                              GDBusActionGroupFlags   flags,
+                              const gchar            *name,
+                              const gchar            *object_path,
+                              GCancellable           *cancellable,
+                              GError                **error)
+{
+  GDBusActionGroup *dbus;
+
+  dbus = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
+  dbus->connection = g_object_ref (connection);
+  dbus->bus_name = g_strdup (name);
+  dbus->object_path = g_strdup (object_path);
+  dbus->flags = flags;
+
+  if (~flags & G_DBUS_ACTION_GROUP_FLAGS_NO_QUERY)
+    {
+      GVariant *reply;
+
+      dbus->signal_id =
+        g_dbus_connection_signal_subscribe (connection,
+                                            name, "org.gtk.Actions",
+                                            NULL, object_path, NULL,
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
+                                            g_dbus_action_group_signal,
+                                            dbus, NULL);
+
+      reply = g_dbus_connection_call_sync (connection, name, object_path,
+                                           "org.gtk.Actions", "DescribeAll",
+                                           NULL,
+                                           G_VARIANT_TYPE ("(a(savbav))"),
+                                           G_DBUS_CALL_FLAGS_NONE, -1,
+                                           cancellable, error);
+
+      if (reply == NULL)
+        {
+          g_object_unref (dbus);
+          return NULL;
+        }
+
+      g_dbus_action_group_populate (dbus, reply);
+      g_variant_unref (reply);
+
+      dbus->emit_signals = TRUE;
+    }
+
+  return dbus;
+}
+
+void
+g_dbus_action_group_new (GDBusConnection        *connection,
+                         GDBusActionGroupFlags   flags,
+                         const gchar            *name,
+                         const gchar            *object_path,
+                         GCancellable           *cancellable,
+                         GAsyncReadyCallback     callback,
+                         gpointer                user_data)
+{
+  g_assert_not_reached ();
+}
+
+GDBusActionGroup *
+g_dbus_action_group_new_finish (GAsyncResult  *result,
+                                GError       **error)
+{
+  g_assert_not_reached ();
+}
diff --git a/gio/gdbusactiongroup.h b/gio/gdbusactiongroup.h
new file mode 100644
index 0000000..bb3b285
--- /dev/null
+++ b/gio/gdbusactiongroup.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This program 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 licence or (at
+ * your option) any later version.
+ *
+ * This library 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 Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_ACTION_GROUP_H__
+#define __G_DBUS_ACTION_GROUP_H__
+
+#include "gactiongroup.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_ACTION_GROUP                            (g_dbus_action_group_get_type ())
+#define G_DBUS_ACTION_GROUP(inst)                           (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroup))
+#define G_DBUS_ACTION_GROUP_CLASS(class)                    (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
+#define G_IS_DBUS_ACTION_GROUP(inst)                        (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_DBUS_ACTION_GROUP))
+#define G_IS_DBUS_ACTION_GROUP_CLASS(class)                 (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_DBUS_ACTION_GROUP))
+#define G_DBUS_ACTION_GROUP_GET_CLASS(inst)                 (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
+
+/**
+ * GDBusActionGroup:
+ *
+ * The #GDBusActionGroup structure contains private data and should only be accessed using the provided API.
+ *
+ * Since: 2.26
+ */
+
+GType                   g_dbus_action_group_get_type                  (void) G_GNUC_CONST;
+
+void                    g_dbus_action_group_new                       (GDBusConnection        *connection,
+                                                                       GDBusActionGroupFlags   flags,
+                                                                       const gchar            *name,
+                                                                       const gchar            *object_path,
+                                                                       GCancellable           *cancellable,
+                                                                       GAsyncReadyCallback     callback,
+                                                                       gpointer                user_data);
+
+GDBusActionGroup *      g_dbus_action_group_new_finish                (GAsyncResult           *result,
+                                                                       GError                **error);
+
+GDBusActionGroup *      g_dbus_action_group_new_sync                  (GDBusConnection        *connection,
+                                                                       GDBusActionGroupFlags   flags,
+                                                                       const gchar            *name,
+                                                                       const gchar            *object_path,
+                                                                       GCancellable           *cancellable,
+                                                                       GError                **error);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_ACTION_GROUP_H__ */
diff --git a/gio/gio.h b/gio/gio.h
index b21bba4..1f75cd5 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -47,6 +47,7 @@
 #include <gio/gcredentials.h>
 #include <gio/gdatainputstream.h>
 #include <gio/gdataoutputstream.h>
+#include <gio/gdbusactiongroup.h>
 #include <gio/gdbusaddress.h>
 #include <gio/gdbusauthobserver.h>
 #include <gio/gdbusconnection.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 13a2ce4..7ddb132 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1041,6 +1041,7 @@ g_dbus_send_message_flags_get_type G_GNUC_CONST
 g_credentials_type_get_type G_GNUC_CONST
 g_dbus_message_byte_order_get_type G_GNUC_CONST
 g_dbus_message_filter_result_get_type G_GNUC_CONST
+g_dbus_action_group_flags_get_type G_GNUC_CONST
 #endif
 #endif
 
@@ -1935,3 +1936,10 @@ g_contextual_push_context
 g_contextual_pop_context
 #endif
 #endif
+
+#if IN_HEADER(__G_DBUS_ACTION_GROUP_H__)
+#if IN_FILE(__G_DBUS_ACTION_GROUP_H__)
+g_dbus_action_group_get_type
+g_dbus_action_group_new_sync
+#endif
+#endif
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 482e7f2..a806173 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1239,6 +1239,13 @@ typedef enum
   G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED
 } GDBusMessageFilterResult;
 
+typedef enum
+{
+  G_DBUS_ACTION_GROUP_FLAGS_NONE,
+  G_DBUS_ACTION_GROUP_FLAGS_NO_QUERY       = (1 << 0),
+  G_DBUS_ACTION_GROUP_FLAGS_IGNORE_CONTEXT = (1 << 1)
+} GDBusActionGroupFlags;
+
 G_END_DECLS
 
 #endif /* __GIO_ENUMS_H__ */
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 9d084ed..d0874c0 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -358,6 +358,7 @@ struct _GOutputVector {
 typedef struct _GCredentials                  GCredentials;
 typedef struct _GUnixCredentialsMessage       GUnixCredentialsMessage;
 typedef struct _GUnixFDList                   GUnixFDList;
+typedef struct _GDBusActionGroup              GDBusActionGroup;
 typedef struct _GDBusMessage                  GDBusMessage;
 typedef struct _GDBusConnection               GDBusConnection;
 typedef struct _GDBusProxy                    GDBusProxy;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]