aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore8
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac14
-rw-r--r--libdbusmenu-glib/Makefile.am20
-rw-r--r--libdbusmenu-glib/client.c91
-rw-r--r--libdbusmenu-glib/dbus-menu.xml7
-rw-r--r--libdbusmenu-glib/menuitem-marshal.list3
-rw-r--r--libdbusmenu-glib/menuitem.c380
-rw-r--r--libdbusmenu-glib/menuitem.h53
-rw-r--r--libdbusmenu-glib/server.c196
-rw-r--r--libdbusmenu-gtk/Makefile.am9
-rw-r--r--libdbusmenu-gtk/menu.c304
-rw-r--r--libdbusmenu-gtk/menu.h74
-rw-r--r--libdbusmenu-gtk/test.c4
-rw-r--r--libdbusmenu-gtk/test.h2
-rw-r--r--tests/Makefile.am73
-rw-r--r--tests/glib-server-nomenu.c1
-rw-r--r--tests/test-glib-layout-server.c2
-rw-r--r--tests/test-glib-properties-client.c175
-rw-r--r--tests/test-glib-properties-server.c88
-rw-r--r--tests/test-glib-properties.h95
-rw-r--r--tests/test-gtk-label-client.c180
-rw-r--r--tests/test-gtk-label-server.c101
-rw-r--r--tests/test-gtk-label.h75
24 files changed, 1887 insertions, 70 deletions
diff --git a/.bzrignore b/.bzrignore
index 36595be..da9e9ce 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -28,3 +28,11 @@ libdbusmenu_glib_la-server-marshal.lo
glib-server-nomenu
test-glib-layout-server
test-glib-layout-client
+menuitem-marshal.c
+menuitem-marshal.h
+libdbusmenu_glib_la-menuitem-marshal.lo
+test-glib-properties-client
+test-glib-properties-server
+libdbusmenu_gtk_la-menu.lo
+test-gtk-label-client
+test-gtk-label-server
diff --git a/Makefile.am b/Makefile.am
index 6f3f6f1..2e22cf9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = libdbusmenu-glib libdbusmenu-gtk libdbusmenu-qt tests
+SUBDIRS = libdbusmenu-glib libdbusmenu-gtk libdbusmenu-qt tests po
diff --git a/configure.ac b/configure.ac
index 96b23fc..9770814 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,6 +35,20 @@ AC_SUBST(DBUSMENUGLIB_CFLAGS)
AC_SUBST(DBUSMENUGLIB_LIBS)
###########################
+# Dependencies - GTK
+###########################
+
+GTK_REQUIRED_VERSION=2.16
+
+PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-2.0 >= $GTK_REQUIRED_VERSION
+ glib-2.0 >= $GLIB_REQUIRED_VERSION
+ dbus-glib-1 >= $DBUS_REQUIRED_VERSION
+ libxml-2.0 >= $XML_REQUIRED_VERSION)
+
+AC_SUBST(DBUSMENUGTK_CFLAGS)
+AC_SUBST(DBUSMENUGTK_LIBS)
+
+###########################
# Lib versioning
###########################
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index b273555..e74b9ab 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -1,7 +1,9 @@
EXTRA_DIST = \
dbusmenu-glib.pc.in \
- dbus-menu.xml
+ dbus-menu.xml \
+ menuitem-marshal.list \
+ server-marshal.list
lib_LTLIBRARIES = \
libdbusmenu-glib.la
@@ -18,6 +20,8 @@ libdbusmenu_glib_la_SOURCES = \
dbusmenu-client.h \
menuitem.h \
menuitem.c \
+ menuitem-marshal.h \
+ menuitem-marshal.c \
server.h \
server.c \
server-marshal.h \
@@ -31,7 +35,7 @@ libdbusmenu_glib_la_LDFLAGS = \
-export-symbols-regex "^[^_].*"
libdbusmenu_glib_la_CFLAGS = \
- $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror -DG_DISABLE_DEPRECATED -DG_LOG_DOMAIN="\"LIBDBUSMENU-GLIB\""
libdbusmenu_glib_la_LIBADD = \
$(DBUSMENUGLIB_LIBS)
@@ -42,6 +46,8 @@ pkgconfigdir = $(libdir)/pkgconfig
BUILT_SOURCES = \
dbusmenu-client.h \
dbusmenu-server.h \
+ menuitem-marshal.h \
+ menuitem-marshal.c \
server-marshal.h \
server-marshal.c
@@ -69,3 +75,13 @@ server-marshal.c: $(srcdir)/server-marshal.list
--prefix=_dbusmenu_server_marshal $(srcdir)/server-marshal.list \
> server-marshal.c
+menuitem-marshal.h: $(srcdir)/menuitem-marshal.list
+ glib-genmarshal --header \
+ --prefix=_dbusmenu_menuitem_marshal $(srcdir)/menuitem-marshal.list \
+ > menuitem-marshal.h
+
+menuitem-marshal.c: $(srcdir)/menuitem-marshal.list
+ glib-genmarshal --body \
+ --prefix=_dbusmenu_menuitem_marshal $(srcdir)/menuitem-marshal.list \
+ > menuitem-marshal.c
+
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index b0b1157..61f8c34 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -35,6 +35,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include "client.h"
#include "dbusmenu-client.h"
+#include "server-marshal.h"
/* Properties */
enum {
@@ -77,9 +78,11 @@ static void set_property (GObject * obj, guint id, const GValue * value, GParamS
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
/* Private Funcs */
static void layout_update (DBusGProxy * proxy, DbusmenuClient * client);
+static void id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, DbusmenuClient * client);
+static void id_update (DBusGProxy * proxy, guint id, 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 DbusmenuMenuitem * parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy);
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);
@@ -166,6 +169,11 @@ dbusmenu_client_dispose (GObject *object)
}
priv->session_bus = NULL;
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_client_parent_class)->dispose (object);
return;
}
@@ -238,6 +246,36 @@ layout_update (DBusGProxy * proxy, DbusmenuClient * client)
return;
}
+/* Signal from the server that a property has changed
+ on one of our menuitems */
+static void
+id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gchar * value, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ g_return_if_fail(priv->root != NULL);
+
+ DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
+ g_return_if_fail(menuitem != NULL);
+
+ dbusmenu_menuitem_property_set(menuitem, property, value);
+ return;
+}
+
+/* Oh, lots of updates now. That silly server, they want
+ to change all kinds of stuff! */
+static void
+id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ g_return_if_fail(priv->root != NULL);
+
+ DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
+ g_return_if_fail(menuitem != NULL);
+
+ /* dbusmenu_menuitem_property_set(menuitem, property, value); */
+ return;
+}
+
/* When we have a name and an object, build the two proxies and get the
first version of the layout */
static void
@@ -281,6 +319,13 @@ build_proxies (DbusmenuClient * client)
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);
+ dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_STRING_STRING, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->menuproxy, "IdPropUpdate", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->menuproxy, "IdPropUpdate", G_CALLBACK(id_prop_update), client, NULL);
+
+ dbus_g_proxy_add_signal(priv->menuproxy, "IdUpdate", G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->menuproxy, "IdUpdate", G_CALLBACK(id_update), client, NULL);
+
return;
}
@@ -301,7 +346,7 @@ parse_node_get_id (xmlNodePtr node)
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);
+ /* g_debug ("Found ID: %d", id); */
return id;
}
break;
@@ -312,19 +357,42 @@ parse_node_get_id (xmlNodePtr node)
return 0;
}
+/* A small helper that calls _property_set on each hash table
+ entry in the properties hash. */
+static void
+get_properties_helper (gpointer key, gpointer value, gpointer data)
+{
+ dbusmenu_menuitem_property_set((DbusmenuMenuitem *)data, (gchar *)key, (gchar *)value);
+ return;
+}
+
+/* This is the callback for the properties on a menu item. There
+ should be all of them in the Hash, and they we use foreach to
+ copy them into the menuitem.
+ This isn't the most efficient way. We can optimize this by
+ somehow removing the foreach. But that is for later. */
+static void
+menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
+{
+ g_hash_table_foreach(properties, get_properties_helper, data);
+ g_hash_table_destroy(properties);
+ return;
+}
+
/* Parse recursively through the XML and make it into
objects as need be */
static DbusmenuMenuitem *
-parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent)
+parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
{
guint id = parse_node_get_id(node);
- g_debug("Looking at node with id: %d", id);
+ /* 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));
+ item = NULL;
}
if (id == 0) {
@@ -334,14 +402,17 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
/* Build a new item */
item = dbusmenu_menuitem_new_with_id(id);
+ /* Get the properties queued up for this item */
+ org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, item);
}
xmlNodePtr children;
guint position;
GList * oldchildren = dbusmenu_menuitem_take_children(item);
+ /* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
- g_debug("Looking at child: %d", position);
+ /* g_debug("Looking at child: %d", position); */
guint childid = parse_node_get_id(children);
DbusmenuMenuitem * childmi = NULL;
@@ -355,13 +426,15 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
}
}
- childmi = parse_layout_xml(children, childmi, item);
+ childmi = parse_layout_xml(children, childmi, item, proxy);
dbusmenu_menuitem_child_add_position(item, childmi, position);
}
+ /* g_debug("Stopping old children: %d", g_list_length(oldchildren)); */
GList * oldchildleft = NULL;
for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) {
DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data);
+ g_debug("Unref'ing menu item with layout update. ID: %d", dbusmenu_menuitem_get_id(oldmi));
g_object_unref(G_OBJECT(oldmi));
}
g_list_free(oldchildren);
@@ -382,7 +455,7 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
xmlNodePtr root = xmlDocGetRootElement(xmldoc);
- priv->root = parse_layout_xml(root, priv->root, NULL);
+ priv->root = parse_layout_xml(root, priv->root, NULL, priv->menuproxy);
if (priv->root == NULL) {
g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, layout);
}
@@ -409,11 +482,11 @@ update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
}
const gchar * xml = g_value_get_string(&value);
- g_debug("Got layout string: %s", xml);
+ /* 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_debug("Root is now: 0x%X", (unsigned int)priv->root); */
g_signal_emit(G_OBJECT(client), signals[LAYOUT_UPDATED], 0, TRUE);
return;
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 7c41ac2..51c529b 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -41,16 +41,11 @@ License version 3 and version 2.1 along with this program. If not, see
</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" />
+ <arg type="a{ss}" name="properties" 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">
diff --git a/libdbusmenu-glib/menuitem-marshal.list b/libdbusmenu-glib/menuitem-marshal.list
new file mode 100644
index 0000000..fc0318f
--- /dev/null
+++ b/libdbusmenu-glib/menuitem-marshal.list
@@ -0,0 +1,3 @@
+VOID: STRING, STRING
+VOID: OBJECT
+VOID: VOID
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index 0894d2c..95391a4 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -30,6 +30,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
#include "menuitem.h"
+#include "menuitem-marshal.h"
/* Private */
/**
@@ -37,6 +38,7 @@ License version 3 and version 2.1 along with this program. If not, see
@id: The ID of this menu item
@children: A list of #DbusmenuMenuitem objects that are
children to this one.
+ @properties: All of the properties on this menu item.
These are the little secrets that we don't want getting
out of data that we have. They can still be gotten using
@@ -47,8 +49,20 @@ struct _DbusmenuMenuitemPrivate
{
guint id;
GList * children;
+ GHashTable * properties;
};
+/* Signals */
+enum {
+ PROPERTY_CHANGED,
+ ITEM_ACTIVATED,
+ CHILD_ADDED,
+ CHILD_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
/* Properties */
enum {
PROP_0,
@@ -81,6 +95,69 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
object_class->set_property = set_property;
object_class->get_property = get_property;
+ /**
+ DbusmenuMenuitem::property-changed:
+ @arg0: The #DbusmenuMenuitem object.
+ @arg1: The name of the property that changed
+ @arg2: The new value of the property
+
+ Emitted everytime a property on a menuitem is either
+ updated or added.
+ */
+ signals[PROPERTY_CHANGED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, property_changed),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+ /**
+ DbusmenuMenuitem::item-activated:
+ @arg0: The #DbusmenuMenuitem object.
+
+ Emitted on the objects on the server side when
+ they are signaled on the client side.
+ */
+ signals[ITEM_ACTIVATED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, item_activated),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+ /**
+ DbusmenuMenuitem::child-added:
+ @arg0: The #DbusmenuMenuitem which is the parent.
+ @arg1: The #DbusmenuMenuitem which is the child.
+
+ Signaled when the child menuitem has been added to
+ the parent.
+ */
+ signals[CHILD_ADDED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_added),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ /**
+ DbusmenuMenuitem::child-removed:
+ @arg0: The #DbusmenuMenuitem which was the parent.
+ @arg1: The #DbusmenuMenuitem which was the child.
+
+ Signaled when the child menuitem has been requested to
+ be removed from the parent. This signal is called when
+ it has been removed from the list but not yet had
+ #g_object_unref called on it.
+ */
+ signals[CHILD_REMOVED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_removed),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
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.",
@@ -99,6 +176,8 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
priv->id = 0;
priv->children = NULL;
+
+ priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
return;
}
@@ -106,6 +185,14 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
static void
dbusmenu_menuitem_dispose (GObject *object)
{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(object);
+
+ GList * child = NULL;
+ for (child = priv->children; child != NULL; child = g_list_next(child)) {
+ g_object_unref(G_OBJECT(child->data));
+ }
+ g_list_free(priv->children);
+ priv->children = NULL;
G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object);
return;
@@ -114,6 +201,13 @@ dbusmenu_menuitem_dispose (GObject *object)
static void
dbusmenu_menuitem_finalize (GObject *object)
{
+ g_debug("Menuitem dying");
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(object);
+
+ if (priv->properties != NULL) {
+ g_hash_table_destroy(priv->properties);
+ priv->properties = NULL;
+ }
G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->finalize (object);
return;
@@ -181,7 +275,7 @@ 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);
+ /* g_debug("New Menuitem id %d goal id %d", dbusmenu_menuitem_get_id(mi), id); */
return mi;
}
@@ -223,6 +317,15 @@ dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi)
return priv->children;
}
+/* For all the taken children we need to signal
+ that they were removed */
+static void
+take_children_signal (gpointer data, gpointer user_data)
+{
+ g_signal_emit(G_OBJECT(user_data), signals[CHILD_REMOVED], 0, DBUSMENU_MENUITEM(data), TRUE);
+ return;
+}
+
/**
dbusmenu_menuitem_take_children:
@mi: The #DbusmenMenuitem to take the children from.
@@ -243,6 +346,7 @@ dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi)
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
GList * children = priv->children;
priv->children = NULL;
+ g_list_foreach(children, take_children_signal, mi);
return children;
}
@@ -294,6 +398,29 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_append(priv->children, child);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_prepend:
+ @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 beginning of that list.
+
+ Return value: Whether the child has been added successfully.
+*/
+gboolean
+dbusmenu_menuitem_child_prepend (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_prepend(priv->children, child);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
return TRUE;
}
@@ -316,6 +443,7 @@ dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_remove(priv->children, child);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_REMOVED], 0, child, TRUE);
return TRUE;
}
@@ -339,6 +467,32 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem *
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_insert(priv->children, child, position);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
+ return TRUE;
+}
+
+/**
+ dbusmenu_menuitem_child_reorder:
+ @base: The #DbusmenuMenuitem that has children needing realignment
+ @child: The #DbusmenuMenuitem that is a child needing to be moved
+ @position: The position in the list to place it in
+
+ This function moves a child on the list of children. It is
+ for a child that is already in the list, but simply needs a
+ new location.
+
+ Return value: Whether the move was successful.
+*/
+gboolean
+dbusmenu_menuitem_child_reorder(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_remove(priv->children, child);
+ priv->children = g_list_insert(priv->children, child, position);
+
return TRUE;
}
@@ -371,25 +525,192 @@ dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id)
return NULL;
}
+typedef struct {
+ DbusmenuMenuitem * mi;
+ guint id;
+} find_id_t;
+
+/* Basically the heart of the find_id that matches the
+ API of GFunc. Unfortunately, this goes through all the
+ children, but it rejects them quickly. */
+static void
+find_id_helper (gpointer in_mi, gpointer in_find_id)
+{
+ DbusmenuMenuitem * mi = (DbusmenuMenuitem *)in_mi;
+ find_id_t * find_id = (find_id_t *)in_find_id;
+
+ if (find_id->mi != NULL) return;
+ if (find_id->id == dbusmenu_menuitem_get_id(mi)) {
+ find_id->mi = mi;
+ return;
+ }
+
+ g_list_foreach(dbusmenu_menuitem_get_children(mi), find_id_helper, in_find_id);
+ return;
+}
+
+/**
+ dbusmenu_menuitem_find_id:
+ @mi: #DbusmenuMenuitem at the top of the tree to search
+ @id: ID of the #DbusmenuMenuitem to search for
+
+ This function searchs the whole tree of children that
+ are attached to @mi. This could be quite a few nodes, all
+ the way down the tree. It is a depth first search.
+
+ Return value: The #DbusmenuMenuitem with the ID of @id
+ or #NULL if there isn't such a menu item in the tree
+ represented by @mi.
+*/
+DbusmenuMenuitem *
+dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, guint id)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+ find_id_t find_id = {mi: NULL, id: id};
+ find_id_helper(mi, &find_id);
+ return find_id.mi;
+}
+
+/**
+ dbusmenu_menuitem_property_set:
+ @mi: The #DbusmenuMenuitem to set the property on.
+ @property: Name of the property to set.
+ @value: The value of the property.
+
+ Takes the pair of @property and @value and places them as a
+ property on @mi. If a property already exists by that name,
+ then the value is set to the new value. If not, the property
+ is added. If the value is changed or the property was previously
+ unset then the signal #DbusmenuMenuitem::prop-changed will be
+ emitted by this function.
+
+ Return value: A boolean representing if the property value was set.
+*/
gboolean
dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value)
{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(property != NULL, FALSE);
- return FALSE;
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ /* g_debug("Setting a property. ID: %d Prop: %s Value: %s", priv->id, property, value); */
+
+ gpointer lookup = g_hash_table_lookup(priv->properties, property);
+ if (g_strcmp0((gchar *)lookup, value) == 0) {
+ /* The value is the same as the value currently in the
+ table so we don't really care. Just say everything's okay */
+ return TRUE;
+ }
+
+ gchar * lprop = g_strdup(property);
+ gchar * lval = g_strdup(value);
+
+ g_hash_table_insert(priv->properties, lprop, lval);
+ g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, value, TRUE);
+
+ return TRUE;
}
+/**
+ dbusmenu_menuitem_property_get:
+ @mi: The #DbusmenuMenuitem to look for the property on.
+ @property: The property to grab.
+
+ Look up a property on @mi and return the value of it if
+ it exits. #NULL will be returned if the property doesn't
+ exist.
+
+ Return value: A string with the value of the property
+ that shouldn't be free'd. Or #NULL if the property
+ is not set.
+*/
const gchar *
dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property)
{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+ g_return_val_if_fail(property != NULL, NULL);
- return NULL;
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+
+ return (const gchar *)g_hash_table_lookup(priv->properties, property);
}
+/**
+ dbusmenu_menuitem_property_exit:
+ @mi: The #DbusmenuMenuitem to look for the property on.
+ @property: The property to look for.
+
+ Checkes to see if a particular property exists on @mi and
+ returns #TRUE if so.
+
+ Return value: A boolean checking to see if the property is available
+*/
gboolean
dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property)
{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(property != NULL, FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
- return FALSE;
+ gpointer value = g_hash_table_lookup(priv->properties, property);
+
+ return value != NULL;
+}
+
+/**
+ dbusmenu_menuitem_properties_list:
+ @mi: #DbusmenuMenuitem to list the properties on
+
+ This functiong gets a list of the names of all the properties
+ that are set on this menu item. This data on the list is owned
+ by the menuitem but the list is not and should be freed using
+ g_list_free() when the calling function is done with it.
+
+ Return value: A list of strings or NULL if there are none.
+*/
+GList *
+dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ return g_hash_table_get_keys(priv->properties);
+}
+
+static void
+copy_helper (gpointer in_key, gpointer in_value, gpointer in_data)
+{
+ GHashTable * table = (GHashTable *)in_data;
+ g_hash_table_insert(table, in_key, in_value);
+ return;
+}
+
+/**
+ dbusmenu_menuitem_properties_copy:
+ @mi: #DbusmenuMenuitem that we're interested in the properties of
+
+ This function takes the properties of a #DbusmenuMenuitem
+ and puts them into a #GHashTable that is referenced by the
+ key of a string and has the value of a string. The hash
+ table may not have any entries if there aren't any or there
+ is an error in processing. It is the caller's responsibility
+ to destroy the created #GHashTable.
+
+ Return value: A brand new #GHashTable that contains all of the
+ properties that are on this #DbusmenuMenuitem @mi.
+*/
+GHashTable *
+dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
+{
+ GHashTable * ret = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), ret);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ g_hash_table_foreach(priv->properties, copy_helper, ret);
+
+ return ret;
}
/**
@@ -422,3 +743,54 @@ dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array)
return;
}
+typedef struct {
+ void (*func) (DbusmenuMenuitem * mi, gpointer data);
+ gpointer data;
+} foreach_struct_t;
+
+static void
+foreach_helper (gpointer data, gpointer user_data)
+{
+ dbusmenu_menuitem_foreach(DBUSMENU_MENUITEM(data), ((foreach_struct_t *)user_data)->func, ((foreach_struct_t *)user_data)->data);
+ return;
+}
+
+/**
+ dbusmenu_menuitem_foreach:
+ @mi: The #DbusmenItem to start from
+ @func: Function to call on every node in the tree
+ @data: User data to pass to the function
+
+ This calls the function @func on this menu item and all
+ of the children of this item. And their children. And
+ their children. And... you get the point. It will get
+ called on the whole tree.
+*/
+void
+dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ g_return_if_fail(func != NULL);
+
+ func(mi, data);
+ GList * children = dbusmenu_menuitem_get_children(mi);
+ foreach_struct_t foreach_data = {func: func, data: data};
+ g_list_foreach(children, foreach_helper, &foreach_data);
+ return;
+}
+
+/**
+ dbusmenu_menuitem_activate:
+ @mi: The #DbusmenuMenuitem to send the signal on.
+
+ Emits the #DbusmenuMenuitem::item-activate signal on this
+ menu item. Called by server objects when they get the
+ appropriate DBus signals from the client.
+*/
+void
+dbusmenu_menuitem_activate (DbusmenuMenuitem * mi)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ g_signal_emit(G_OBJECT(mi), signals[ITEM_ACTIVATED], 0, TRUE);
+ return;
+}
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index a604e7a..c4fcf73 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -44,11 +44,31 @@ G_BEGIN_DECLS
#define DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED "property-changed"
#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
+#define DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED "child-added"
+#define DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED "child-removed"
+
+/**
+ 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;
+};
/**
DbusmenuMenuitemClass:
@property_changed: Slot for #DbusmenuMenuitem::property-changed.
@item_activated: Slot for #DbusmenuMenuitem::item-activated.
+ @child_added: Slot for #DbusmenuMenuitem::child-added.
+ @child_removed: Slot for #DbusmenuMenuitem::child-removed.
@buildxml: Virtual function that appends the strings required
to represent this menu item in the menu XML file.
@reserved1: Reserved for future use.
@@ -62,8 +82,10 @@ struct _DbusmenuMenuitemClass
GObjectClass parent_class;
/* Signals */
- void (*property_changed) (gchar * property);
+ void (*property_changed) (gchar * property, gchar * value);
void (*item_activated) (void);
+ void (*child_added) (DbusmenuMenuitem * child);
+ void (*child_removed) (DbusmenuMenuitem * child);
/* Virtual functions */
void (*buildxml) (GPtrArray * stringarray);
@@ -74,42 +96,33 @@ struct _DbusmenuMenuitemClass
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);
+DbusmenuMenuitem * dbusmenu_menuitem_new (void) G_GNUC_WARN_UNUSED_RESULT;
+DbusmenuMenuitem * dbusmenu_menuitem_new_with_id (guint id) G_GNUC_WARN_UNUSED_RESULT;
guint dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi);
GList * dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi);
-GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi);
+GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUSED_RESULT;
guint dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent);
gboolean dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child);
+gboolean dbusmenu_menuitem_child_prepend (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);
+gboolean dbusmenu_menuitem_child_reorder (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position);
DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, guint id);
+DbusmenuMenuitem * dbusmenu_menuitem_find_id (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);
+GList * dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUSED_RESULT;
+GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi);
void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array);
+void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data);
+void dbusmenu_menuitem_activate (DbusmenuMenuitem * mi);
/**
SECTION:menuitem
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 3db6db0..cf8ea85 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -34,10 +34,9 @@ License version 3 and version 2.1 along with this program. If not, see
#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);
+static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, guint id, gchar * property, gchar ** value, GError ** error);
+static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GHashTable ** dict, GError ** error);
+static gboolean _dbusmenu_server_call (DbusmenuServer * server, guint id, GError ** error);
#include "dbusmenu-server.h"
@@ -71,6 +70,14 @@ enum {
PROP_LAYOUT
};
+/* Errors */
+enum {
+ INVALID_MENUITEM_ID,
+ INVALID_PROPERTY_NAME,
+ UNKNOWN_DBUS_ERROR,
+ LAST_ERROR
+};
+
/* Prototype */
static void dbusmenu_server_class_init (DbusmenuServerClass *class);
static void dbusmenu_server_init (DbusmenuServer *self);
@@ -78,6 +85,12 @@ 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);
+static void menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * value, DbusmenuServer * server);
+static void menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
+static void menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server);
+static void menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data);
+static void menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data);
+static GQuark error_quark (void);
G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
@@ -93,6 +106,16 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
object_class->set_property = set_property;
object_class->get_property = get_property;
+ /**
+ DbusmenuServer::id-prop-update:
+ @arg0: The #DbusmenuServer emitting the signal.
+ @arg1: The ID of the #DbusmenuMenuitem changing a property.
+ @arg2: The property being changed.
+ @arg3: The value of the property being changed.
+
+ This signal is emitted when a menuitem updates or
+ adds a property.
+ */
signals[ID_PROP_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE,
G_TYPE_FROM_CLASS(class),
G_SIGNAL_RUN_LAST,
@@ -100,6 +123,15 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
NULL, NULL,
_dbusmenu_server_marshal_VOID__UINT_STRING_STRING,
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+ /**
+ DbusmenuServer::id-update:
+ @arg0: The #DbusmenuServer emitting the signal.
+ @arg1: ID of the #DbusmenuMenuitem changing.
+
+ The purpose of this signal is to show major change in
+ a menuitem to the point that #DbusmenuServer::id-prop-update
+ seems a little insubstantive.
+ */
signals[ID_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_ID_UPDATE,
G_TYPE_FROM_CLASS(class),
G_SIGNAL_RUN_LAST,
@@ -107,6 +139,13 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT);
+ /**
+ DbusmenuServer::layout-update:
+ @arg0: The #DbusmenuServer emitting the signal.
+
+ This signal is emitted any time the layout of the
+ menuitems under this server is changed.
+ */
signals[LAYOUT_UPDATE] = g_signal_new(DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE,
G_TYPE_FROM_CLASS(class),
G_SIGNAL_RUN_LAST,
@@ -151,6 +190,13 @@ dbusmenu_server_init (DbusmenuServer *self)
static void
dbusmenu_server_dispose (GObject *object)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(object);
+
+ if (priv->root != NULL) {
+ dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, object);
+ g_object_unref(priv->root);
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object);
return;
}
@@ -178,12 +224,14 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
break;
case PROP_ROOT_NODE:
if (priv->root != NULL) {
+ dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, obj);
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));
+ dbusmenu_menuitem_foreach(priv->root, menuitem_signals_create, obj);
} else {
g_debug("Setting root node to NULL");
}
@@ -226,7 +274,7 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
case PROP_LAYOUT: {
GPtrArray * xmlarray = g_ptr_array_new();
if (priv->root == NULL) {
- g_debug("Getting layout without root node!");
+ /* g_debug("Getting layout without root node!"); */
g_ptr_array_add(xmlarray, g_strdup("<menu />"));
} else {
dbusmenu_menuitem_buildxml(priv->root, xmlarray);
@@ -236,7 +284,7 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
/* build string */
gchar * finalstring = g_strjoinv("", (gchar **)xmlarray->pdata);
g_value_take_string(value, finalstring);
- g_debug("Final string: %s", finalstring);
+ /* g_debug("Final string: %s", finalstring); */
g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
g_ptr_array_free(xmlarray, TRUE);
@@ -250,32 +298,149 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
return;
}
-/* DBus interface */
-static gboolean
-_dbusmenu_server_get_property (void)
+static void
+menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * value, DbusmenuServer * server)
{
+ g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, value, TRUE);
+ return;
+}
- return TRUE;
+static void
+menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
+{
+ menuitem_signals_create(child, server);
+ /* TODO: We probably need to group the layout update signals to make the number more reasonble. */
+ g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, TRUE);
+ return;
}
-static gboolean
-_dbusmenu_server_get_properties (void)
+static void
+menuitem_child_removed (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
{
+ menuitem_signals_remove(child, server);
+ /* TODO: We probably need to group the layout update signals to make the number more reasonble. */
+ g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATE], 0, TRUE);
+ return;
+}
+
+/* Connects all the signals that we're interested in
+ coming from a menuitem */
+static void
+menuitem_signals_create (DbusmenuMenuitem * mi, gpointer data)
+{
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(menuitem_child_added), data);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(menuitem_child_removed), data);
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menuitem_property_changed), data);
+ return;
+}
+
+/* Removes all the signals that we're interested in
+ coming from a menuitem */
+static void
+menuitem_signals_remove (DbusmenuMenuitem * mi, gpointer data)
+{
+ g_signal_handlers_disconnect_by_func(G_OBJECT(mi), G_CALLBACK(menuitem_child_added), data);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(mi), G_CALLBACK(menuitem_child_removed), data);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(mi), G_CALLBACK(menuitem_property_changed), data);
+ return;
+}
+
+static GQuark
+error_quark (void)
+{
+ static GQuark quark = 0;
+ if (quark == 0) {
+ quark = g_quark_from_static_string (G_LOG_DOMAIN);
+ }
+ return quark;
+}
+
+/* DBus interface */
+static gboolean
+_dbusmenu_server_get_property (DbusmenuServer * server, guint id, gchar * property, gchar ** value, GError ** error)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (mi == NULL) {
+ if (error != NULL) {
+ g_set_error(error,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ }
+ return FALSE;
+ }
+
+ const gchar * prop = dbusmenu_menuitem_property_get(mi, property);
+ if (prop == NULL) {
+ if (error != NULL) {
+ g_set_error(error,
+ error_quark(),
+ INVALID_PROPERTY_NAME,
+ "The property '%s' does not exist on menuitem with ID of %d",
+ property,
+ id);
+ }
+ return FALSE;
+ }
+
+ if (value == NULL) {
+ if (error != NULL) {
+ g_set_error(error,
+ error_quark(),
+ UNKNOWN_DBUS_ERROR,
+ "Uhm, yeah. We didn't get anywhere to put the value, that's really weird. Seems impossible really.");
+ }
+ return FALSE;
+ }
+
+ *value = g_strdup(prop);
return TRUE;
}
static gboolean
-_dbusmenu_server_call (void)
+_dbusmenu_server_get_properties (DbusmenuServer * server, guint id, GHashTable ** dict, GError ** error)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (mi == NULL) {
+ if (error != NULL) {
+ g_set_error(error,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ }
+ return FALSE;
+ }
+
+ *dict = dbusmenu_menuitem_properties_copy(mi);
return TRUE;
}
static gboolean
-_dbusmenu_server_list_properties (void)
+_dbusmenu_server_call (DbusmenuServer * server, guint id, GError ** error)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (mi == NULL) {
+ if (error != NULL) {
+ g_set_error(error,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ }
+ return FALSE;
+ }
+ dbusmenu_menuitem_activate(mi);
return TRUE;
}
@@ -320,11 +485,12 @@ 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);
+ /* 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);
+ g_object_unref(G_OBJECT(root));
return;
}
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index 1e36228..80e6058 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -8,10 +8,10 @@ lib_LTLIBRARIES = \
libdbusmenu_gtkincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-gtk/
libdbusmenu_gtkinclude_HEADERS = \
- test.h
+ menu.h
libdbusmenu_gtk_la_SOURCES = \
- test.c
+ menu.c
libdbusmenu_gtk_la_LDFLAGS = \
-version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
@@ -19,10 +19,11 @@ libdbusmenu_gtk_la_LDFLAGS = \
-export-symbols-regex "^[^_].*"
libdbusmenu_gtk_la_CFLAGS = \
- $(LIBDBUSMENU_GTK_CFLAGS)
+ $(DBUSMENUGTK_CFLAGS) -Wall -Werror
libdbusmenu_gtk_la_LIBADD = \
- $(LIBDBUSMENU_GTK_LIBS)
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ $(DBUSMENUGTK_LIBS)
pkgconfig_DATA = dbusmenu-gtk.pc
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/libdbusmenu-gtk/menu.c b/libdbusmenu-gtk/menu.c
new file mode 100644
index 0000000..dd2df37
--- /dev/null
+++ b/libdbusmenu-gtk/menu.c
@@ -0,0 +1,304 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "menu.h"
+#include "libdbusmenu-glib/client.h"
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_DBUSOBJECT,
+ PROP_DBUSNAME
+};
+
+/* Private */
+typedef struct _DbusmenuGtkMenuPrivate DbusmenuGtkMenuPrivate;
+struct _DbusmenuGtkMenuPrivate {
+ DbusmenuClient * client;
+
+ gchar * dbus_object;
+ gchar * dbus_name;
+};
+
+#define DBUSMENU_GTKMENU_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuPrivate))
+
+/* Prototypes */
+static void dbusmenu_gtkmenu_class_init (DbusmenuGtkMenuClass *klass);
+static void dbusmenu_gtkmenu_init (DbusmenuGtkMenu *self);
+static void dbusmenu_gtkmenu_dispose (GObject *object);
+static void dbusmenu_gtkmenu_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);
+/* Internal */
+static void build_client (DbusmenuGtkMenu * self);
+
+/* GObject Stuff */
+G_DEFINE_TYPE (DbusmenuGtkMenu, dbusmenu_gtkmenu, GTK_TYPE_MENU);
+
+static void
+dbusmenu_gtkmenu_class_init (DbusmenuGtkMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuGtkMenuPrivate));
+
+ object_class->dispose = dbusmenu_gtkmenu_dispose;
+ object_class->finalize = dbusmenu_gtkmenu_finalize;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ 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_gtkmenu_init (DbusmenuGtkMenu *self)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(self);
+
+ priv->client = NULL;
+
+ priv->dbus_object = NULL;
+ priv->dbus_name = NULL;
+
+ return;
+}
+
+static void
+dbusmenu_gtkmenu_dispose (GObject *object)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+
+ if (priv->client != NULL) {
+ g_object_unref(G_OBJECT(priv->client));
+ priv->client = NULL;
+ }
+
+ G_OBJECT_CLASS (dbusmenu_gtkmenu_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_gtkmenu_finalize (GObject *object)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+
+ g_free(priv->dbus_object);
+ priv->dbus_object = NULL;
+
+ g_free(priv->dbus_name);
+ priv->dbus_name = NULL;
+
+ G_OBJECT_CLASS (dbusmenu_gtkmenu_parent_class)->finalize (object);
+ return;
+}
+
+static void
+set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_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_client(DBUSMENU_GTKMENU(obj));
+ }
+ break;
+ case PROP_DBUSOBJECT:
+ priv->dbus_object = g_value_dup_string(value);
+ if (priv->dbus_name != NULL && priv->dbus_object != NULL) {
+ build_client(DBUSMENU_GTKMENU(obj));
+ }
+ break;
+ default:
+ g_warning("Unknown property %d.", id);
+ return;
+ }
+
+ return;
+}
+
+static void
+get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_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 Functions */
+
+static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem";
+static const gchar * data_menu = "dbusmenugtk-data-gtkmenu";
+
+static gboolean
+menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
+{
+ dbusmenu_menuitem_activate(mi);
+ return TRUE;
+}
+
+static void
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, gchar * value, GtkMenuItem * gmi)
+{
+ if (!g_strcmp0(prop, "label")) {
+ gtk_menu_item_set_label(gmi, value);
+ gtk_widget_show(GTK_WIDGET(gmi));
+ }
+
+ return;
+}
+
+static void
+destoryed_dbusmenuitem_cb (gpointer udata, GObject * dbusmenuitem)
+{
+ /* g_debug("DbusmenuMenuitem was destroyed"); */
+ gtk_widget_destroy(GTK_WIDGET(udata));
+ return;
+}
+
+static void
+connect_menuitem (DbusmenuMenuitem * mi, GtkMenuItem * gmi)
+{
+ g_object_set_data(G_OBJECT(mi), data_menuitem, gmi);
+
+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi);
+ g_signal_connect(G_OBJECT(gmi), "activate", G_CALLBACK(menu_pressed_cb), mi);
+
+ g_object_weak_ref(G_OBJECT(mi), destoryed_dbusmenuitem_cb, gmi);
+
+ return;
+}
+
+static void
+process_dbusmenu_menuitem (DbusmenuMenuitem * mi, GtkMenu * parentmenu)
+{
+ gpointer unknown_menuitem = g_object_get_data(G_OBJECT(mi), data_menuitem);
+ if (unknown_menuitem == NULL) {
+ /* Oh, a virgin DbusmenuMenuitem, let's fix that. */
+ GtkWidget * menuitem = gtk_menu_item_new();
+ connect_menuitem(mi, GTK_MENU_ITEM(menuitem));
+ unknown_menuitem = menuitem;
+ gtk_menu_shell_append(GTK_MENU_SHELL(parentmenu), menuitem);
+ }
+
+ GList * children = dbusmenu_menuitem_get_children(mi);
+ if (children == NULL) {
+ /* If there are no children to process we are
+ done and we can move along */
+ return;
+ }
+
+ /* Phase 0: Make a submenu if we don't have one */
+ gpointer unknown_menu = g_object_get_data(G_OBJECT(mi), data_menu);
+ if (unknown_menu == NULL) {
+ GtkWidget * gtkmenu = gtk_menu_new();
+ g_object_ref(gtkmenu);
+ g_object_set_data_full(G_OBJECT(mi), data_menu, gtkmenu, g_object_unref);
+ unknown_menu = gtkmenu;
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(unknown_menuitem), gtkmenu);
+ gtk_widget_show(gtkmenu);
+ }
+
+ /* Phase 1: Add missing children */
+ GList * child = NULL;
+ for (child = children; child != NULL; child = g_list_next(child)) {
+ process_dbusmenu_menuitem(DBUSMENU_MENUITEM(child->data), GTK_MENU(unknown_menu));
+ }
+
+ /* Phase 2: Delete removed children */
+ /* Actually, we don't need to do this because of the weak
+ reference that we've added above. When the DbusmenuMenuitem
+ gets destroyed it takes its GtkMenuItem with it. Bye bye. */
+
+ /* Phase 3: Profit! */
+ return;
+}
+
+/* Processing the layout being updated and handling
+ that and making it into a menu */
+static void
+process_layout_change (DbusmenuClient * client, DbusmenuGtkMenu * gtkmenu)
+{
+ DbusmenuMenuitem * root = dbusmenu_client_get_root(client);
+
+ GList * children = dbusmenu_menuitem_get_children(root);
+ if (children == NULL) {
+ return;
+ }
+
+ GList * child = NULL;
+ for (child = children; child != NULL; child = g_list_next(child)) {
+ process_dbusmenu_menuitem(DBUSMENU_MENUITEM(child->data), GTK_MENU(gtkmenu));
+ }
+
+ return;
+}
+
+
+/* Builds the client and connects all of the signals
+ up for it so that it's happy-happy */
+static void
+build_client (DbusmenuGtkMenu * self)
+{
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(self);
+
+ if (priv->client == NULL) {
+ priv->client = dbusmenu_client_new(priv->dbus_name, priv->dbus_object);
+
+ /* Register for layout changes, this should come after the
+ creation of the client pulls it from DBus */
+ g_signal_connect(G_OBJECT(priv->client), DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, G_CALLBACK(process_layout_change), self);
+ }
+
+ return;
+}
+
+/* Public API */
+
+/**
+ dbusmenu_gtkmenu_new:
+ @dbus_name: Name of the #DbusmenuServer on DBus
+ @dbus_name: Name of the object on the #DbusmenuServer
+
+ Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
+ that connects across DBus to a #DbusmenuServer.
+
+ Return value: A new #DbusmenuGtkMenu sync'd with a server
+*/
+DbusmenuGtkMenu *
+dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
+{
+ return g_object_new(DBUSMENU_GTKMENU_TYPE,
+ DBUSMENU_CLIENT_PROP_DBUS_OBJECT, dbus_object,
+ DBUSMENU_CLIENT_PROP_DBUS_NAME, dbus_name,
+ NULL);
+}
+
diff --git a/libdbusmenu-gtk/menu.h b/libdbusmenu-gtk/menu.h
new file mode 100644
index 0000000..dd5bdd1
--- /dev/null
+++ b/libdbusmenu-gtk/menu.h
@@ -0,0 +1,74 @@
+#ifndef __DBUSMENU_GTKMENU_H__
+#define __DBUSMENU_GTKMENU_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_GTKMENU_TYPE (dbusmenu_gtkmenu_get_type ())
+#define DBUSMENU_GTKMENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenu))
+#define DBUSMENU_GTKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuClass))
+#define DBUSMENU_IS_GTKMENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_GTKMENU_TYPE))
+#define DBUSMENU_IS_GTKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_GTKMENU_TYPE))
+#define DBUSMENU_GTKMENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_GTKMENU_TYPE, DbusmenuGtkMenuClass))
+
+/**
+ DbusmenuGtkMenuClass:
+ @parent_class: #GtkMenuClass
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+*/
+typedef struct _DbusmenuGtkMenuClass DbusmenuGtkMenuClass;
+struct _DbusmenuGtkMenuClass {
+ GtkMenuClass parent_class;
+
+ /* Reserved */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+};
+
+/**
+ DbusmenuGtkMenu:
+ @parent: #GtkMenu
+*/
+typedef struct _DbusmenuGtkMenu DbusmenuGtkMenu;
+struct _DbusmenuGtkMenu {
+ GtkMenu parent;
+};
+
+GType dbusmenu_gtkmenu_get_type (void);
+DbusmenuGtkMenu * dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object);
+
+/**
+ SECTION:gtkmenu
+ @short_description: A GTK Menu Object that syncronizes over DBus
+ @stability: Unstable
+ @include: libdbusmenu-gtk/menu.h
+
+ In general, this is just a #GtkMenu, why else would you care? Oh,
+ because this menu is created by someone else on a server that exists
+ on the other side of DBus. You need a #DbusmenuServer to be able
+ push the data into this menu.
+
+ The first thing you need to know is how to find that #DbusmenuServer
+ on DBus. This involves both the DBus name and the DBus object that
+ the menu interface can be found on. Those two value should be set
+ when creating the object using dbusmenu_gtkmenu_new(). They are then
+ stored on two properties #DbusmenuGtkMenu:dbus-name and #DbusmenuGtkMenu:dbus-object.
+
+ After creation the #DbusmenuGtkMenu it will continue to keep in
+ synchronization with the #DbusmenuServer object across Dbus. If the
+ number of entries change, the menus change, if they change thier
+ properties change, they update in the items. All of this should
+ be handled transparently to the user of this object.
+
+ TODO: Document properties.
+*/
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-gtk/test.c b/libdbusmenu-gtk/test.c
deleted file mode 100644
index 8ebb3f7..0000000
--- a/libdbusmenu-gtk/test.c
+++ /dev/null
@@ -1,4 +0,0 @@
-
-void mysymbol (void) {
- return;
-}
diff --git a/libdbusmenu-gtk/test.h b/libdbusmenu-gtk/test.h
deleted file mode 100644
index ad000af..0000000
--- a/libdbusmenu-gtk/test.h
+++ /dev/null
@@ -1,2 +0,0 @@
-
-void mysymbol (void);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ca0bd77..70750ac 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,12 +2,16 @@ check: tests
DBUS_RUNNER=dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf
-tests: test-glib-layout
+tests: test-glib-layout test-glib-properties test-gtk-label
libexec_PROGRAMS = \
glib-server-nomenu \
test-glib-layout-client \
- test-glib-layout-server
+ test-glib-layout-server \
+ test-glib-properties-client \
+ test-glib-properties-server \
+ test-gtk-label-client \
+ test-gtk-label-server
glib_server_nomenu_SOURCES = \
glib-server-nomenu.c
@@ -20,10 +24,13 @@ glib_server_nomenu_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
$(DBUSMENUGLIB_LIBS)
+
+
test-glib-layout: test-glib-layout-client test-glib-layout-server
$(DBUS_RUNNER) --task ./test-glib-layout-client --task-name Client --task ./test-glib-layout-server --task-name Server --ignore-return
test_glib_layout_server_SOURCES = \
+ test-glib-layout.h \
test-glib-layout-server.c
test_glib_layout_server_CFLAGS = \
@@ -35,6 +42,7 @@ test_glib_layout_server_LDADD = \
$(DBUSMENUGLIB_LIBS)
test_glib_layout_client_SOURCES = \
+ test-glib-layout.h \
test-glib-layout-client.c
test_glib_layout_client_CFLAGS = \
@@ -47,6 +55,67 @@ test_glib_layout_client_LDADD = \
+test-glib-properties: test-glib-properties-client test-glib-properties-server
+ $(DBUS_RUNNER) --task ./test-glib-properties-client --task-name Client --task ./test-glib-properties-server --task-name Server --ignore-return
+
+test_glib_properties_server_SOURCES = \
+ test-glib-properties.h \
+ test-glib-properties-server.c
+
+test_glib_properties_server_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_properties_server_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ $(DBUSMENUGLIB_LIBS)
+
+test_glib_properties_client_SOURCES = \
+ test-glib-properties.h \
+ test-glib-properties-client.c
+
+test_glib_properties_client_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_glib_properties_client_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ $(DBUSMENUGLIB_LIBS)
+
+
+
+test-gtk-label: test-gtk-label-client test-gtk-label-server
+ $(DBUS_RUNNER) --task ./test-gtk-label-client --task-name Client --task ./test-gtk-label-server --task-name Server --ignore-return
+
+test_gtk_label_server_SOURCES = \
+ test-gtk-label.h \
+ test-gtk-label-server.c
+
+test_gtk_label_server_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_label_server_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS)
+
+test_gtk_label_client_SOURCES = \
+ test-gtk-label.h \
+ test-gtk-label-client.c
+
+test_gtk_label_client_CFLAGS = \
+ -I $(srcdir)/.. \
+ $(DBUSMENUGTK_CFLAGS) \
+ $(DBUSMENUGLIB_CFLAGS) -Wall -Werror
+
+test_gtk_label_client_LDADD = \
+ ../libdbusmenu-glib/libdbusmenu-glib.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ $(DBUSMENUGTK_LIBS)
+
+
examplesdir = $(docdir)/examples/
diff --git a/tests/glib-server-nomenu.c b/tests/glib-server-nomenu.c
index e5ea159..fb2c61e 100644
--- a/tests/glib-server-nomenu.c
+++ b/tests/glib-server-nomenu.c
@@ -31,6 +31,7 @@ main (int argc, char ** argv)
DbusmenuServer * server = dbusmenu_server_new("/org/test");
DbusmenuMenuitem * menuitem = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(menuitem, "test", "test");
dbusmenu_server_set_root(server, menuitem);
g_main_loop_run(g_main_loop_new(NULL, FALSE));
diff --git a/tests/test-glib-layout-server.c b/tests/test-glib-layout-server.c
index e69c6b2..cc9b8e7 100644
--- a/tests/test-glib-layout-server.c
+++ b/tests/test-glib-layout-server.c
@@ -48,7 +48,7 @@ layout2menuitem (layout_t * layout)
}
}
- g_debug("Layout to menu return: 0x%X", (unsigned int)local);
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
return local;
}
diff --git a/tests/test-glib-properties-client.c b/tests/test-glib-properties-client.c
new file mode 100644
index 0000000..4439788
--- /dev/null
+++ b/tests/test-glib-properties-client.c
@@ -0,0 +1,175 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+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 the GNU General Public License version 3, 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 GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+
+#include <libdbusmenu-glib/client.h>
+#include <libdbusmenu-glib/menuitem.h>
+
+#include "test-glib-properties.h"
+
+static guint layouton = 0;
+static GMainLoop * mainloop = NULL;
+static gboolean passed = TRUE;
+static guint death_timer = 0;
+
+static gboolean
+verify_props (DbusmenuMenuitem * mi, gchar ** properties)
+{
+ if (properties == NULL) {
+ return TRUE;
+ }
+
+ /* Verify they're all there and correct */
+ guint i;
+ for (i = 0; properties[i] != NULL; i += 2) {
+ const gchar * value = dbusmenu_menuitem_property_get(mi, properties[i]);
+ if (g_strcmp0(value, properties[i + 1])) {
+ g_debug("\tFailed as property '%s' should be '%s' and is '%s'", properties[i], properties[i+1], value);
+ return FALSE;
+ }
+ }
+
+ /* Verify that we don't have any extras */
+ // GList * props = dbusmenu_menuitem_properties_list(mi);
+
+ return TRUE;
+}
+
+static gboolean
+verify_root_to_layout(DbusmenuMenuitem * mi, proplayout_t * layout)
+{
+ g_debug("Verifying ID: %d", layout->id);
+
+ if (layout->id != dbusmenu_menuitem_get_id(mi)) {
+ g_debug("\tFailed as ID %d is not equal to %d", layout->id, dbusmenu_menuitem_get_id(mi));
+ return FALSE;
+ }
+
+ if (!verify_props(mi, layout->properties)) {
+ g_debug("\tFailed as unable to verify properties.");
+ return FALSE;
+ }
+
+ GList * children = dbusmenu_menuitem_get_children(mi);
+
+ if (children == NULL && layout->submenu == NULL) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+ if (children == NULL || layout->submenu == NULL) {
+ if (children == NULL) {
+ g_debug("\tFailed as there are no children but we have submenus");
+ } else {
+ g_debug("\tFailed as we have children but no submenu");
+ }
+ return FALSE;
+ }
+
+ guint i = 0;
+ for (i = 0; children != NULL && layout->submenu[i].id != 0; children = g_list_next(children), i++) {
+ if (!verify_root_to_layout(DBUSMENU_MENUITEM(children->data), &layout->submenu[i])) {
+ return FALSE;
+ }
+ }
+
+ if (children == NULL && layout->submenu[i].id == 0) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+
+ if (children != NULL) {
+ g_debug("\tFailed as there are still children but no submenus. (ID: %d)", layout->id);
+ } else {
+ g_debug("\tFailed as there are still submenus but no children. (ID: %d)", layout->id);
+ }
+ return FALSE;
+}
+
+static gboolean
+timer_func (gpointer data)
+{
+ g_debug("Death timer. Oops. Got to: %d", layouton);
+ passed = FALSE;
+ g_main_loop_quit(mainloop);
+ return FALSE;
+}
+
+static gboolean layout_verify_timer (gpointer data);
+
+static void
+layout_updated (DbusmenuClient * client, gpointer data)
+{
+ g_debug("Layout Updated");
+ g_timeout_add (250, layout_verify_timer, client);
+ return;
+}
+
+static gboolean
+layout_verify_timer (gpointer data)
+{
+ DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(DBUSMENU_CLIENT(data));
+ proplayout_t * layout = &layouts[layouton];
+
+ if (!verify_root_to_layout(menuroot, layout)) {
+ g_debug("FAILED LAYOUT: %d", layouton);
+ passed = FALSE;
+ } else {
+ /* Extend our death */
+ g_source_remove(death_timer);
+ death_timer = g_timeout_add_seconds(10, timer_func, data);
+ }
+
+ layouton++;
+
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ }
+
+ return FALSE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ /* Make sure the server starts up and all that */
+ g_usleep(500000);
+
+ DbusmenuClient * client = dbusmenu_client_new(":1.0", "/org/test");
+ g_signal_connect(G_OBJECT(client), DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED, G_CALLBACK(layout_updated), NULL);
+
+ death_timer = g_timeout_add_seconds(10, timer_func, client);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_object_unref(G_OBJECT(client));
+
+ if (passed) {
+ g_debug("Quiting");
+ return 0;
+ } else {
+ g_debug("Quiting as we're a failure");
+ return 0;
+ }
+}
diff --git a/tests/test-glib-properties-server.c b/tests/test-glib-properties-server.c
new file mode 100644
index 0000000..a51ac0c
--- /dev/null
+++ b/tests/test-glib-properties-server.c
@@ -0,0 +1,88 @@
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/server.h>
+
+#include "test-glib-properties.h"
+
+static void
+set_props (DbusmenuMenuitem * mi, gchar ** props)
+{
+ if (props == NULL) return;
+
+ guint i;
+ for (i = 0; props[i] != NULL; i += 2) {
+ dbusmenu_menuitem_property_set(mi, props[i], props[i+1]);
+ }
+
+ return;
+}
+
+static DbusmenuMenuitem *
+layout2menuitem (proplayout_t * layout)
+{
+ if (layout == NULL || layout->id == 0) return NULL;
+
+ DbusmenuMenuitem * local = dbusmenu_menuitem_new_with_id(layout->id);
+ set_props(local, layout->properties);
+
+ if (layout->submenu != NULL) {
+ guint count;
+ for (count = 0; layout->submenu[count].id != 0; count++) {
+ DbusmenuMenuitem * child = layout2menuitem(&layout->submenu[count]);
+ if (child != NULL) {
+ dbusmenu_menuitem_child_append(local, child);
+ }
+ }
+ }
+
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
+ return local;
+}
+
+static guint layouton = 0;
+static DbusmenuServer * server = NULL;
+static GMainLoop * mainloop = NULL;
+
+static gboolean
+timer_func (gpointer data)
+{
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ return FALSE;
+ }
+ g_debug("Updating to Layout %d", layouton);
+
+ DbusmenuMenuitem * mi = layout2menuitem(&layouts[layouton]);
+ dbusmenu_server_set_root(server, mi);
+ g_object_unref(G_OBJECT(mi));
+ layouton++;
+
+ return TRUE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(dbus_g_bus_get(DBUS_BUS_SESSION, NULL))));
+
+ server = dbusmenu_server_new("/org/test");
+
+ timer_func(NULL);
+ g_timeout_add(2500, timer_func, NULL);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_object_unref(G_OBJECT(server));
+ g_debug("Quiting");
+
+ return 0;
+}
+
diff --git a/tests/test-glib-properties.h b/tests/test-glib-properties.h
new file mode 100644
index 0000000..3ab7ee7
--- /dev/null
+++ b/tests/test-glib-properties.h
@@ -0,0 +1,95 @@
+
+#include <glib.h>
+
+typedef struct _proplayout_t proplayout_t;
+struct _proplayout_t {
+ guint id;
+ gchar ** properties;
+ proplayout_t * submenu;
+};
+
+gchar * props1[] = {"property1", "value1", "property2", "value2", NULL};
+gchar * props2[] = {"property00", "value00", "property01", "value01", "property02", "value02", "property03", "value03", "property04", "value04",
+ "property05", "value05", "property06", "value06", "property07", "value07", "property08", "value08", "property09", "value09",
+ "property10", "value10", "property11", "value11", "property12", "value12", "property13", "value13", "property14", "value14",
+ "property15", "value15", "property16", "value16", "property17", "value17", "property18", "value18", "property19", "value19",
+ "property20", "value20", "property21", "value21", "property22", "value22", "property23", "value23", "property24", "value24",
+ "property25", "value25", "property26", "value26", "property27", "value27", "property28", "value28", "property29", "value29",
+ "property30", "value30", "property31", "value31", "property32", "value32", "property33", "value33", "property34", "value34",
+ "property35", "value35", "property36", "value36", "property37", "value37", "property38", "value38", "property39", "value39",
+ "property40", "value40", "property41", "value41", "property42", "value42", "property43", "value43", "property44", "value44",
+ "property45", "value45", "property46", "value46", "property47", "value47", "property48", "value48", "property49", "value49",
+ "property50", "value50", "property51", "value51", "property52", "value52", "property53", "value53", "property54", "value54",
+ "property55", "value55", "property56", "value56", "property57", "value57", "property58", "value58", "property59", "value59",
+ "property60", "value60", "property61", "value61", "property62", "value62", "property63", "value63", "property64", "value64",
+ "property65", "value65", "property66", "value66", "property67", "value67", "property68", "value68", "property69", "value69",
+ "property70", "value70", "property71", "value71", "property72", "value72", "property73", "value73", "property74", "value74",
+ "property75", "value75", "property76", "value76", "property77", "value77", "property78", "value78", "property79", "value79",
+ "property80", "value80", "property81", "value81", "property82", "value82", "property83", "value83", "property84", "value84",
+ "property85", "value85", "property86", "value86", "property87", "value87", "property88", "value88", "property89", "value89",
+ "property90", "value90", "property91", "value91", "property92", "value92", "property93", "value93", "property94", "value94",
+ "property95", "value95", "property96", "value96", "property97", "value97", "property98", "value98", "property99", "value99",
+ NULL};
+gchar * props3[] = {"property name that is really long and will ensure that we can really have long property names, which could be important at some point.",
+ "And a property name that is really long should have a value that is really long, because well, that's an important part of the yin and yang of software testing.",
+ NULL};
+gchar * props4[] = {"icon-name", "network-status", "label", "Look at network", "right-column", "10:32", NULL};
+
+
+proplayout_t submenu_4_1[] = {
+ {id: 10, properties: props2, submenu: NULL},
+ {id: 11, properties: props2, submenu: NULL},
+ {id: 12, properties: props2, submenu: NULL},
+ {id: 13, properties: props2, submenu: NULL},
+ {id: 14, properties: props2, submenu: NULL},
+ {id: 15, properties: props2, submenu: NULL},
+ {id: 16, properties: props2, submenu: NULL},
+ {id: 17, properties: props2, submenu: NULL},
+ {id: 18, properties: props2, submenu: NULL},
+ {id: 19, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_2[] = {
+ {id: 20, properties: props2, submenu: NULL},
+ {id: 21, properties: props2, submenu: NULL},
+ {id: 22, properties: props2, submenu: NULL},
+ {id: 23, properties: props2, submenu: NULL},
+ {id: 24, properties: props2, submenu: NULL},
+ {id: 25, properties: props2, submenu: NULL},
+ {id: 26, properties: props2, submenu: NULL},
+ {id: 27, properties: props2, submenu: NULL},
+ {id: 28, properties: props2, submenu: NULL},
+ {id: 29, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_3[] = {
+ {id: 30, properties: props2, submenu: NULL},
+ {id: 31, properties: props2, submenu: NULL},
+ {id: 32, properties: props2, submenu: NULL},
+ {id: 33, properties: props2, submenu: NULL},
+ {id: 34, properties: props2, submenu: NULL},
+ {id: 35, properties: props2, submenu: NULL},
+ {id: 36, properties: props2, submenu: NULL},
+ {id: 37, properties: props2, submenu: NULL},
+ {id: 38, properties: props2, submenu: NULL},
+ {id: 39, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_0[] = {
+ {id: 1, properties: props2, submenu: submenu_4_1},
+ {id: 2, properties: props2, submenu: submenu_4_2},
+ {id: 3, properties: props2, submenu: submenu_4_3},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t layouts[] = {
+ {id: 1, properties: props1, submenu: NULL},
+ {id: 10, properties: props2, submenu: NULL},
+ {id: 20, properties: props3, submenu: NULL},
+ {id: 100, properties: props2, submenu: submenu_4_0},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c
new file mode 100644
index 0000000..44a847f
--- /dev/null
+++ b/tests/test-gtk-label-client.c
@@ -0,0 +1,180 @@
+/*
+A test for libdbusmenu to ensure its quality.
+
+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 the GNU General Public License version 3, 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 GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtk/gtk.h>
+#include <libdbusmenu-gtk/menu.h>
+
+static guint layouton = 0;
+static GMainLoop * mainloop = NULL;
+static gboolean passed = TRUE;
+static guint death_timer = 0;
+
+#if 0
+static gboolean
+verify_props (DbusmenuMenuitem * mi, gchar ** properties)
+{
+ if (properties == NULL) {
+ return TRUE;
+ }
+
+ /* Verify they're all there and correct */
+ guint i;
+ for (i = 0; properties[i] != NULL; i += 2) {
+ const gchar * value = dbusmenu_menuitem_property_get(mi, properties[i]);
+ if (g_strcmp0(value, properties[i + 1])) {
+ g_debug("\tFailed as property '%s' should be '%s' and is '%s'", properties[i], properties[i+1], value);
+ return FALSE;
+ }
+ }
+
+ /* Verify that we don't have any extras */
+ // GList * props = dbusmenu_menuitem_properties_list(mi);
+
+ return TRUE;
+}
+
+static gboolean
+verify_root_to_layout(DbusmenuMenuitem * mi, proplayout_t * layout)
+{
+ g_debug("Verifying ID: %d", layout->id);
+
+ if (layout->id != dbusmenu_menuitem_get_id(mi)) {
+ g_debug("\tFailed as ID %d is not equal to %d", layout->id, dbusmenu_menuitem_get_id(mi));
+ return FALSE;
+ }
+
+ if (!verify_props(mi, layout->properties)) {
+ g_debug("\tFailed as unable to verify properties.");
+ return FALSE;
+ }
+
+ GList * children = dbusmenu_menuitem_get_children(mi);
+
+ if (children == NULL && layout->submenu == NULL) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+ if (children == NULL || layout->submenu == NULL) {
+ if (children == NULL) {
+ g_debug("\tFailed as there are no children but we have submenus");
+ } else {
+ g_debug("\tFailed as we have children but no submenu");
+ }
+ return FALSE;
+ }
+
+ guint i = 0;
+ for (i = 0; children != NULL && layout->submenu[i].id != 0; children = g_list_next(children), i++) {
+ if (!verify_root_to_layout(DBUSMENU_MENUITEM(children->data), &layout->submenu[i])) {
+ return FALSE;
+ }
+ }
+
+ if (children == NULL && layout->submenu[i].id == 0) {
+ g_debug("\tPassed: %d", layout->id);
+ return TRUE;
+ }
+
+ if (children != NULL) {
+ g_debug("\tFailed as there are still children but no submenus. (ID: %d)", layout->id);
+ } else {
+ g_debug("\tFailed as there are still submenus but no children. (ID: %d)", layout->id);
+ }
+ return FALSE;
+}
+#endif
+
+static gboolean
+timer_func (gpointer data)
+{
+ g_debug("Death timer. Oops. Got to: %d", layouton);
+ passed = FALSE;
+ g_main_loop_quit(mainloop);
+ return FALSE;
+}
+
+#if 0
+static gboolean layout_verify_timer (gpointer data);
+
+static void
+layout_updated (DbusmenuClient * client, gpointer data)
+{
+ g_debug("Layout Updated");
+ g_timeout_add (250, layout_verify_timer, client);
+ return;
+}
+
+static gboolean
+layout_verify_timer (gpointer data)
+{
+ DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(DBUSMENU_CLIENT(data));
+ proplayout_t * layout = &layouts[layouton];
+
+ if (!verify_root_to_layout(menuroot, layout)) {
+ g_debug("FAILED LAYOUT: %d", layouton);
+ passed = FALSE;
+ } else {
+ /* Extend our death */
+ g_source_remove(death_timer);
+ death_timer = g_timeout_add_seconds(10, timer_func, data);
+ }
+
+ layouton++;
+
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ }
+
+ return FALSE;
+}
+#endif
+
+int
+main (int argc, char ** argv)
+{
+ gtk_init(&argc, &argv);
+
+ /* Make sure the server starts up and all that */
+ g_usleep(500000);
+
+ GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ GtkWidget * menubar = gtk_menu_bar_new();
+ GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test")));
+ gtk_widget_show(menuitem);
+ gtk_menu_bar_append(menubar, menuitem);
+ gtk_widget_show(menubar);
+ gtk_container_add(GTK_CONTAINER(window), menubar);
+ gtk_widget_show(window);
+
+ death_timer = g_timeout_add_seconds(10, timer_func, window);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ if (passed) {
+ g_debug("Quiting");
+ return 0;
+ } else {
+ g_debug("Quiting as we're a failure");
+ return 0;
+ }
+}
diff --git a/tests/test-gtk-label-server.c b/tests/test-gtk-label-server.c
new file mode 100644
index 0000000..4e2fe0a
--- /dev/null
+++ b/tests/test-gtk-label-server.c
@@ -0,0 +1,101 @@
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/server.h>
+
+#include "test-gtk-label.h"
+
+static void
+set_props (DbusmenuMenuitem * mi, gchar ** props)
+{
+ if (props == NULL) return;
+
+ guint i;
+ for (i = 0; props[i] != NULL; i += 2) {
+ dbusmenu_menuitem_property_set(mi, props[i], props[i+1]);
+ }
+
+ return;
+}
+
+static DbusmenuMenuitem *
+layout2menuitem (proplayout_t * layout)
+{
+ if (layout == NULL || layout->id == 0) return NULL;
+
+ DbusmenuMenuitem * local = dbusmenu_menuitem_new_with_id(layout->id);
+ set_props(local, layout->properties);
+
+ if (layout->submenu != NULL) {
+ guint count;
+ for (count = 0; layout->submenu[count].id != 0; count++) {
+ DbusmenuMenuitem * child = layout2menuitem(&layout->submenu[count]);
+ if (child != NULL) {
+ dbusmenu_menuitem_child_append(local, child);
+ }
+ }
+ }
+
+ /* g_debug("Layout to menu return: 0x%X", (unsigned int)local); */
+ return local;
+}
+
+static guint layouton = 0;
+static DbusmenuServer * server = NULL;
+static GMainLoop * mainloop = NULL;
+
+static gboolean
+timer_func (gpointer data)
+{
+ if (layouts[layouton].id == 0) {
+ g_main_loop_quit(mainloop);
+ return FALSE;
+ }
+ g_debug("Updating to Layout %d", layouton);
+
+ dbusmenu_server_set_root(server, layout2menuitem(&layouts[layouton]));
+ layouton++;
+
+ return TRUE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ g_debug("DBus ID: %s", dbus_connection_get_server_id(dbus_g_connection_get_connection(dbus_g_bus_get(DBUS_BUS_SESSION, NULL))));
+
+ DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+ GError * error = NULL;
+ guint nameret = 0;
+
+ if (!org_freedesktop_DBus_request_name(bus_proxy, "glib.label.test", 0, &nameret, &error)) {
+ g_error("Unable to call to request name");
+ return 1;
+ }
+
+ if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ g_error("Unable to get name");
+ return 1;
+ }
+
+ server = dbusmenu_server_new("/org/test");
+
+ timer_func(NULL);
+ g_timeout_add(2500, timer_func, NULL);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_debug("Quiting");
+
+ return 0;
+}
+
diff --git a/tests/test-gtk-label.h b/tests/test-gtk-label.h
new file mode 100644
index 0000000..e63ac44
--- /dev/null
+++ b/tests/test-gtk-label.h
@@ -0,0 +1,75 @@
+
+#include <glib.h>
+
+typedef struct _proplayout_t proplayout_t;
+struct _proplayout_t {
+ guint id;
+ gchar ** properties;
+ proplayout_t * submenu;
+};
+
+gchar * props1[] = {"label", "value1", NULL};
+gchar * props2[] = {"label", "value1", NULL};
+gchar * props3[] = {"label",
+ "And a property name that is really long should have a value that is really long, because well, that's an important part of the yin and yang of software testing.",
+ NULL};
+gchar * props4[] = {"icon-name", "network-status", "label", "Look at network", "right-column", "10:32", NULL};
+
+
+proplayout_t submenu_4_1[] = {
+ {id: 10, properties: props2, submenu: NULL},
+ {id: 11, properties: props2, submenu: NULL},
+ {id: 12, properties: props2, submenu: NULL},
+ {id: 13, properties: props2, submenu: NULL},
+ {id: 14, properties: props2, submenu: NULL},
+ {id: 15, properties: props2, submenu: NULL},
+ {id: 16, properties: props2, submenu: NULL},
+ {id: 17, properties: props2, submenu: NULL},
+ {id: 18, properties: props2, submenu: NULL},
+ {id: 19, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_2[] = {
+ {id: 20, properties: props2, submenu: NULL},
+ {id: 21, properties: props2, submenu: NULL},
+ {id: 22, properties: props2, submenu: NULL},
+ {id: 23, properties: props2, submenu: NULL},
+ {id: 24, properties: props2, submenu: NULL},
+ {id: 25, properties: props2, submenu: NULL},
+ {id: 26, properties: props2, submenu: NULL},
+ {id: 27, properties: props2, submenu: NULL},
+ {id: 28, properties: props2, submenu: NULL},
+ {id: 29, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_3[] = {
+ {id: 30, properties: props2, submenu: NULL},
+ {id: 31, properties: props2, submenu: NULL},
+ {id: 32, properties: props2, submenu: NULL},
+ {id: 33, properties: props2, submenu: NULL},
+ {id: 34, properties: props2, submenu: NULL},
+ {id: 35, properties: props2, submenu: NULL},
+ {id: 36, properties: props2, submenu: NULL},
+ {id: 37, properties: props2, submenu: NULL},
+ {id: 38, properties: props2, submenu: NULL},
+ {id: 39, properties: props2, submenu: NULL},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t submenu_4_0[] = {
+ {id: 1, properties: props2, submenu: submenu_4_1},
+ {id: 2, properties: props2, submenu: submenu_4_2},
+ {id: 3, properties: props2, submenu: submenu_4_3},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+
+proplayout_t layouts[] = {
+ {id: 1, properties: props1, submenu: submenu_4_3},
+ {id: 10, properties: props2, submenu: submenu_4_2},
+ {id: 20, properties: props3, submenu: submenu_4_1},
+ {id: 100, properties: props2, submenu: submenu_4_0},
+ {id: 0, properties: NULL, submenu: NULL}
+};
+