aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2010-02-18 10:27:53 -0600
committerTed Gould <ted@gould.cx>2010-02-18 10:27:53 -0600
commit23c53793f62ee6391366a6d9fb1013f1ff92ce7c (patch)
treeaf1117b5c1170ed7bee0f2f4ace569a40bbc7212 /libdbusmenu-glib
parentd1abfffd1770a22420f08851453b93cc72f31e29 (diff)
parent8021b8e5434ecc2d76495c1d3e52cc9a0691852a (diff)
downloadlibdbusmenu-23c53793f62ee6391366a6d9fb1013f1ff92ce7c.tar.gz
libdbusmenu-23c53793f62ee6391366a6d9fb1013f1ff92ce7c.tar.bz2
libdbusmenu-23c53793f62ee6391366a6d9fb1013f1ff92ce7c.zip
Adds a menuitem proxy object to glib and applies various fixes through that this uncovered.
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/Makefile.am3
-rw-r--r--libdbusmenu-glib/client.c131
-rw-r--r--libdbusmenu-glib/menuitem-proxy.c362
-rw-r--r--libdbusmenu-glib/menuitem-proxy.h74
-rw-r--r--libdbusmenu-glib/server.c19
5 files changed, 588 insertions, 1 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index 998af50..65ebf4c 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -12,6 +12,7 @@ libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-glib/
libdbusmenu_glibinclude_HEADERS = \
menuitem.h \
+ menuitem-proxy.h \
server.h \
client.h
@@ -23,6 +24,8 @@ libdbusmenu_glib_la_SOURCES = \
menuitem-marshal.h \
menuitem-marshal.c \
menuitem-private.h \
+ menuitem-proxy.h \
+ menuitem-proxy.c \
server.h \
server.c \
server-marshal.h \
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 071654e..4735794 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -75,6 +75,8 @@ struct _DbusmenuClientPrivate
DBusGProxy * dbusproxy;
GHashTable * type_handlers;
+
+ GArray * delayed_properties;
};
typedef struct _newItemPropData newItemPropData;
@@ -85,6 +87,21 @@ struct _newItemPropData
DbusmenuMenuitem * parent;
};
+typedef struct _propertyDelay propertyDelay;
+struct _propertyDelay
+{
+ guint revision;
+ GArray * entries;
+};
+
+typedef struct _propertyDelayValue propertyDelayValue;
+struct _propertyDelayValue
+{
+ gint id;
+ gchar * name;
+ GValue value;
+};
+
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
@@ -208,6 +225,8 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
+ priv->delayed_properties = g_array_new(FALSE, TRUE, sizeof(propertyDelay));
+
return;
}
@@ -255,6 +274,23 @@ dbusmenu_client_finalize (GObject *object)
g_hash_table_destroy(priv->type_handlers);
}
+ if (priv->delayed_properties) {
+ gint i;
+ for (i = 0; i < priv->delayed_properties->len; i++) {
+ propertyDelay * delay = &g_array_index(priv->delayed_properties, propertyDelay, i);
+ gint j;
+ for (j = 0; j < delay->entries->len; j++) {
+ propertyDelayValue * value = &g_array_index(delay->entries, propertyDelayValue, j);
+ g_free(value->name);
+ g_value_unset(&value->value);
+ }
+ g_array_free(delay->entries, TRUE);
+ delay->entries = NULL;
+ }
+ g_array_free(priv->delayed_properties, TRUE);
+ priv->delayed_properties = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object);
return;
}
@@ -319,6 +355,49 @@ layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient *
return;
}
+/* Add an entry to the set of entries that are delayed until the
+ layout has been updated to this revision */
+static void
+delay_prop_update (guint revision, GArray * delayarray, gint id, gchar * prop, GValue * value)
+{
+ propertyDelay * delay = NULL;
+ gint i;
+
+ /* First look for something with this revision number. This
+ array should be really short, probably not more than an entry or
+ two so there is no reason to optimize this. */
+ for (i = 0; i < delayarray->len; i++) {
+ propertyDelay * localdelay = &g_array_index(delayarray, propertyDelay, i);
+ if (localdelay->revision == revision) {
+ delay = localdelay;
+ break;
+ }
+ }
+
+ /* If we don't have any entires for this revision number then we
+ need to create a new one with it's own array of entires. */
+ if (delay == NULL) {
+ propertyDelay localdelay = {0};
+ localdelay.revision = revision;
+ localdelay.entries = g_array_new(FALSE, TRUE, sizeof(propertyDelayValue));
+
+ g_array_append_val(delayarray, localdelay);
+ delay = &g_array_index(delayarray, propertyDelay, delayarray->len - 1);
+ }
+
+ /* Build the actual entry and tack it on the end of the array
+ of entries */
+ propertyDelayValue delayvalue = {0};
+ delayvalue.id = id;
+ delayvalue.name = g_strdup(prop);
+
+ g_value_init(&delayvalue.value, G_VALUE_TYPE(value));
+ g_value_copy(value, &delayvalue.value);
+
+ g_array_append_val(delay->entries, delayvalue);
+ return;
+}
+
/* Signal from the server that a property has changed
on one of our menuitems */
static void
@@ -333,7 +412,16 @@ id_prop_update (DBusGProxy * proxy, gint id, gchar * property, GValue * value, D
#endif
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- g_return_if_fail(priv->root != NULL);
+
+ /* If we're not on the right revision, we need to cache the property
+ changes as it could be that the menuitems don't exist yet. */
+ if (priv->root == NULL || priv->my_revision != priv->current_revision) {
+ #ifdef MASSIVEDEBUGGING
+ g_debug("Delaying prop update until rev %d for id %d property %s", priv->current_revision, id, property);
+ #endif
+ delay_prop_update(priv->current_revision, priv->delayed_properties, id, property, value);
+ return;
+ }
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
g_return_if_fail(menuitem != NULL);
@@ -810,16 +898,19 @@ update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, vo
DbusmenuClient * client = DBUSMENU_CLIENT(data);
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ /* Check to make sure this isn't an issue */
if (error != NULL) {
g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
return;
}
+ /* Try to take in the layout that we got */
if (!parse_layout(client, xml)) {
g_warning("Unable to parse layout!");
return;
}
+ /* Success, so we need to update our local variables */
priv->my_revision = rev;
/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
priv->layoutcall = NULL;
@@ -828,6 +919,44 @@ update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, vo
#endif
g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE);
+ /* Apply the delayed properties that were queued up while
+ we were waiting on this layout update. */
+ if (G_LIKELY(priv->delayed_properties != NULL)) {
+ gint i;
+ for (i = 0; i < priv->delayed_properties->len; i++) {
+ propertyDelay * delay = &g_array_index(priv->delayed_properties, propertyDelay, i);
+ if (delay->revision > priv->my_revision) {
+ /* Check to see if this is for future revisions, which
+ is possible if there is a ton of updates. */
+ break;
+ }
+
+ gint j;
+ for (j = 0; j < delay->entries->len; j++) {
+ propertyDelayValue * value = &g_array_index(delay->entries, propertyDelayValue, j);
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, value->id);
+ if (mi != NULL) {
+ #ifdef MASSIVEDEBUGGING
+ g_debug("Applying delayed property id %d property %s", value->id, value->name);
+ #endif
+ dbusmenu_menuitem_property_set_value(mi, value->name, &value->value);
+ }
+ g_free(value->name);
+ g_value_unset(&value->value);
+ }
+ g_array_free(delay->entries, TRUE);
+
+ /* We're removing the entry and moving the index down one
+ to ensure that we adjust for the shift in the array. The
+ reality is that i is always 0. You understood this loop
+ until you got here, didn't you :) */
+ g_array_remove_index(priv->delayed_properties, i);
+ i--;
+ }
+ }
+
+ /* Check to see if we got another update in the time this
+ one was issued. */
if (priv->my_revision < priv->current_revision) {
update_layout(client);
}
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;
+}
diff --git a/libdbusmenu-glib/menuitem-proxy.h b/libdbusmenu-glib/menuitem-proxy.h
new file mode 100644
index 0000000..56c4941
--- /dev/null
+++ b/libdbusmenu-glib/menuitem-proxy.h
@@ -0,0 +1,74 @@
+/*
+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/>
+*/
+
+#ifndef __DBUSMENU_MENUITEM_PROXY_H__
+#define __DBUSMENU_MENUITEM_PROXY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "menuitem.h"
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_MENUITEM_PROXY (dbusmenu_menuitem_proxy_get_type ())
+#define DBUSMENU_MENUITEM_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxy))
+#define DBUSMENU_MENUITEM_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyClass))
+#define DBUSMENU_IS_MENUITEM_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_MENUITEM_PROXY))
+#define DBUSMENU_IS_MENUITEM_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM_PROXY))
+#define DBUSMENU_MENUITEM_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyClass))
+
+typedef struct _DbusmenuMenuitemProxy DbusmenuMenuitemProxy;
+typedef struct _DbusmenuMenuitemProxyClass DbusmenuMenuitemProxyClass;
+
+/**
+ DbusmenuMenuitemProxyClass:
+ @parent_class: The Class of #DbusmeneMenuitem
+
+ Functions and signal slots for #DbusmenuMenuitemProxy.
+*/
+struct _DbusmenuMenuitemProxyClass {
+ DbusmenuMenuitemClass parent_class;
+};
+
+/**
+ DbusmeneMenuitemProxy:
+ @parent: The instance of #DbusmenuMenuitem
+
+ Public instance data for a #DbusmenuMenuitemProxy.
+*/
+struct _DbusmenuMenuitemProxy {
+ DbusmenuMenuitem parent;
+};
+
+GType dbusmenu_menuitem_proxy_get_type (void);
+DbusmenuMenuitemProxy * dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi);
+DbusmenuMenuitem * dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi);
+
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 55d339f..f6dddf1 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -301,10 +301,29 @@ menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * val
return;
}
+/* Adds the signals for this entry to the list and looks at
+ the children of this entry to add the signals we need
+ as well. We like signals. */
+static void
+added_check_children (gpointer data, gpointer user_data)
+{
+ DbusmenuMenuitem * mi = (DbusmenuMenuitem *)data;
+ DbusmenuServer * server = (DbusmenuServer *)user_data;
+
+ menuitem_signals_create(mi, server);
+ g_list_foreach(dbusmenu_menuitem_get_children(mi), added_check_children, server);
+
+ return;
+}
+
+/* Callback for when a child is added. We need to connect everything
+ up and signal that the layout has changed. */
static void
menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server)
{
menuitem_signals_create(child, server);
+ g_list_foreach(dbusmenu_menuitem_get_children(child), added_check_children, server);
+
/* TODO: We probably need to group the layout update signals to make the number more reasonble. */
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
priv->layout_revision++;