aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib/menuitem-proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-glib/menuitem-proxy.c')
-rw-r--r--libdbusmenu-glib/menuitem-proxy.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/libdbusmenu-glib/menuitem-proxy.c b/libdbusmenu-glib/menuitem-proxy.c
new file mode 100644
index 0000000..2dd5ada
--- /dev/null
+++ b/libdbusmenu-glib/menuitem-proxy.c
@@ -0,0 +1,362 @@
+/*
+An object to ferry over properties and signals between two different
+dbusmenu instances. Useful for services.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "menuitem-proxy.h"
+
+typedef struct _DbusmenuMenuitemProxyPrivate DbusmenuMenuitemProxyPrivate;
+struct _DbusmenuMenuitemProxyPrivate {
+ DbusmenuMenuitem * mi;
+ gulong sig_property_changed;
+ gulong sig_child_added;
+ gulong sig_child_removed;
+ gulong sig_child_moved;
+};
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_MENU_ITEM
+};
+
+#define PROP_MENU_ITEM_S "menu-item"
+
+#define DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyPrivate))
+
+static void dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass);
+static void dbusmenu_menuitem_proxy_init (DbusmenuMenuitemProxy *self);
+static void dbusmenu_menuitem_proxy_dispose (GObject *object);
+static void dbusmenu_menuitem_proxy_finalize (GObject *object);
+static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
+static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
+static void handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
+static void add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi);
+static void remove_menuitem (DbusmenuMenuitemProxy * pmi);
+
+G_DEFINE_TYPE (DbusmenuMenuitemProxy, dbusmenu_menuitem_proxy, DBUSMENU_TYPE_MENUITEM);
+
+static void
+dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuMenuitemProxyPrivate));
+
+ object_class->dispose = dbusmenu_menuitem_proxy_dispose;
+ object_class->finalize = dbusmenu_menuitem_proxy_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ DbusmenuMenuitemClass * miclass = DBUSMENU_MENUITEM_CLASS(klass);
+
+ miclass->handle_event = handle_event;
+
+ g_object_class_install_property (object_class, PROP_MENU_ITEM,
+ g_param_spec_object(PROP_MENU_ITEM_S, "The Menuitem we're proxying",
+ "An instance of the DbusmenuMenuitem class that this menuitem will mimic.",
+ DBUSMENU_TYPE_MENUITEM,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ return;
+}
+
+static void
+dbusmenu_menuitem_proxy_init (DbusmenuMenuitemProxy *self)
+{
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(self);
+
+ priv->mi = NULL;
+
+ priv->sig_property_changed = 0;
+ priv->sig_child_added = 0;
+ priv->sig_child_removed = 0;
+ priv->sig_child_moved = 0;
+
+ return;
+}
+
+/* Remove references to objects */
+static void
+dbusmenu_menuitem_proxy_dispose (GObject *object)
+{
+ remove_menuitem(DBUSMENU_MENUITEM_PROXY(object));
+
+ G_OBJECT_CLASS (dbusmenu_menuitem_proxy_parent_class)->dispose (object);
+ return;
+}
+
+/* Free any memory that we've allocated */
+static void
+dbusmenu_menuitem_proxy_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_menuitem_proxy_parent_class)->finalize (object);
+ return;
+}
+
+/* Set a property using the generic GObject interface */
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ switch (id) {
+ case PROP_MENU_ITEM: {
+ GObject * lobj = g_value_get_object(value);
+ add_menuitem(DBUSMENU_MENUITEM_PROXY(obj), DBUSMENU_MENUITEM(lobj));
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
+ break;
+ }
+
+ return;
+}
+
+/* Get a property using the generic GObject interface */
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_MENU_ITEM:
+ g_value_set_object(value, priv->mi);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, pspec);
+ break;
+ }
+
+ return;
+}
+
+/* Takes the event and passes it along to the item that we're
+ playing proxy for. */
+static void
+handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM_PROXY(mi));
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(mi);
+ g_return_if_fail(priv->mi != NULL);
+ return dbusmenu_menuitem_handle_event(priv->mi, name, value, timestamp);
+}
+
+/* Watches a property change and makes sure to put that value
+ into our property list. */
+static void
+proxy_item_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, gpointer user_data)
+{
+ DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+ dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), property, value);
+ return;
+}
+
+/* Looks for a child getting added and wraps it and places it
+ in our list of children. */
+static void
+proxy_item_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint position, gpointer user_data)
+{
+ DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+ DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(child);
+ dbusmenu_menuitem_child_add_position(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi), position);
+ return;
+}
+
+/* Find the wrapper for this child and remove it as well. */
+static void
+proxy_item_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, gpointer user_data)
+{
+ DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+ GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(pmi));
+ DbusmenuMenuitemProxy * finalpmi = NULL;
+ GList * childitem;
+
+ for (childitem = children; childitem != NULL; childitem = g_list_next(childitem)) {
+ DbusmenuMenuitemProxy * childpmi = (DbusmenuMenuitemProxy *)childitem->data;
+ DbusmenuMenuitem * childmi = dbusmenu_menuitem_proxy_get_wrapped(childpmi);
+ if (childmi == child) {
+ finalpmi = childpmi;
+ break;
+ }
+ }
+
+ if (finalpmi != NULL) {
+ dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(finalpmi));
+ }
+
+ return;
+}
+
+/* Find the wrapper for the item and move it in our child list */
+static void
+proxy_item_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint newpos, guint oldpos, gpointer user_data)
+{
+ DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
+ GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(pmi));
+ DbusmenuMenuitemProxy * finalpmi = NULL;
+ GList * childitem;
+
+ for (childitem = children; childitem != NULL; childitem = g_list_next(childitem)) {
+ DbusmenuMenuitemProxy * childpmi = (DbusmenuMenuitemProxy *)childitem->data;
+ DbusmenuMenuitem * childmi = dbusmenu_menuitem_proxy_get_wrapped(childpmi);
+ if (childmi == child) {
+ finalpmi = childpmi;
+ break;
+ }
+ }
+
+ if (finalpmi != NULL) {
+ dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(finalpmi), newpos);
+ }
+
+ return;
+}
+
+/* Making g_object_unref into a GFunc */
+static void
+func_g_object_unref (gpointer data, gpointer user_data)
+{
+ return g_object_unref(G_OBJECT(data));
+}
+
+/* References all of the things we need for talking to this menuitem
+ including signals and other data. If the menuitem already has
+ properties we need to signal that they've changed for us. */
+static void
+add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi)
+{
+ /* Put it in private */
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+ if (priv->mi != NULL) {
+ remove_menuitem(pmi);
+ }
+ priv->mi = mi;
+ g_object_ref(G_OBJECT(priv->mi));
+
+ /* Attach signals */
+ priv->sig_property_changed = g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(proxy_item_property_changed), pmi);
+ priv->sig_child_added = g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(proxy_item_child_added), pmi);
+ priv->sig_child_removed = g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(proxy_item_child_removed), pmi);
+ priv->sig_child_moved = g_signal_connect(G_OBJECT(priv->mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(proxy_item_child_moved), pmi);
+
+ /* Grab (cache) Properties */
+ GList * props = dbusmenu_menuitem_properties_list(priv->mi);
+ GList * prop;
+ for (prop = props; prop != NULL; prop = g_list_next(prop)) {
+ gchar * prop_name = (gchar *)prop->data;
+ dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), prop_name, dbusmenu_menuitem_property_get_value(priv->mi, prop_name));
+ }
+ g_list_free(props);
+
+ /* Go through children and wrap them */
+ GList * children = dbusmenu_menuitem_get_children(priv->mi);
+ GList * child;
+ for (child = children; child != NULL; child = g_list_next(child)) {
+ DbusmenuMenuitemProxy * child_pmi = dbusmenu_menuitem_proxy_new(DBUSMENU_MENUITEM(child->data));
+ dbusmenu_menuitem_child_append(DBUSMENU_MENUITEM(pmi), DBUSMENU_MENUITEM(child_pmi));
+ }
+
+ return;
+}
+
+/* Removes the menuitem from being our proxy. Typically this isn't
+ done until this object is destroyed, but who knows?!? */
+static void
+remove_menuitem (DbusmenuMenuitemProxy * pmi)
+{
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+ if (priv->mi == NULL) {
+ return;
+ }
+
+ /* Remove signals */
+ if (priv->sig_property_changed != 0) {
+ g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_property_changed);
+ }
+ if (priv->sig_child_added != 0) {
+ g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_added);
+ }
+ if (priv->sig_child_removed != 0) {
+ g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_removed);
+ }
+ if (priv->sig_child_moved != 0) {
+ g_signal_handler_disconnect(G_OBJECT(priv->mi), priv->sig_child_moved);
+ }
+
+ /* Unref */
+ g_object_unref(G_OBJECT(priv->mi));
+ priv->mi = NULL;
+
+ /* Remove our own children */
+ GList * children = dbusmenu_menuitem_take_children(DBUSMENU_MENUITEM(pmi));
+ g_list_foreach(children, func_g_object_unref, NULL);
+ g_list_free(children);
+
+ return;
+}
+
+/**
+ dbusmenu_menuitem_proxy_new:
+ @mi: The #DbusmenuMenuitem to proxy
+
+ Builds a new #DbusmenuMenuitemProxy object that proxies
+ all of the values for @mi.
+
+ Return value: A new #DbusmenuMenuitemProxy object.
+*/
+DbusmenuMenuitemProxy *
+dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi)
+{
+ DbusmenuMenuitemProxy * pmi = g_object_new(DBUSMENU_TYPE_MENUITEM_PROXY,
+ PROP_MENU_ITEM_S, mi,
+ NULL);
+
+ return pmi;
+}
+
+/**
+ dbusmenu_menuitem_proxy_get_wrapped:
+ @pmi: #DbusmenuMenuitemProxy to look into
+
+ Accesses the private variable of which #DbusmenuMenuitem
+ we are doing the proxying for.
+
+ Return value: A #DbusmenuMenuitem object or a #NULL if we
+ don't have one or there is an error.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi)
+{
+ g_return_val_if_fail(DBUSMENU_MENUITEM_PROXY(pmi), NULL);
+ DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(pmi);
+ return priv->mi;
+}