aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
authorTed Gould <ted@canonical.com>2009-05-12 11:23:10 -0500
committerTed Gould <ted@canonical.com>2009-05-12 11:23:10 -0500
commit3d0e0276fd7856831dcc845a24a252ad304b3bad (patch)
tree5ec06325d34b0fdab498045aa911fb534eb2ca48 /libdbusmenu-glib
parentc5024e812fb046f6bccceafaf46ebbd966f465cf (diff)
parent53b4a13ca4541bcdf85ca3ee28cb6ffc81e6f5b0 (diff)
downloadlibdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.tar.gz
libdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.tar.bz2
libdbusmenu-3d0e0276fd7856831dcc845a24a252ad304b3bad.zip
Merging in my working branch that brings in basic object functionality passing across DBus.
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/Makefile.am52
-rw-r--r--libdbusmenu-glib/client.c499
-rw-r--r--libdbusmenu-glib/client.h116
-rw-r--r--libdbusmenu-glib/dbus-menu.xml70
-rw-r--r--libdbusmenu-glib/menuitem.c424
-rw-r--r--libdbusmenu-glib/menuitem.h135
-rw-r--r--libdbusmenu-glib/server-marshal.list1
-rw-r--r--libdbusmenu-glib/server.c332
-rw-r--r--libdbusmenu-glib/server.h120
-rw-r--r--libdbusmenu-glib/test.c4
-rw-r--r--libdbusmenu-glib/test.h2
11 files changed, 1744 insertions, 11 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index c94231b..b273555 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -1,6 +1,7 @@
EXTRA_DIST = \
- dbusmenu-glib.pc.in
+ dbusmenu-glib.pc.in \
+ dbus-menu.xml
lib_LTLIBRARIES = \
libdbusmenu-glib.la
@@ -8,10 +9,21 @@ lib_LTLIBRARIES = \
libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-glib/
libdbusmenu_glibinclude_HEADERS = \
- test.h
+ menuitem.h \
+ server.h \
+ client.h
libdbusmenu_glib_la_SOURCES = \
- test.c
+ dbusmenu-server.h \
+ dbusmenu-client.h \
+ menuitem.h \
+ menuitem.c \
+ server.h \
+ server.c \
+ server-marshal.h \
+ server-marshal.c \
+ client.h \
+ client.c
libdbusmenu_glib_la_LDFLAGS = \
-version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
@@ -19,11 +31,41 @@ libdbusmenu_glib_la_LDFLAGS = \
-export-symbols-regex "^[^_].*"
libdbusmenu_glib_la_CFLAGS = \
- $(LIBDBUSMENU_GLIB_CFLAGS)
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
libdbusmenu_glib_la_LIBADD = \
- $(LIBDBUSMENU_GLIB_LIBS)
+ $(DBUSMENUGLIB_LIBS)
pkgconfig_DATA = dbusmenu-glib.pc
pkgconfigdir = $(libdir)/pkgconfig
+BUILT_SOURCES = \
+ dbusmenu-client.h \
+ dbusmenu-server.h \
+ server-marshal.h \
+ server-marshal.c
+
+dbusmenu-server.h: dbus-menu.xml
+ dbus-binding-tool \
+ --prefix=_dbusmenu_server \
+ --mode=glib-server \
+ --output=dbusmenu-server.h \
+ $(srcdir)/dbus-menu.xml
+
+dbusmenu-client.h: dbus-menu.xml
+ dbus-binding-tool \
+ --prefix=_dbusmenu_client \
+ --mode=glib-client \
+ --output=dbusmenu-client.h \
+ $(srcdir)/dbus-menu.xml
+
+server-marshal.h: $(srcdir)/server-marshal.list
+ glib-genmarshal --header \
+ --prefix=_dbusmenu_server_marshal $(srcdir)/server-marshal.list \
+ > server-marshal.h
+
+server-marshal.c: $(srcdir)/server-marshal.list
+ glib-genmarshal --body \
+ --prefix=_dbusmenu_server_marshal $(srcdir)/server-marshal.list \
+ > server-marshal.c
+
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
new file mode 100644
index 0000000..b0b1157
--- /dev/null
+++ b/libdbusmenu-glib/client.c
@@ -0,0 +1,499 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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 <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "client.h"
+#include "dbusmenu-client.h"
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_DBUSOBJECT,
+ PROP_DBUSNAME
+};
+
+/* Signals */
+enum {
+ LAYOUT_UPDATED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct _DbusmenuClientPrivate DbusmenuClientPrivate;
+struct _DbusmenuClientPrivate
+{
+ DbusmenuMenuitem * root;
+
+ gchar * dbus_object;
+ gchar * dbus_name;
+
+ DBusGConnection * session_bus;
+ DBusGProxy * menuproxy;
+ DBusGProxy * propproxy;
+ DBusGProxyCall * layoutcall;
+};
+
+#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
+
+/* GObject Stuff */
+static void dbusmenu_client_class_init (DbusmenuClientClass *klass);
+static void dbusmenu_client_init (DbusmenuClient *self);
+static void dbusmenu_client_dispose (GObject *object);
+static void dbusmenu_client_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);
+/* Private Funcs */
+static void layout_update (DBusGProxy * proxy, DbusmenuClient * client);
+static void build_proxies (DbusmenuClient * client);
+static guint parse_node_get_id (xmlNodePtr node);
+static DbusmenuMenuitem * parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent);
+static void parse_layout (DbusmenuClient * client, const gchar * layout);
+static void update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data);
+static void update_layout (DbusmenuClient * client);
+
+/* Build a type */
+G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT);
+
+static void
+dbusmenu_client_class_init (DbusmenuClientClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuClientPrivate));
+
+ object_class->dispose = dbusmenu_client_dispose;
+ object_class->finalize = dbusmenu_client_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /**
+ DbusmenuClient::layout-update:
+ @arg0: The #DbusmenuClient object
+
+ Tells that the layout has been updated and parsed by
+ this object and is ready for grabbing by the calling
+ application.
+ */
+ signals[LAYOUT_UPDATED] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, layout_updated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+
+ g_object_class_install_property (object_class, PROP_DBUSOBJECT,
+ g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent",
+ "The Object on the client that we're getting our data from.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_DBUSNAME,
+ g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_NAME, "DBus Client we connect to",
+ "Name of the DBus client we're connecting to.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ return;
+}
+
+static void
+dbusmenu_client_init (DbusmenuClient *self)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(self);
+
+ priv->root = NULL;
+
+ priv->dbus_object = NULL;
+ priv->dbus_name = NULL;
+
+ priv->session_bus = NULL;
+ priv->menuproxy = NULL;
+ priv->propproxy = NULL;
+ priv->layoutcall = NULL;
+
+ return;
+}
+
+static void
+dbusmenu_client_dispose (GObject *object)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object);
+
+ if (priv->layoutcall != NULL) {
+ dbus_g_proxy_cancel_call(priv->propproxy, priv->layoutcall);
+ priv->layoutcall = NULL;
+ }
+ if (priv->menuproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->menuproxy));
+ priv->menuproxy = NULL;
+ }
+ if (priv->propproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->propproxy));
+ priv->propproxy = NULL;
+ }
+ priv->session_bus = NULL;
+
+ G_OBJECT_CLASS (dbusmenu_client_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_client_finalize (GObject *object)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object);
+
+ g_free(priv->dbus_name);
+ g_free(priv->dbus_object);
+
+ G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object);
+ return;
+}
+
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUSNAME:
+ priv->dbus_name = g_value_dup_string(value);
+ if (priv->dbus_name != NULL && priv->dbus_object != NULL) {
+ build_proxies(DBUSMENU_CLIENT(obj));
+ }
+ break;
+ case PROP_DBUSOBJECT:
+ priv->dbus_object = g_value_dup_string(value);
+ if (priv->dbus_name != NULL && priv->dbus_object != NULL) {
+ build_proxies(DBUSMENU_CLIENT(obj));
+ }
+ break;
+ default:
+ g_warning("Unknown property %d.", id);
+ return;
+ }
+
+ return;
+}
+
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUSNAME:
+ g_value_set_string(value, priv->dbus_name);
+ break;
+ case PROP_DBUSOBJECT:
+ g_value_set_string(value, priv->dbus_object);
+ break;
+ default:
+ g_warning("Unknown property %d.", id);
+ return;
+ }
+
+ return;
+}
+
+/* Internal funcs */
+
+/* Annoying little wrapper to make the right function update */
+static void
+layout_update (DBusGProxy * proxy, DbusmenuClient * client)
+{
+ update_layout(client);
+ return;
+}
+
+/* When we have a name and an object, build the two proxies and get the
+ first version of the layout */
+static void
+build_proxies (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ GError * error = NULL;
+
+ g_return_if_fail(priv->dbus_object != NULL);
+ g_return_if_fail(priv->dbus_name != NULL);
+
+ priv->session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ if (error != NULL) {
+ g_error("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ priv->propproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
+ priv->dbus_name,
+ priv->dbus_object,
+ DBUS_INTERFACE_PROPERTIES,
+ &error);
+ if (error != NULL) {
+ g_error("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ priv->menuproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
+ priv->dbus_name,
+ priv->dbus_object,
+ "org.freedesktop.dbusmenu",
+ &error);
+ if (error != NULL) {
+ g_error("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdate", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdate", G_CALLBACK(layout_update), client, NULL);
+
+ return;
+}
+
+/* Get the ID attribute of the node, parse it and
+ return it. Also we're checking to ensure the node
+ is a 'menu' here. */
+static guint
+parse_node_get_id (xmlNodePtr node)
+{
+ if (g_strcmp0((gchar *)node->name, "menu") != 0) {
+ /* This kills some nodes early */
+ g_warning("XML Node is not 'menu' it is '%s'", node->name);
+ return 0;
+ }
+
+ xmlAttrPtr attrib;
+ for (attrib = node->properties; attrib != NULL; attrib = attrib->next) {
+ if (g_strcmp0((gchar *)attrib->name, "id") == 0) {
+ if (attrib->children != NULL) {
+ guint id = (guint)g_ascii_strtoull((gchar *)attrib->children->content, NULL, 10);
+ g_debug ("Found ID: %d", id);
+ return id;
+ }
+ break;
+ }
+ }
+
+ g_warning("Unable to find an ID on the node");
+ return 0;
+}
+
+/* Parse recursively through the XML and make it into
+ objects as need be */
+static DbusmenuMenuitem *
+parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent)
+{
+ guint id = parse_node_get_id(node);
+ g_debug("Looking at node with id: %d", id);
+ if (item == NULL || dbusmenu_menuitem_get_id(item) != id || id == 0) {
+ if (item != NULL) {
+ if (parent != NULL) {
+ dbusmenu_menuitem_child_delete(parent, item);
+ }
+ g_object_unref(G_OBJECT(item));
+ }
+
+ if (id == 0) {
+ g_warning("ID from XML file is zero");
+ return NULL;
+ }
+
+ /* Build a new item */
+ item = dbusmenu_menuitem_new_with_id(id);
+ }
+
+ xmlNodePtr children;
+ guint position;
+ GList * oldchildren = dbusmenu_menuitem_take_children(item);
+
+ for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
+ g_debug("Looking at child: %d", position);
+ guint childid = parse_node_get_id(children);
+ DbusmenuMenuitem * childmi = NULL;
+
+ GList * childsearch = NULL;
+ for (childsearch = oldchildren; childsearch != NULL; childsearch = g_list_next(childsearch)) {
+ DbusmenuMenuitem * cs_mi = DBUSMENU_MENUITEM(childsearch->data);
+ if (childid == dbusmenu_menuitem_get_id(cs_mi)) {
+ oldchildren = g_list_remove(oldchildren, cs_mi);
+ childmi = cs_mi;
+ break;
+ }
+ }
+
+ childmi = parse_layout_xml(children, childmi, item);
+ dbusmenu_menuitem_child_add_position(item, childmi, position);
+ }
+
+ GList * oldchildleft = NULL;
+ for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) {
+ DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data);
+ g_object_unref(G_OBJECT(oldmi));
+ }
+ g_list_free(oldchildren);
+
+ return item;
+}
+
+/* Take the layout passed to us over DBus and turn it into
+ a set of beautiful objects */
+static void
+parse_layout (DbusmenuClient * client, const gchar * layout)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ xmlDocPtr xmldoc;
+
+ xmldoc = xmlReadMemory(layout, g_utf8_strlen(layout, 16*1024), "dbusmenu.xml", NULL, 0);
+
+ xmlNodePtr root = xmlDocGetRootElement(xmldoc);
+
+ priv->root = parse_layout_xml(root, priv->root, NULL);
+ if (priv->root == NULL) {
+ g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout);
+ }
+
+ xmlFreeDoc(xmldoc);
+ return;
+}
+
+/* When the layout property returns, here's where we take care of that. */
+static void
+update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
+{
+ DbusmenuClient * client = DBUSMENU_CLIENT(data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ GError * error = NULL;
+ GValue value = {0};
+
+ if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+ g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ priv->layoutcall = NULL;
+ g_error_free(error);
+ return;
+ }
+
+ const gchar * xml = g_value_get_string(&value);
+ g_debug("Got layout string: %s", xml);
+ parse_layout(client, xml);
+
+ priv->layoutcall = NULL;
+ g_debug("Root is now: 0x%X", (unsigned int)priv->root);
+ g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE);
+
+ return;
+}
+
+/* Call the property on the server we're connected to and set it up to
+ be async back to _update_layout_cb */
+static void
+update_layout (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (priv->layoutcall != NULL) {
+ return;
+ }
+
+ priv->layoutcall = dbus_g_proxy_begin_call (priv->propproxy,
+ "Get",
+ update_layout_cb,
+ client,
+ NULL,
+ G_TYPE_STRING, "org.freedesktop.dbusmenu",
+ G_TYPE_STRING, "layout",
+ G_TYPE_INVALID, G_TYPE_VALUE, G_TYPE_INVALID);
+
+ return;
+}
+
+/* Public API */
+/**
+ dbusmenu_client_new:
+ @name: The DBus name for the server to connect to
+ @object: The object on the server to monitor
+
+ This function creates a new client that connects to a specific
+ server on DBus. That server is at a specific location sharing
+ a known object. The interface is assumed by the code to be
+ the DBus menu interface. The newly created client will start
+ sending out events as it syncs up with the server.
+
+ Return value: A brand new #DbusmenuClient
+*/
+DbusmenuClient *
+dbusmenu_client_new (const gchar * name, const gchar * object)
+{
+ DbusmenuClient * self = g_object_new(DBUSMENU_TYPE_CLIENT,
+ DBUSMENU_CLIENT_PROP_DBUS_NAME, name,
+ DBUSMENU_CLIENT_PROP_DBUS_OBJECT, object,
+ NULL);
+ update_layout(self);
+
+ return self;
+}
+
+/**
+ dbusmenu_client_get_root:
+ @client: The #DbusmenuClient to get the root node from
+
+ Grabs the root node for the specified client @client. This
+ function may block. It will block if there is currently a
+ call to update the layout, it will block on that layout
+ updated and then return the newly updated layout. Chances
+ are that this update is in the queue for the mainloop as
+ it would have been requested some time ago, but in theory
+ it could block longer.
+
+ Return value: A #DbusmenuMenuitem representing the root of
+ menu on the server.
+*/
+DbusmenuMenuitem *
+dbusmenu_client_get_root (DbusmenuClient * client)
+{
+ g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), NULL);
+
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (priv->layoutcall != NULL) {
+ /* Will end the current call and block on it's completion */
+ update_layout_cb(priv->propproxy, priv->layoutcall, client);
+ }
+
+ return priv->root;
+}
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
new file mode 100644
index 0000000..d591ebb
--- /dev/null
+++ b/libdbusmenu-glib/client.h
@@ -0,0 +1,116 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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_CLIENT_H__
+#define __DBUSMENU_CLIENT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "menuitem.h"
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_CLIENT (dbusmenu_client_get_type ())
+#define DBUSMENU_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClient))
+#define DBUSMENU_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass))
+#define DBUSMENU_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_CLIENT))
+#define DBUSMENU_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_CLIENT))
+#define DBUSMENU_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass))
+
+#define DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED "layout-updated"
+
+#define DBUSMENU_CLIENT_PROP_DBUS_NAME "dbus-name"
+#define DBUSMENU_CLIENT_PROP_DBUS_OBJECT "dbus-object"
+
+/**
+ DbusmenuClientClass:
+ @parent_class: #GObjectClass
+ @layout_updated: Slot for #DbusmenuClient::layout-updated.
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+
+ A simple class that takes all of the information from a
+ #DbusmenuServer over DBus and makes the same set of
+ #DbusmenuMenuitem objects appear on the other side.
+*/
+typedef struct _DbusmenuClientClass DbusmenuClientClass;
+struct _DbusmenuClientClass {
+ GObjectClass parent_class;
+
+ void (*layout_updated)(void);
+
+ /* Reserved for future use */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+};
+
+/**
+ DbusmenuClient:
+ @parent: #GObject.
+
+ The client for a #DbusmenuServer creating a shared
+ object set of #DbusmenuMenuitem objects.
+*/
+typedef struct _DbusmenuClient DbusmenuClient;
+struct _DbusmenuClient {
+ GObject parent;
+};
+
+GType dbusmenu_client_get_type (void);
+DbusmenuClient * dbusmenu_client_new (const gchar * name, const gchar * object);
+DbusmenuMenuitem * dbusmenu_client_get_root (DbusmenuClient * client);
+
+/**
+ SECTION:client
+ @short_description: The catcher of all the server traffic
+ @stability: Unstable
+ @include: libdbusmenu-glib/client.h
+
+ The client exists as a mirror to the server. For most folks
+ all they will do with a client is set it up to connect to
+ a server and then watch as the menu items on their side
+ of the bus change. This is all they should need to know about
+ the client, that it magically makes their menuitems dance.
+
+ It does this by setting up signal watchers and adjusting
+ the menuitems appropriately. Most users should watch the
+ menu items and the signal #DbusmenuClient::layout-changed for
+ larger events so that they can be optimized. It is possible
+ with that signal that even the root node would change. If
+ that doesn't happen the normal signals on the individual
+ nodes should be enough for most users.
+*/
+
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
new file mode 100644
index 0000000..7c41ac2
--- /dev/null
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+A library to allow applictions to provide simple indications of
+information to be displayed to users of the application through the
+interface shell.
+
+Copyright 2009 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/>
+-->
+<node name="/">
+ <interface name="org.freedesktop.dbusmenu">
+
+<!-- Properties -->
+ <property name="layout" type="s" access="read"/>
+
+<!-- Functions -->
+ <method name="GetProperty">
+ <arg type="u" name="id" direction="in" />
+ <arg type="s" name="property" direction="in" />
+ <arg type="s" name="value" direction="out" />
+ </method>
+ <method name="GetProperties">
+ <arg type="u" name="id" direction="in" />
+ <arg type="as" name="property" direction="in" />
+ <arg type="a(ss)" name="value" direction="out" />
+ </method>
+ <method name="Call">
+ <arg type="u" name="id" direction="in" />
+ </method>
+ <method name="ListProperties">
+ <arg type="u" name="id" direction="in" />
+ <arg type="as" name="properties" direction="out" />
+ </method>
+
+<!-- Signals -->
+ <signal name="IdPropUpdate">
+ <arg type="u" name="id" direction="out" />
+ <arg type="s" name="prop" direction="out" />
+ <arg type="s" name="value" direction="out" />
+ </signal>
+ <signal name="IdUpdate">
+ <arg type="u" name="id" direction="out" />
+ </signal>
+ <signal name="LayoutUpdate">
+ </signal>
+
+<!-- End of interesting stuff -->
+
+ </interface>
+</node>
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
new file mode 100644
index 0000000..0894d2c
--- /dev/null
+++ b/libdbusmenu-glib/menuitem.c
@@ -0,0 +1,424 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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.h"
+
+/* Private */
+/**
+ DbusmenuMenuitemPrivate:
+ @id: The ID of this menu item
+ @children: A list of #DbusmenuMenuitem objects that are
+ children to this one.
+
+ These are the little secrets that we don't want getting
+ out of data that we have. They can still be gotten using
+ accessor functions, but are protected appropriately.
+*/
+typedef struct _DbusmenuMenuitemPrivate DbusmenuMenuitemPrivate;
+struct _DbusmenuMenuitemPrivate
+{
+ guint id;
+ GList * children;
+};
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_ID,
+};
+
+#define DBUSMENU_MENUITEM_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemPrivate))
+
+/* Prototypes */
+static void dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass);
+static void dbusmenu_menuitem_init (DbusmenuMenuitem *self);
+static void dbusmenu_menuitem_dispose (GObject *object);
+static void dbusmenu_menuitem_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);
+
+/* GObject stuff */
+G_DEFINE_TYPE (DbusmenuMenuitem, dbusmenu_menuitem, G_TYPE_OBJECT);
+
+static void
+dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuMenuitemPrivate));
+
+ object_class->dispose = dbusmenu_menuitem_dispose;
+ object_class->finalize = dbusmenu_menuitem_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ g_object_class_install_property (object_class, PROP_ID,
+ g_param_spec_uint("id", "ID for the menu item",
+ "This is a unique indentifier for the menu item.",
+ 0, 30000, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ return;
+}
+
+static guint menuitem_next_id = 1;
+
+static void
+dbusmenu_menuitem_init (DbusmenuMenuitem *self)
+{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(self);
+
+ priv->id = 0;
+ priv->children = NULL;
+
+ return;
+}
+
+static void
+dbusmenu_menuitem_dispose (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_menuitem_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->finalize (object);
+ return;
+}
+
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_ID:
+ priv->id = g_value_get_uint(value);
+ if (priv->id > menuitem_next_id) {
+ menuitem_next_id = priv->id;
+ }
+ break;
+ }
+
+ return;
+}
+
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_ID:
+ if (priv->id == 0) {
+ priv->id = menuitem_next_id++;
+ }
+ g_value_set_uint(value, priv->id);
+ break;
+ }
+
+ return;
+}
+
+
+/* Public interface */
+
+/**
+ dbusmenu_menuitem_new:
+
+ Create a new #DbusmenuMenuitem with all default values.
+
+ Return value: A newly allocated #DbusmenuMenuitem.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_new (void)
+{
+ return g_object_new(DBUSMENU_TYPE_MENUITEM, NULL);
+}
+
+/**
+ dbusmenu_menuitem_new_with_id:
+ @id: ID to use for this menuitem
+
+ This creates a blank #DbusmenuMenuitem with a specific ID.
+
+ Return value: A newly allocated #DbusmenuMenuitem.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_new_with_id (guint id)
+{
+ DbusmenuMenuitem * mi = g_object_new(DBUSMENU_TYPE_MENUITEM, "id", id, NULL);
+ g_debug("New Menuitem id %d goal id %d", dbusmenu_menuitem_get_id(mi), id);
+ return mi;
+}
+
+/**
+ dbusmenu_menuitem_get_id:
+ @mi: The #DbusmenuMenuitem to query.
+
+ Gets the unique ID for @mi.
+
+ Return value: The ID of the @mi.
+*/
+guint
+dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), 0);
+
+ GValue retval = {0};
+ g_value_init(&retval, G_TYPE_UINT);
+ g_object_get_property(G_OBJECT(mi), "id", &retval);
+ return g_value_get_uint(&retval);
+}
+
+/**
+ dbusmenu_menuitem_get_children:
+ @mi: The #DbusmenuMenuitem to query.
+
+ Returns simply the list of children that this menu item
+ has. The list is valid until another child related function
+ is called, where it might be changed.
+
+ Return value: A #GList of pointers to #DbusmenuMenuitem objects.
+*/
+GList *
+dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ return priv->children;
+}
+
+/**
+ dbusmenu_menuitem_take_children:
+ @mi: The #DbusmenMenuitem to take the children from.
+
+ While the name sounds devious that's exactly what this function
+ does. It takes the list of children from the @mi and clears the
+ internal list. The calling function is no in charge of the ref's
+ on the children it has taken. A lot of responsibility involved
+ in taking children.
+
+ Return value: A #GList of pointers to #DbusmenuMenuitem objects.
+*/
+GList *
+dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ GList * children = priv->children;
+ priv->children = NULL;
+ return children;
+}
+
+/**
+ dbusmenu_menuitem_get_position:
+ @mi: The #DbusmenuMenuitem to find the position of
+ @parent: The #DbusmenuMenuitem who's children contain @mi
+
+ This function returns the position of the menu item @mi
+ in the children of @parent. It will return zero if the
+ menu item can't be found.
+
+ Return value: The position of @mi in the children of @parent.
+*/
+guint
+dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent)
+{
+ /* TODO: I'm not too happy returning zeros here. But that's all I've got */
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), 0);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(parent), 0);
+
+ GList * childs = dbusmenu_menuitem_get_children(parent);
+ if (childs == NULL) return 0;
+ guint count = 0;
+ for ( ; childs != NULL; childs = childs->next, count++) {
+ if (childs->data == mi) break;
+ }
+
+ if (childs == NULL) return 0;
+
+ return count;
+}
+
+/**
+ dbusmenu_menuitem_child_append:
+ @mi: The #DbusmenuMenuitem which will become a new parent
+ @child: The #DbusmenMenuitem that will be a child
+
+ This function adds @child to the list of children on @mi at
+ the end of that list.
+
+ Return value: Whether the child has been added successfully.
+*/
+gboolean
+dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->children = g_list_append(priv->children, child);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_delete:
+ @mi: The #DbusmenuMenuitem which has @child as a child
+ @child: The child #DbusmenuMenuitem that you want to no longer
+ be a child of @mi.
+
+ This function removes @child from the children list of @mi. It does
+ not call #g_object_unref on @child.
+
+ Return value: If we were able to delete @child.
+*/
+gboolean
+dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->children = g_list_remove(priv->children, child);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_add_position:
+ @mi: The #DbusmenuMenuitem that we're adding the child @child to.
+ @child: The #DbusmenuMenuitem to make a child of @mi.
+ @position: Where in @mi object's list of chidren @child should be placed.
+
+ Puts @child in the list of children for @mi at the location
+ specified in @position. If there is not enough entires available
+ then @child will be placed at the end of the list.
+
+ Return value: Whether @child was added successfully.
+*/
+gboolean
+dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->children = g_list_insert(priv->children, child, position);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_find:
+ @mi: The #DbusmenuMenuitem who's children to look on
+ @id: The ID of the child that we're looking for.
+
+ Search the children of @mi to find one with the ID of @id.
+ If it doesn't exist then we return #NULL.
+
+ Return value: The menu item with the ID @id or #NULL if it
+ can't be found.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+
+ GList * childs = dbusmenu_menuitem_get_children(mi);
+ if (childs == NULL) return NULL;
+
+ for ( ; childs == NULL; childs = g_list_next(childs)) {
+ DbusmenuMenuitem * lmi = DBUSMENU_MENUITEM(childs->data);
+ if (id == dbusmenu_menuitem_get_id(lmi)) {
+ return lmi;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean
+dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value)
+{
+
+ return FALSE;
+}
+
+const gchar *
+dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property)
+{
+
+ return NULL;
+}
+
+gboolean
+dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property)
+{
+
+ return FALSE;
+}
+
+/**
+ dbusmenu_menuitem_buildxml:
+ @mi: #DbusmenuMenuitem to represent in XML
+ @array: A list of string that will be turned into an XML file
+
+ This function will add strings to the array @array. It will put
+ at least one entry if this menu item has no children. If it has
+ children it will put two for this entry, one representing the
+ start tag and one that is a closing tag. It will allow it's
+ children to place their own tags in the array in between those two.
+*/
+void
+dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+
+ GList * children = dbusmenu_menuitem_get_children(mi);
+ if (children == NULL) {
+ g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\" />", dbusmenu_menuitem_get_id(mi)));
+ } else {
+ g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\">", dbusmenu_menuitem_get_id(mi)));
+ for ( ; children != NULL; children = children->next) {
+ dbusmenu_menuitem_buildxml(DBUSMENU_MENUITEM(children->data), array);
+ }
+ g_ptr_array_add(array, g_strdup("</menu>"));
+ }
+
+ return;
+}
+
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
new file mode 100644
index 0000000..a604e7a
--- /dev/null
+++ b/libdbusmenu-glib/menuitem.h
@@ -0,0 +1,135 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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_H__
+#define __DBUSMENU_MENUITEM_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_MENUITEM (dbusmenu_menuitem_get_type ())
+#define DBUSMENU_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitem))
+#define DBUSMENU_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass))
+#define DBUSMENU_IS_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_MENUITEM))
+#define DBUSMENU_IS_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM))
+#define DBUSMENU_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass))
+
+
+#define DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED "property-changed"
+#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
+
+/**
+ DbusmenuMenuitemClass:
+ @property_changed: Slot for #DbusmenuMenuitem::property-changed.
+ @item_activated: Slot for #DbusmenuMenuitem::item-activated.
+ @buildxml: Virtual function that appends the strings required
+ to represent this menu item in the menu XML file.
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+*/
+typedef struct _DbusmenuMenuitemClass DbusmenuMenuitemClass;
+struct _DbusmenuMenuitemClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*property_changed) (gchar * property);
+ void (*item_activated) (void);
+
+ /* Virtual functions */
+ void (*buildxml) (GPtrArray * stringarray);
+
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+};
+
+/**
+ DbusmenuMenuitem:
+
+ This is the #GObject based object that represents a menu
+ item. It gets created the same on both the client and
+ the server side and libdbusmenu-glib does the work of making
+ this object model appear on both sides of DBus. Simple
+ really, though through updates and people coming on and off
+ the bus it can lead to lots of fun complex scenarios.
+*/
+typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
+struct _DbusmenuMenuitem
+{
+ GObject parent;
+};
+
+GType dbusmenu_menuitem_get_type (void);
+
+DbusmenuMenuitem * dbusmenu_menuitem_new (void);
+DbusmenuMenuitem * dbusmenu_menuitem_new_with_id (guint id);
+guint dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi);
+
+GList * dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi);
+GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi);
+guint dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent);
+
+gboolean dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
+gboolean dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
+gboolean dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position);
+DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id);
+
+gboolean dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value);
+const gchar * dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property);
+gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property);
+
+void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array);
+
+/**
+ SECTION:menuitem
+ @short_description: A lowlevel represenation of a menuitem
+ @stability: Unstable
+ @include: libdbusmenu-glib/menuitem.h
+
+ A #DbusmenuMenuitem is the lowest level of represenation of a
+ single item in a menu. It gets created on the server side
+ and copied over to the client side where it gets rendered. As
+ the server starts to change it, and grow it, and do all kinds
+ of fun stuff that information is transfered over DBus and the
+ client updates it's understanding of the object model.
+
+ Most people using either the client or the server should be
+ able to deal mostly with #DbusmenuMenuitem objects. These
+ are simple, but then they can be attached to more complex
+ objects and handled appropriately.
+*/
+
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/server-marshal.list b/libdbusmenu-glib/server-marshal.list
new file mode 100644
index 0000000..950fc9d
--- /dev/null
+++ b/libdbusmenu-glib/server-marshal.list
@@ -0,0 +1 @@
+VOID: UINT, STRING, STRING
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
new file mode 100644
index 0000000..3db6db0
--- /dev/null
+++ b/libdbusmenu-glib/server.c
@@ -0,0 +1,332 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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 "server.h"
+#include "server-marshal.h"
+
+/* DBus Prototypes */
+static gboolean _dbusmenu_server_get_property (void);
+static gboolean _dbusmenu_server_get_properties (void);
+static gboolean _dbusmenu_server_call (void);
+static gboolean _dbusmenu_server_list_properties (void);
+
+#include "dbusmenu-server.h"
+
+/* Privates, I'll show you mine... */
+typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
+
+struct _DbusmenuServerPrivate
+{
+ DbusmenuMenuitem * root;
+ gchar * dbusobject;
+};
+
+#define DBUSMENU_SERVER_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate))
+
+/* Signals */
+enum {
+ ID_PROP_UPDATE,
+ ID_UPDATE,
+ LAYOUT_UPDATE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_DBUS_OBJECT,
+ PROP_ROOT_NODE,
+ PROP_LAYOUT
+};
+
+/* Prototype */
+static void dbusmenu_server_class_init (DbusmenuServerClass *class);
+static void dbusmenu_server_init (DbusmenuServer *self);
+static void dbusmenu_server_dispose (GObject *object);
+static void dbusmenu_server_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);
+
+G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
+
+static void
+dbusmenu_server_class_init (DbusmenuServerClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (DbusmenuServerPrivate));
+
+ object_class->dispose = dbusmenu_server_dispose;
+ object_class->finalize = dbusmenu_server_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ signals[ID_PROP_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE,
+ G_TYPE_FROM_CLASS(class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update),
+ NULL, NULL,
+ _dbusmenu_server_marshal_VOID__UINT_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+ signals[ID_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_UPDATE,
+ G_TYPE_FROM_CLASS(class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuServerClass, id_update),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals[LAYOUT_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE,
+ G_TYPE_FROM_CLASS(class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuServerClass, layout_update),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+
+ g_object_class_install_property (object_class, PROP_DBUS_OBJECT,
+ g_param_spec_string(DBUSMENU_SERVER_PROP_DBUS_OBJECT, "DBus object path",
+ "The object that represents this set of menus on DBus",
+ "/org/freedesktop/dbusmenu",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_ROOT_NODE,
+ g_param_spec_object(DBUSMENU_SERVER_PROP_ROOT_NODE, "Root menu node",
+ "The base object of the menus that are served",
+ DBUSMENU_TYPE_MENUITEM,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_LAYOUT,
+ g_param_spec_string(DBUSMENU_SERVER_PROP_LAYOUT, "XML Layout of the menus",
+ "A simple XML string that describes the layout of the menus",
+ "<menu />",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info);
+
+ return;
+}
+
+static void
+dbusmenu_server_init (DbusmenuServer *self)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(self);
+
+ priv->root = NULL;
+ priv->dbusobject = NULL;
+
+ return;
+}
+
+static void
+dbusmenu_server_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_server_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (dbusmenu_server_parent_class)->finalize (object);
+ return;
+}
+
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUS_OBJECT:
+ g_return_if_fail(priv->dbusobject == NULL);
+ priv->dbusobject = g_value_dup_string(value);
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ dbus_g_connection_register_g_object(connection,
+ priv->dbusobject,
+ obj);
+ break;
+ case PROP_ROOT_NODE:
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ }
+ priv->root = DBUSMENU_MENUITEM(g_value_get_object(value));
+ if (priv->root != NULL) {
+ g_object_ref(G_OBJECT(priv->root));
+ } else {
+ g_debug("Setting root node to NULL");
+ }
+ g_signal_emit(obj, signals[LAYOUT_UPDATE], 0, TRUE);
+ break;
+ case PROP_LAYOUT:
+ /* Can't set this, fall through to error */
+ g_warning("Can not set property: layout");
+ default:
+ g_return_if_reached();
+ break;
+ }
+
+ return;
+}
+
+static void
+xmlarray_foreach_free (gpointer arrayentry, gpointer userdata)
+{
+ if (arrayentry != NULL) {
+ /* g_debug("Freeing pointer: %s", (gchar *)arrayentry); */
+ g_free(arrayentry);
+ }
+
+ return;
+}
+
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
+
+ switch (id) {
+ case PROP_DBUS_OBJECT:
+ g_value_set_string(value, priv->dbusobject);
+ break;
+ case PROP_ROOT_NODE:
+ g_value_set_object(value, priv->root);
+ break;
+ case PROP_LAYOUT: {
+ GPtrArray * xmlarray = g_ptr_array_new();
+ if (priv->root == NULL) {
+ g_debug("Getting layout without root node!");
+ g_ptr_array_add(xmlarray, g_strdup("<menu />"));
+ } else {
+ dbusmenu_menuitem_buildxml(priv->root, xmlarray);
+ }
+ g_ptr_array_add(xmlarray, NULL);
+
+ /* build string */
+ gchar * finalstring = g_strjoinv("", (gchar **)xmlarray->pdata);
+ g_value_take_string(value, finalstring);
+ g_debug("Final string: %s", finalstring);
+
+ g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
+ g_ptr_array_free(xmlarray, TRUE);
+ break;
+ }
+ default:
+ g_return_if_reached();
+ break;
+ }
+
+ return;
+}
+
+/* DBus interface */
+static gboolean
+_dbusmenu_server_get_property (void)
+{
+
+ return TRUE;
+}
+
+static gboolean
+_dbusmenu_server_get_properties (void)
+{
+
+ return TRUE;
+}
+
+static gboolean
+_dbusmenu_server_call (void)
+{
+
+ return TRUE;
+}
+
+static gboolean
+_dbusmenu_server_list_properties (void)
+{
+
+ return TRUE;
+}
+
+/* Public Interface */
+/**
+ dbusmenu_server_new:
+ @object: The object name to show for this menu structure
+ on DBus. May be NULL.
+
+ Creates a new #DbusmenuServer object with a specific object
+ path on DBus. If @object is set to NULL the default object
+ name of "/org/freedesktop/dbusmenu" will be used.
+
+ Return value: A brand new #DbusmenuServer
+*/
+DbusmenuServer *
+dbusmenu_server_new (const gchar * object)
+{
+ if (object == NULL) {
+ object = "/org/freedesktop/dbusmenu";
+ }
+
+ DbusmenuServer * self = g_object_new(DBUSMENU_TYPE_SERVER,
+ DBUSMENU_SERVER_PROP_DBUS_OBJECT, object,
+ NULL);
+
+ return self;
+}
+
+/**
+ dbusmenu_server_set_root:
+ @self: The #DbusmenuServer object to set the root on
+ @root: The new root #DbusmenuMenuitem tree
+
+ This function contains all of the #GValue wrapping
+ required to set the property #DbusmenuServer:root-node
+ on the server @self.
+*/
+void
+dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(self));
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(root));
+
+ g_debug("Setting root object: 0x%X", (unsigned int)root);
+ GValue rootvalue = {0};
+ g_value_init(&rootvalue, G_TYPE_OBJECT);
+ g_value_set_object(&rootvalue, root);
+ g_object_set_property(G_OBJECT(self), DBUSMENU_SERVER_PROP_ROOT_NODE, &rootvalue);
+ return;
+}
+
+
+
diff --git a/libdbusmenu-glib/server.h b/libdbusmenu-glib/server.h
new file mode 100644
index 0000000..2c6c817
--- /dev/null
+++ b/libdbusmenu-glib/server.h
@@ -0,0 +1,120 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2009 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_SERVER_H__
+#define __DBUSMENU_SERVER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "menuitem.h"
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_SERVER (dbusmenu_server_get_type ())
+#define DBUSMENU_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServer))
+#define DBUSMENU_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_SERVER, DbusmenuServerClass))
+#define DBUSMENU_IS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_SERVER))
+#define DBUSMENU_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_SERVER))
+#define DBUSMENU_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServerClass))
+
+#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE "id-prop-update"
+#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE "id-update"
+#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE "layout-update"
+
+#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
+#define DBUSMENU_SERVER_PROP_ROOT_NODE "root-node"
+#define DBUSMENU_SERVER_PROP_LAYOUT "layout"
+
+/**
+ DbusmenuServerClass:
+ @parent_class: #GObjectClass
+ @id_prop_update: Slot for #DbusmenuServer::id-prop-update.
+ @id_update: Slot for #DbusmenuServer::id-update.
+ @layout_update: Slot for #DbusmenuServer::layout-update.
+ @dbusmenu_server_reserved1: Reserved for future use.
+ @dbusmenu_server_reserved2: Reserved for future use.
+ @dbusmenu_server_reserved3: Reserved for future use.
+ @dbusmenu_server_reserved4: Reserved for future use.
+
+ The class implementing the virtual functions for #DbusmenuServer.
+*/
+typedef struct _DbusmenuServerClass DbusmenuServerClass;
+struct _DbusmenuServerClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*id_prop_update)(guint id, gchar * property, gchar * value);
+ void (*id_update)(guint id);
+ void (*layout_update)(void);
+
+ /* Reserved */
+ void (*dbusmenu_server_reserved1)(void);
+ void (*dbusmenu_server_reserved2)(void);
+ void (*dbusmenu_server_reserved3)(void);
+ void (*dbusmenu_server_reserved4)(void);
+};
+
+/**
+ DbusmenuServer:
+ @parent: #GObject
+
+ A server which represents a sharing of a set of
+ #DbusmenuMenuitems across DBus to a #DbusmenuClient.
+*/
+typedef struct _DbusmenuServer DbusmenuServer;
+struct _DbusmenuServer {
+ GObject parent;
+};
+
+GType dbusmenu_server_get_type (void);
+DbusmenuServer * dbusmenu_server_new (const gchar * object);
+void dbusmenu_server_set_root (DbusmenuServer * server, DbusmenuMenuitem * root);
+
+/**
+ SECIONT:server
+ @short_description: The server signals changed and
+ updates on a tree of #DbusmenuMenuitem objecs.
+ @stability: Unstable
+ @include: libdbusmenu-glib/server.h
+
+ A #DbusmenuServer is the object that represents the local
+ tree of #DbusmenuMenuitem objects on DBus. It watches the
+ various signals that those objects emit and correctly
+ represents them across DBus to a #DbusmenuClient so that
+ the same tree can be maintained in another process.
+
+ The server needs to have the root set of #DbusmenuMenuitem
+ objects set via #dbusmenu_server_set_root but it will query
+ all of the objects in that tree automatically. After setting
+ the root there should be no other maintence required by
+ users of the server class.
+*/
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/test.c b/libdbusmenu-glib/test.c
deleted file mode 100644
index 8ebb3f7..0000000
--- a/libdbusmenu-glib/test.c
+++ /dev/null
@@ -1,4 +0,0 @@
-
-void mysymbol (void) {
- return;
-}
diff --git a/libdbusmenu-glib/test.h b/libdbusmenu-glib/test.h
deleted file mode 100644
index ad000af..0000000
--- a/libdbusmenu-glib/test.h
+++ /dev/null
@@ -1,2 +0,0 @@
-
-void mysymbol (void);