[glib/wip/menus: 30/61] add GMenuExporter



commit c1d573c70eb53cb81f3c7e462bcafba2c073f8d3
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Aug 25 01:31:45 2011 -0400

    add GMenuExporter

 gio/Makefile.am     |    2 +
 gio/gio.h           |    1 +
 gio/gio.symbols     |    3 +
 gio/gmenuexporter.c |  783 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gmenuexporter.h |   28 ++
 5 files changed, 817 insertions(+), 0 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 7e83fd3..2fa96a6 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -135,6 +135,7 @@ application_headers = \
 	gapplication.h			\
 	gmenumodel.h			\
 	gmenu.h				\
+	gmenuexporter.h			\
 	gmenumarkup.h
 
 application_sources = \
@@ -150,6 +151,7 @@ application_sources = \
 	gapplication.c				\
 	gmenumodel.c				\
 	gmenu.c					\
+	gmenuexporter.c				\
 	gmenumarkup.c
 
 local_sources = \
diff --git a/gio/gio.h b/gio/gio.h
index 6f91e94..a634c07 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -148,6 +148,7 @@
 #include <gio/gdbusactiongroup.h>
 #include <gio/gmenumodel.h>
 #include <gio/gmenu.h>
+#include <gio/gmenuexporter.h>
 #include <gio/gmenumarkup.h>
 
 #undef __GIO_GIO_H_INSIDE__
diff --git a/gio/gio.symbols b/gio/gio.symbols
index ed394f6..d832392 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1608,6 +1608,9 @@ g_menu_attribute_iter_get_next
 g_menu_attribute_iter_get_type
 g_menu_attribute_iter_get_value
 g_menu_attribute_iter_next
+g_menu_exporter_export
+g_menu_exporter_query
+g_menu_exporter_stop
 g_menu_freeze
 g_menu_get_type
 g_menu_insert
diff --git a/gio/gmenuexporter.c b/gio/gmenuexporter.c
new file mode 100644
index 0000000..06b147a
--- /dev/null
+++ b/gio/gmenuexporter.c
@@ -0,0 +1,783 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gmenuexporter.h"
+
+#include "gdbusmethodinvocation.h"
+#include "gdbusintrospection.h"
+#include "gdbusnamewatching.h"
+#include "gdbuserror.h"
+
+/* {{{1 D-Bus Interface description */
+static GDBusInterfaceInfo *
+org_gtk_Menus_get_interface (void)
+{
+  static GDBusInterfaceInfo *interface_info;
+
+  if (interface_info == NULL)
+    {
+      GError *error = NULL;
+      GDBusNodeInfo *info;
+
+      info = g_dbus_node_info_new_for_xml ("<node>"
+                                           "  <interface name='org.gtk.Menus'>"
+                                           "    <method name='Start'>"
+                                           "      <arg type='au' name='groups' direction='in'/>"
+                                           "      <arg type='a(uuaa{sv})' name='content' direction='out'/>"
+                                           "    </method>"
+                                           "    <method name='End'>"
+                                           "      <arg type='au' name='groups' direction='in'/>"
+                                           "    </method>"
+                                           "    <signal name='Changed'>"
+                                           "      arg type='a(uuuuaa{sv})' name='changes'/>"
+                                           "    </signal>"
+                                           "  </interface>"
+                                           "</node>", &error);
+      if (info == NULL)
+        g_error ("%s\n", error->message);
+      interface_info = g_dbus_node_info_lookup_interface (info, "org.gtk.Menus");
+      g_assert (interface_info != NULL);
+      g_dbus_interface_info_ref (interface_info);
+      g_dbus_node_info_unref (info);
+    }
+
+  return interface_info;
+}
+
+/* {{{1 Forward declarations */
+typedef struct _GMenuExporterMenu                           GMenuExporterMenu;
+typedef struct _GMenuExporterLink                           GMenuExporterLink;
+typedef struct _GMenuExporterGroup                          GMenuExporterGroup;
+typedef struct _GMenuExporterRemote                         GMenuExporterRemote;
+typedef struct _GMenuExporterWatch                          GMenuExporterWatch;
+typedef struct _GMenuExporter                               GMenuExporter;
+
+static gboolean                 g_menu_exporter_group_is_subscribed    (GMenuExporterGroup *group);
+static guint                    g_menu_exporter_group_get_id           (GMenuExporterGroup *group);
+static GMenuExporter *          g_menu_exporter_group_get_exporter     (GMenuExporterGroup *group);
+static GMenuExporterMenu *      g_menu_exporter_group_add_menu         (GMenuExporterGroup *group,
+                                                                        GMenuModel         *model);
+static void                     g_menu_exporter_group_remove_menu      (GMenuExporterGroup *group,
+                                                                        guint               id);
+
+static GMenuExporterGroup *     g_menu_exporter_create_group           (GMenuExporter      *exporter);
+static GMenuExporterGroup *     g_menu_exporter_lookup_group           (GMenuExporter      *exporter,
+                                                                        guint               group_id);
+static void                     g_menu_exporter_report                 (GMenuExporter      *exporter,
+                                                                        GVariant           *report);
+static void                     g_menu_exporter_remove_group           (GMenuExporter      *exporter,
+                                                                        guint               id);
+
+/* {{{1 GMenuExporterLink, GMenuExporterMenu */
+
+struct _GMenuExporterMenu
+{
+  GMenuExporterGroup *group;
+  guint               id;
+
+  GMenuModel *model;
+  gulong      handler_id;
+  GSequence  *item_links;
+};
+
+struct _GMenuExporterLink
+{
+  gchar             *name;
+  GMenuExporterMenu *menu;
+  GMenuExporterLink *next;
+};
+
+static void
+g_menu_exporter_menu_free (GMenuExporterMenu *menu)
+{
+  g_menu_exporter_group_remove_menu (menu->group, menu->id);
+
+  if (menu->handler_id != 0)
+    g_signal_handler_disconnect (menu->model, menu->handler_id);
+
+  if (menu->item_links != NULL)
+    g_sequence_free (menu->item_links);
+
+  g_object_unref (menu->model);
+
+  g_slice_free (GMenuExporterMenu, menu);
+}
+
+static void
+g_menu_exporter_link_free (gpointer data)
+{
+  GMenuExporterLink *link = data;
+
+  while (link != NULL)
+    {
+      GMenuExporterLink *tmp = link;
+      link = tmp->next;
+
+      g_menu_exporter_menu_free (tmp->menu);
+      g_free (tmp->name);
+
+      g_slice_free (GMenuExporterLink, tmp);
+    }
+}
+
+static GMenuExporterLink *
+g_menu_exporter_menu_create_links (GMenuExporterMenu *menu,
+                                   gint               position)
+{
+  GMenuExporterLink *list = NULL;
+  GMenuLinkIter *iter;
+  const gchar *name;
+  GMenuModel *model;
+
+  iter = g_menu_model_iterate_item_links (menu->model, position);
+
+  while (g_menu_link_iter_get_next (iter, &name, &model))
+    {
+      GMenuExporterGroup *group;
+      GMenuExporterLink *tmp;
+
+      if (0) /* [magic] */
+        group = g_menu_exporter_create_group (g_menu_exporter_group_get_exporter (menu->group));
+      else
+        group = menu->group;
+
+      tmp = g_slice_new (GMenuExporterLink);
+      tmp->name = g_strconcat (":", name, NULL);
+      tmp->menu = g_menu_exporter_group_add_menu (group, model);
+      tmp->next = list;
+      list = tmp;
+
+      g_object_unref (model);
+    }
+
+  g_object_unref (iter);
+
+  return list;
+}
+
+static GVariant *
+g_menu_exporter_menu_describe_item (GMenuExporterMenu *menu,
+                                    gint               position)
+{
+  GMenuAttributeIter *attr_iter;
+  GVariantBuilder builder;
+  GSequenceIter *iter;
+  GMenuExporterLink *link;
+  const gchar *name;
+  GVariant *value;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+  attr_iter = g_menu_model_iterate_item_attributes (menu->model, position);
+  while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
+    {
+      g_variant_builder_add (&builder, "{sv}", name, value);
+      g_variant_unref (value);
+    }
+  g_object_unref (attr_iter);
+
+  iter = g_sequence_get_iter_at_pos (menu->item_links, position);
+  for (link = g_sequence_get (iter); link; link = link->next)
+    g_variant_builder_add (&builder, "{sv}", link->name,
+                           g_variant_new ("(uu)", g_menu_exporter_group_get_id (link->menu->group), link->menu->id));
+
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+g_menu_exporter_menu_list (GMenuExporterMenu *menu)
+{
+  GVariantBuilder builder;
+  gint i, n;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+
+  n = g_sequence_get_length (menu->item_links);
+  for (i = 0; i < n; i++)
+    g_variant_builder_add_value (&builder, g_menu_exporter_menu_describe_item (menu, i));
+
+  return g_variant_builder_end (&builder);
+}
+
+static void
+g_menu_exporter_menu_items_changed (GMenuModel *model,
+                                    gint        position,
+                                    gint        removed,
+                                    gint        added,
+                                    gpointer    user_data)
+{
+  GMenuExporterMenu *menu = user_data;
+  GSequenceIter *point;
+  gint i;
+
+  g_assert (menu->model == model);
+  g_assert (menu->item_links != NULL);
+  g_assert (position + removed <= g_sequence_get_length (menu->item_links));
+
+  point = g_sequence_get_iter_at_pos (menu->item_links, position + removed);
+  g_sequence_remove_range (g_sequence_get_iter_at_pos (menu->item_links, position), point);
+
+  for (i = position; i < position + added; i++)
+    g_sequence_append (menu->item_links, g_menu_exporter_menu_create_links (menu, i));
+
+  if (g_menu_exporter_group_is_subscribed (menu->group))
+    {
+      GVariantBuilder builder;
+
+      g_variant_builder_init (&builder, G_VARIANT_TYPE ("(uuuuaa{sv})"));
+      g_variant_builder_add (&builder, "u", g_menu_exporter_group_get_id (menu->group));
+      g_variant_builder_add (&builder, "u", menu->id);
+      g_variant_builder_add (&builder, "u", position);
+      g_variant_builder_add (&builder, "u", removed);
+
+      g_variant_builder_open (&builder, G_VARIANT_TYPE ("aa{sv}"));
+      for (i = position; i < position + added; i++)
+        g_variant_builder_add_value (&builder, g_menu_exporter_menu_describe_item (menu, i));
+      g_variant_builder_close (&builder);
+
+      g_menu_exporter_report (g_menu_exporter_group_get_exporter (menu->group), g_variant_builder_end (&builder));
+    }
+}
+
+static void
+g_menu_exporter_menu_prepare (GMenuExporterMenu *menu)
+{
+  g_assert (menu->item_links == NULL);
+
+  if (g_menu_model_is_mutable (menu->model))
+    menu->handler_id = g_signal_connect (menu->model, "items-changed",
+                                         G_CALLBACK (g_menu_exporter_menu_items_changed), menu);
+
+  menu->item_links = g_sequence_new (g_menu_exporter_link_free);
+  g_menu_exporter_menu_items_changed (menu->model, 0, 0, g_menu_model_get_n_items (menu->model), menu);
+}
+
+static GMenuExporterMenu *
+g_menu_exporter_menu_new (GMenuExporterGroup *group,
+                          guint               id,
+                          GMenuModel         *model)
+{
+  GMenuExporterMenu *menu;
+
+  menu = g_slice_new0 (GMenuExporterMenu);
+  menu->group = group;
+  menu->id = id;
+  menu->model = g_object_ref (model);
+
+  return menu;
+}
+
+/* {{{1 GMenuExporterGroup */
+
+struct _GMenuExporterGroup
+{
+  GMenuExporter *exporter;
+  guint          id;
+
+  GHashTable *menus;
+  GSList     *pending;
+  guint       next_menu_id;
+
+  gint subscribed;
+};
+
+static void
+g_menu_exporter_group_check_if_useless (GMenuExporterGroup *group)
+{
+  if (g_hash_table_size (group->menus) == 0 && group->subscribed == 0)
+    {
+      g_menu_exporter_remove_group (group->exporter, group->id);
+
+      g_hash_table_unref (group->menus);
+      g_slist_free (group->pending);
+
+      g_slice_free (GMenuExporterGroup, group);
+    }
+}
+
+static void
+g_menu_exporter_group_clear_pending (GMenuExporterGroup *group)
+{
+  while (group->pending)
+    {
+      GMenuExporterMenu *menu;
+
+      menu = g_hash_table_lookup (group->menus, group->pending->data);
+      group->pending = g_slist_delete_link (group->pending, group->pending);
+      g_assert (menu != NULL);
+
+      g_menu_exporter_menu_prepare (menu);
+    }
+}
+
+static void
+g_menu_exporter_group_subscribe (GMenuExporterGroup *group,
+                                 GVariantBuilder    *builder)
+{
+  GHashTableIter iter;
+  gpointer key, val;
+
+  g_menu_exporter_group_clear_pending (group);
+
+  group->subscribed++;
+
+  g_hash_table_iter_init (&iter, group->menus);
+  while (g_hash_table_iter_next (&iter, &key, &val))
+    {
+      guint id = GPOINTER_TO_INT (key);
+      GMenuExporterMenu *menu = val;
+
+      g_variant_builder_open (builder, G_VARIANT_TYPE ("(uuaa{sv})"));
+      g_variant_builder_add (builder, "u", group->id);
+      g_variant_builder_add (builder, "u", id);
+      g_variant_builder_add_value (builder, g_menu_exporter_menu_list (menu));
+      g_variant_builder_close (builder);
+    }
+}
+
+static void
+g_menu_exporter_group_unsubscribe (GMenuExporterGroup *group,
+                                   gint                count)
+{
+  g_assert (group->subscribed >= count);
+
+  group->subscribed -= count;
+
+  g_menu_exporter_group_check_if_useless (group);
+}
+
+static GMenuExporter *
+g_menu_exporter_group_get_exporter (GMenuExporterGroup *group)
+{
+  return group->exporter;
+}
+
+static gboolean
+g_menu_exporter_group_is_subscribed (GMenuExporterGroup *group)
+{
+  return group->subscribed > 0;
+}
+
+static guint
+g_menu_exporter_group_get_id (GMenuExporterGroup *group)
+{
+  return group->id;
+}
+
+static void
+g_menu_exporter_group_remove_menu (GMenuExporterGroup *group,
+                                   guint               id)
+{
+  g_hash_table_remove (group->menus, GINT_TO_POINTER (id));
+
+  g_menu_exporter_group_check_if_useless (group);
+}
+
+static GMenuExporterMenu *
+g_menu_exporter_group_add_menu (GMenuExporterGroup *group,
+                                GMenuModel         *model)
+{
+  GMenuExporterMenu *menu;
+  guint id;
+
+  id = group->next_menu_id++;
+  menu = g_menu_exporter_menu_new (group, id, model);
+  g_hash_table_insert (group->menus, GINT_TO_POINTER (id), menu);
+  group->pending = g_slist_prepend (group->pending, GINT_TO_POINTER (id));
+
+  return menu;
+}
+
+static GMenuExporterGroup *
+g_menu_exporter_group_new (GMenuExporter *exporter,
+                           guint          id)
+{
+  GMenuExporterGroup *group;
+
+  group = g_slice_new0 (GMenuExporterGroup);
+  group->menus = g_hash_table_new (NULL, NULL);
+  group->exporter = exporter;
+  group->id = id;
+
+  return group;
+}
+
+/* {{{1 GMenuExporterRemote */
+
+struct _GMenuExporterRemote
+{
+  GMenuExporter *exporter;
+  GHashTable    *watches;
+  guint          watch_id;
+};
+
+static void
+g_menu_exporter_remote_subscribe (GMenuExporterRemote *remote,
+                                  guint                group_id,
+                                  GVariantBuilder     *builder)
+{
+  GMenuExporterGroup *group;
+  guint count;
+
+  count = (gsize) g_hash_table_lookup (remote->watches, GINT_TO_POINTER (group_id));
+  g_hash_table_insert (remote->watches, GINT_TO_POINTER (group_id), GINT_TO_POINTER (count + 1));
+
+  group = g_menu_exporter_lookup_group (remote->exporter, group_id);
+  g_menu_exporter_group_subscribe (group, builder);
+}
+
+static void
+g_menu_exporter_remote_unsubscribe (GMenuExporterRemote *remote,
+                                    guint                group_id)
+{
+  GMenuExporterGroup *group;
+  guint count;
+
+  count = (gsize) g_hash_table_lookup (remote->watches, GINT_TO_POINTER (group_id));
+
+  if (count == 0)
+    return;
+
+  if (count != 1)
+    g_hash_table_insert (remote->watches, GINT_TO_POINTER (group_id), GINT_TO_POINTER (count - 1));
+  else
+    g_hash_table_remove (remote->watches, GINT_TO_POINTER (group_id));
+
+  group = g_menu_exporter_lookup_group (remote->exporter, group_id);
+  g_menu_exporter_group_unsubscribe (group, 1);
+}
+
+static gboolean
+g_menu_exporter_remote_has_subscriptions (GMenuExporterRemote *remote)
+{
+  return g_hash_table_size (remote->watches) != 0;
+}
+
+static void
+g_menu_exporter_remote_free (gpointer data)
+{
+  GMenuExporterRemote *remote = data;
+  GHashTableIter iter;
+  gpointer key, val;
+
+  g_hash_table_iter_init (&iter, remote->watches);
+  while (g_hash_table_iter_next (&iter, &key, &val))
+    {
+      GMenuExporterGroup *group;
+
+      group = g_menu_exporter_lookup_group (remote->exporter, GPOINTER_TO_INT (key));
+      g_menu_exporter_group_unsubscribe (group, GPOINTER_TO_INT (val));
+    }
+
+  g_bus_unwatch_name (remote->watch_id);
+  g_hash_table_unref (remote->watches);
+
+  g_slice_free (GMenuExporterRemote, remote);
+}
+
+static GMenuExporterRemote *
+g_menu_exporter_remote_new (GMenuExporter *exporter,
+                            guint          watch_id)
+{
+  GMenuExporterRemote *remote;
+
+  remote = g_slice_new0 (GMenuExporterRemote);
+  remote->exporter = exporter;
+  remote->watches = g_hash_table_new (NULL, NULL);
+  remote->watch_id = watch_id;
+
+  return remote;
+}
+
+/* {{{1 GMenuExporter */
+
+struct _GMenuExporter
+{
+  GDBusConnection *connection;
+  gchar *object_path;
+  guint registration_id;
+  GHashTable *groups;
+  guint next_group_id;
+
+  GMenuExporterMenu *root;
+  GHashTable *remotes;
+};
+
+static void
+g_menu_exporter_name_vanished (GDBusConnection *connection,
+                               const gchar     *name,
+                               gpointer         user_data)
+{
+  GMenuExporter *exporter = user_data;
+
+  g_assert (exporter->connection == connection);
+
+  g_hash_table_remove (exporter->remotes, name);
+}
+
+static GVariant *
+g_menu_exporter_subscribe (GMenuExporter *exporter,
+                           const gchar   *sender,
+                           GVariant      *group_ids)
+{
+  GMenuExporterRemote *remote;
+  GVariantBuilder builder;
+  GVariantIter iter;
+  guint32 id;
+
+  remote = g_hash_table_lookup (exporter->remotes, sender);
+
+  if (remote == NULL)
+    {
+      guint watch_id;
+
+      watch_id = g_bus_watch_name_on_connection (exporter->connection, sender, G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                                 NULL, g_menu_exporter_name_vanished, exporter, NULL);
+      remote = g_menu_exporter_remote_new (exporter, watch_id);
+      g_hash_table_insert (exporter->remotes, g_strdup (sender), remote);
+    }
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a(uuaa{sv}))"));
+
+  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(uuaa{sv})"));
+
+  g_variant_iter_init (&iter, group_ids);
+  while (g_variant_iter_next (&iter, "u", &id))
+    g_menu_exporter_remote_subscribe (remote, id, &builder);
+
+  g_variant_builder_close (&builder);
+
+  return g_variant_builder_end (&builder);
+}
+
+static void
+g_menu_exporter_unsubscribe (GMenuExporter *exporter,
+                             const gchar   *sender,
+                             GVariant      *group_ids)
+{
+  GMenuExporterRemote *remote;
+  GVariantIter iter;
+  guint32 id;
+
+  remote = g_hash_table_lookup (exporter->remotes, sender);
+
+  if (remote == NULL)
+    return;
+
+  g_variant_iter_init (&iter, group_ids);
+  while (g_variant_iter_next (&iter, "u", &id))
+    g_menu_exporter_remote_unsubscribe (remote, id);
+
+  if (!g_menu_exporter_remote_has_subscriptions (remote))
+    g_hash_table_remove (exporter->remotes, sender);
+}
+
+static void
+g_menu_exporter_report (GMenuExporter *exporter,
+                        GVariant      *report)
+{
+}
+
+static void
+g_menu_exporter_remove_group (GMenuExporter *exporter,
+                              guint          id)
+{
+  g_hash_table_remove (exporter->groups, GINT_TO_POINTER (id));
+}
+
+static GMenuExporterGroup *
+g_menu_exporter_lookup_group (GMenuExporter *exporter,
+                              guint          group_id)
+{
+  GMenuExporterGroup *group;
+
+  group = g_hash_table_lookup (exporter->groups, GINT_TO_POINTER (group_id));
+
+  if (group == NULL)
+    {
+      group = g_menu_exporter_group_new (exporter, group_id);
+      g_hash_table_insert (exporter->groups, GINT_TO_POINTER (group_id), group);
+    }
+
+  return group;
+}
+
+static GMenuExporterGroup *
+g_menu_exporter_create_group (GMenuExporter *exporter)
+{
+  GMenuExporterGroup *group;
+  guint id;
+
+  id = exporter->next_group_id++;
+  group = g_menu_exporter_group_new (exporter, id);
+  g_hash_table_insert (exporter->groups, GINT_TO_POINTER (id), group);
+
+  return group;
+}
+
+static void
+g_menu_exporter_free (GMenuExporter *exporter)
+{
+  g_dbus_connection_unregister_object (exporter->connection, exporter->registration_id);
+  g_menu_exporter_menu_free (exporter->root);
+  g_hash_table_unref (exporter->remotes);
+  g_hash_table_unref (exporter->groups);
+  g_object_unref (exporter->connection);
+  g_free (exporter->object_path);
+
+  g_slice_free (GMenuExporter, exporter);
+}
+
+static void
+g_menu_exporter_method_call (GDBusConnection       *connection,
+                             const gchar           *sender,
+                             const gchar           *object_path,
+                             const gchar           *interface_name,
+                             const gchar           *method_name,
+                             GVariant              *parameters,
+                             GDBusMethodInvocation *invocation,
+                             gpointer               user_data)
+{
+  GMenuExporter *exporter = user_data;
+  GVariant *group_ids;
+
+  group_ids = g_variant_get_child_value (parameters, 0);
+
+  if (g_str_equal (method_name, "Start"))
+    g_dbus_method_invocation_return_value (invocation, g_menu_exporter_subscribe (exporter, sender, group_ids));
+
+  else if (g_str_equal (method_name, "End"))
+    {
+      g_menu_exporter_unsubscribe (exporter, sender, group_ids);
+      g_dbus_method_invocation_return_value (invocation, NULL);
+    }
+
+  else
+    g_assert_not_reached ();
+
+  g_variant_unref (group_ids);
+}
+
+static GDBusConnection *
+g_menu_exporter_get_connection (GMenuExporter *exporter)
+{
+  return exporter->connection;
+}
+
+static const gchar *
+g_menu_exporter_get_object_path (GMenuExporter *exporter)
+{
+  return exporter->object_path;
+}
+
+static GMenuExporter *
+g_menu_exporter_new (GDBusConnection  *connection,
+                     const gchar      *object_path,
+                     GMenuModel       *model,
+                     GError          **error)
+{
+  const GDBusInterfaceVTable vtable = {
+    g_menu_exporter_method_call,
+  };
+  GMenuExporter *exporter;
+  guint id;
+
+  exporter = g_slice_new0 (GMenuExporter);
+
+  id = g_dbus_connection_register_object (connection, object_path, org_gtk_Menus_get_interface (),
+                                          &vtable, exporter, NULL, error);
+
+  if (id == 0)
+    {
+      g_slice_free (GMenuExporter, exporter);
+      return NULL;
+    }
+
+  exporter->connection = g_object_ref (connection);
+  exporter->object_path = g_strdup (object_path);
+  exporter->registration_id = id;
+  exporter->groups = g_hash_table_new (NULL, NULL);
+  exporter->remotes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_menu_exporter_remote_free);
+  exporter->root = g_menu_exporter_group_add_menu (g_menu_exporter_create_group (exporter), model);
+
+  return exporter;
+}
+
+/* {{{1 Public API */
+
+static GHashTable *g_menu_exporter_exported_menus;
+
+gboolean
+g_menu_exporter_export (GDBusConnection  *connection,
+                        const gchar      *object_path,
+                        GMenuModel       *menu,
+                        GError          **error)
+{
+  GMenuExporter *exporter;
+
+  if G_UNLIKELY (g_menu_exporter_exported_menus == NULL)
+    g_menu_exporter_exported_menus = g_hash_table_new (NULL, NULL);
+
+  if G_UNLIKELY (g_hash_table_lookup (g_menu_exporter_exported_menus, menu))
+    {
+      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_EXISTS, "The given GMenuModel has already been exported");
+      return FALSE;
+    }
+
+  exporter = g_menu_exporter_new (connection, object_path, menu, error);
+
+  if (exporter == NULL)
+    return FALSE;
+
+  g_hash_table_insert (g_menu_exporter_exported_menus, menu, exporter);
+
+  return TRUE;
+}
+
+gboolean
+g_menu_exporter_stop (GMenuModel *menu)
+{
+  GMenuExporter *exporter;
+
+  if G_UNLIKELY (g_menu_exporter_exported_menus == NULL)
+    return FALSE;
+
+  exporter = g_hash_table_lookup (g_menu_exporter_exported_menus, menu);
+  if G_UNLIKELY (exporter == NULL)
+    return FALSE;
+
+  g_hash_table_remove (g_menu_exporter_exported_menus, menu);
+  g_menu_exporter_free (exporter);
+
+  return TRUE;
+}
+
+gboolean
+g_menu_exporter_query (GMenuModel       *menu,
+                       GDBusConnection **connection,
+                       const gchar     **object_path)
+{
+  GMenuExporter *exporter;
+
+  if (g_menu_exporter_exported_menus == NULL)
+    return FALSE;
+
+  exporter = g_hash_table_lookup (g_menu_exporter_exported_menus, menu);
+  if (exporter == NULL)
+    return FALSE;
+
+  if (connection)
+    *connection = g_menu_exporter_get_connection (exporter);
+
+  if (object_path)
+    *object_path = g_menu_exporter_get_object_path (exporter);
+
+  return TRUE;
+}
+
+/* {{{1 Epilogue */
+/* vim:set foldmethod=marker: */
diff --git a/gio/gmenuexporter.h b/gio/gmenuexporter.h
new file mode 100644
index 0000000..260406f
--- /dev/null
+++ b/gio/gmenuexporter.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2011 Canonical Ltd.
+ * All rights reserved.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __G_MENU_EXPORTER_H__
+#define __G_MENU_EXPORTER_H__
+
+#include <gio/gdbusconnection.h>
+#include <gio/gmenumodel.h>
+
+gboolean        g_menu_exporter_export          (GDBusConnection  *connection,
+                                                 const gchar      *object_path,
+                                                 GMenuModel       *menu,
+                                                 GError          **error);
+
+gboolean        g_menu_exporter_stop            (GMenuModel       *menu);
+
+
+
+gboolean        g_menu_exporter_query           (GMenuModel       *menu,
+                                                 GDBusConnection **connection,
+                                                 const gchar     **object_path);
+
+
+#endif /* __G_MENU_EXPORTER_H__ */



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