From: Matthias Clasen Date: Sun, 27 Nov 2011 02:02:15 +0000 (-0500) Subject: Add GMenuModel D-Bus exporter X-Git-Tag: 2.31.4~95 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;ds=sidebyside;h=66e089f086c0243eb43847137081bf99f2fc89dc;p=platform%2Fupstream%2Fglib.git Add GMenuModel D-Bus exporter --- diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index a1e6ad2..bb2bcb0 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -203,6 +203,7 @@ + Extending GIO diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 88bc4e9..b034148 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -3542,6 +3542,13 @@ G_NETWORK_MONITOR_GET_INTERFACE
+gmenuexporter +g_menu_exporter_export +g_menu_exporter_stop +g_menu_exporter_query +
+ +
gmenu GMenu g_menu_new diff --git a/gio/Makefile.am b/gio/Makefile.am index d80a97b..0f0515e 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -136,6 +136,7 @@ application_headers = \ gmenumodel.h \ gmenu.h \ gmenumarkup.h \ + gmenuexporter.h \ $(NULL) application_sources = \ @@ -152,6 +153,7 @@ application_sources = \ gmenumodel.c \ gmenu.c \ gmenumarkup.c \ + gmenuexporter.c \ $(NULL) local_sources = \ diff --git a/gio/gio.h b/gio/gio.h index f379ecc..296355c 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -148,6 +148,7 @@ #include #include #include +#include #include #undef __GIO_GIO_H_INSIDE__ diff --git a/gio/gio.symbols b/gio/gio.symbols index 1091455..0129067 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1613,6 +1613,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..e0fb4ca --- /dev/null +++ b/gio/gmenuexporter.c @@ -0,0 +1,884 @@ +/* + * Copyright © 2011 Canonical Ltd. + * + * This library 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. + * + * Author: Ryan Lortie + */ + +#include "gmenuexporter.h" + +#include "gdbusmethodinvocation.h" +#include "gdbusintrospection.h" +#include "gdbusnamewatching.h" +#include "gdbuserror.h" + +/** + * SECTION:gmenuexporter + * @title: GMenuModel exporter + * @short_description: Export GMenuModels on D-Bus + * @see_also: #GMenuModel, #GMenuProxy + * + * These functions support exporting a #GMenuModel on D-Bus. + * The D-Bus interface that is used is a private implementation + * detail. + * + * To access an exported #GMenuModel remotely, use + * g_menu_proxy_get() to obtain a #GMenuProxy. + */ + +/* {{{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 ("" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " arg type='a(uuuuaa{sv})' name='changes'/>" + " " + " " + "", &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 char *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 char *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_insert_before (point, 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) +{ + gint n_items; + + 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); + + n_items = g_menu_model_get_n_items (menu->model); + if (n_items) + g_menu_exporter_menu_items_changed (menu->model, 0, 0, n_items, 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; + guint next_menu_id; + gboolean prepared; + + 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_slice_free (GMenuExporterGroup, group); + } +} + +static void +g_menu_exporter_group_subscribe (GMenuExporterGroup *group, + GVariantBuilder *builder) +{ + GHashTableIter iter; + gpointer key, val; + + if (!group->prepared) + { + GMenuExporterMenu *menu; + + /* set this first, so that any menus created during the + * preparation of the first menu also end up in the prepared + * state. + * */ + group->prepared = TRUE; + + menu = g_hash_table_lookup (group->menus, 0); + g_menu_exporter_menu_prepare (menu); + } + + 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; + + if (g_sequence_get_length (menu->item_links)) + { + 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); + + if (group->prepared) + g_menu_exporter_menu_prepare (menu); + + 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) +{ + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_open (&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value (&builder, report); + g_variant_builder_close (&builder); + + g_dbus_connection_emit_signal (exporter->connection, + NULL, + exporter->object_path, + "org.gtk.Menus", "Changed", + g_variant_builder_end (&builder), + NULL); +} + +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; + +/** + * g_menu_exporter_export: + * @connection: a #GDBusConnection + * @object_path: a D-Bus object path + * @menu: a #GMenuModel + * @error: return location for an error, or %NULL + * + * Exports @menu on @connection at @object_path. + * + * The implemented D-Bus API should be considered private. + * It is subject to change in the future. + * + * A given menu model can only be exported on one object path + * and an object_path can only have one action group exported + * on it. If either constraint is violated, the export will + * fail and %FALSE will be returned (with @error set accordingly). + * + * Use g_menu_exporter_stop() to stop exporting @menu + * or g_menu_exporter_query() to find out if and where a given + * menu model is exported. + * + * Returns: %TRUE if the export is successful, or %FALSE (with + * @error set) in the event of a failure. + */ +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; +} + +/** + * g_menu_exporter_stop: + * @menu: a #GMenuModel + * + * Stops the export of @menu. + * + * This reverses the effect of a previous call to + * g_menu_exporter_export() for @menu. + * + * Returns: %TRUE if an export was stopped or %FALSE + * if @menu was not exported in the first place + */ +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; +} + +/** + * g_menu_exporter_query: + * @menu: a #GMenuModel + * @connection: (out): the #GDBusConnection used for exporting + * @object_path: (out): the object path used for exporting + * + * Queries if and where @menu is exported. + * + * If @menu is exported, %TRUE is returned. If @connection is + * non-%NULL then it is set to the #GDBusConnection used for + * the export. If @object_path is non-%NULL then it is set to + * the object path. + * + * If the @menu is not exported, %FALSE is returned and + * @connection and @object_path remain unmodified. + * + * Returns: %TRUE if @menu was exported, else %FALSE + */ +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..602bf31 --- /dev/null +++ b/gio/gmenuexporter.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2011 Canonical Ltd. + * + * This library 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. + * + * Author: Ryan Lortie + */ + +#ifndef __G_MENU_EXPORTER_H__ +#define __G_MENU_EXPORTER_H__ + +#include +#include + +G_BEGIN_DECLS + +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); + +G_END_DECLS + +#endif /* __G_MENU_EXPORTER_H__ */