aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
authorTed Gould <ted@canonical.com>2009-08-04 16:34:08 +0100
committerTed Gould <ted@canonical.com>2009-08-04 16:34:08 +0100
commitf5c8526e3bb73345242ea37c337562182351020b (patch)
tree432cb5de765f8484daa87646c0a60d3bac6b3f4f /libdbusmenu-glib
parentec5c382624da19a1a83d1e75d21f74778df01f38 (diff)
parent92f690257a45e81b86b39fc77ce1fe10c7b4908a (diff)
downloadlibdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.tar.gz
libdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.tar.bz2
libdbusmenu-f5c8526e3bb73345242ea37c337562182351020b.zip
Merging in the development branches
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/client.c204
-rw-r--r--libdbusmenu-glib/client.h4
-rw-r--r--libdbusmenu-glib/menuitem-marshal.list2
-rw-r--r--libdbusmenu-glib/menuitem.c138
-rw-r--r--libdbusmenu-glib/menuitem.h10
-rw-r--r--libdbusmenu-glib/server.c5
6 files changed, 344 insertions, 19 deletions
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 6094eca..212071b 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -47,6 +47,8 @@ enum {
/* Signals */
enum {
LAYOUT_UPDATED,
+ ROOT_CHANGED,
+ NEW_MENUITEM,
LAST_SIGNAL
};
@@ -64,6 +66,8 @@ struct _DbusmenuClientPrivate
DBusGProxy * menuproxy;
DBusGProxy * propproxy;
DBusGProxyCall * layoutcall;
+
+ DBusGProxy * dbusproxy;
};
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
@@ -82,7 +86,7 @@ static void id_prop_update (DBusGProxy * proxy, guint id, gchar * property, gcha
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, DBusGProxy * proxy);
+static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, 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);
@@ -118,6 +122,39 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, G_TYPE_NONE);
+ /**
+ DbusmenuClient::root-changed:
+ @arg0: The #DbusmenuClient object
+ @arg1: The new root #DbusmenuMenuitem
+
+ The layout has changed in a way that can not be
+ represented by the individual items changing as the
+ root of this client has changed.
+ */
+ signals[ROOT_CHANGED] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, root_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ /**
+ DbusmenuClient::new-menuitem:
+ @arg0: The #DbusmenuClient object
+ @arg1: The new #DbusmenuMenuitem created
+
+ Signaled when the client creates a new menuitem. This
+ doesn't mean that it's placed anywhere. The parent that
+ it's applied to will signal #DbusmenuMenuitem::child-added
+ when it gets parented.
+ */
+ signals[NEW_MENUITEM] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, new_menuitem),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
g_object_class_install_property (object_class, PROP_DBUSOBJECT,
g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent",
@@ -148,6 +185,8 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->propproxy = NULL;
priv->layoutcall = NULL;
+ priv->dbusproxy = NULL;
+
return;
}
@@ -168,8 +207,17 @@ dbusmenu_client_dispose (GObject *object)
g_object_unref(G_OBJECT(priv->propproxy));
priv->propproxy = NULL;
}
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
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;
}
@@ -272,6 +320,85 @@ id_update (DBusGProxy * proxy, guint id, DbusmenuClient * client)
return;
}
+/* Watches to see if our DBus savior comes onto the bus */
+static void
+dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (!(new != NULL && prev == NULL)) {
+ /* If it's not someone new getting on the bus, sorry we
+ simply just don't care. It's not that your service isn't
+ important to someone, just not us. You'll find the right
+ process someday, there's lots of processes out there. */
+ return;
+ }
+
+ if (g_strcmp0(new, priv->dbus_name)) {
+ /* Again, someone else's service. */
+ return;
+ }
+
+ /* Woot! A service for us to love and to hold for ever
+ and ever and ever! */
+ return build_proxies(client);
+}
+
+/* This function builds the DBus proxy which will look out for
+ the service coming up. */
+static void
+build_dbus_proxy (DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ GError * error = NULL;
+
+ if (priv->dbusproxy != NULL) {
+ return;
+ }
+
+ priv->dbusproxy = dbus_g_proxy_new_for_name_owner (priv->session_bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ &error);
+ if (error != NULL) {
+ g_debug("Oh, that's bad. That's really bad. We can't get a proxy to DBus itself? Seriously? Here's all I know: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ dbus_g_proxy_add_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(priv->dbusproxy, "NameOwnerChanged",
+ G_CALLBACK(dbus_owner_change), client, NULL);
+
+ return;
+}
+
+/* A signal handler that gets called when a proxy is destoryed a
+ so it needs to clean up a little. Make sure we don't think we
+ have a layout and setup the dbus watcher. */
+static void
+proxy_destroyed (GObject * gobj_proxy, gpointer userdata)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(userdata);
+
+ if (priv->root != NULL) {
+ g_object_unref(G_OBJECT(priv->root));
+ priv->root = NULL;
+ g_signal_emit(G_OBJECT(userdata), signals[ROOT_CHANGED], 0, NULL, TRUE);
+ g_signal_emit(G_OBJECT(userdata), signals[LAYOUT_UPDATED], 0, TRUE);
+ }
+
+ if ((gpointer)priv->menuproxy == (gpointer)gobj_proxy) {
+ priv->layoutcall = NULL;
+ }
+
+ build_dbus_proxy(DBUSMENU_CLIENT(userdata));
+ return;
+}
+
/* When we have a name and an object, build the two proxies and get the
first version of the layout */
static void
@@ -287,6 +414,7 @@ build_proxies (DbusmenuClient * client)
if (error != NULL) {
g_error("Unable to get session bus: %s", error->message);
g_error_free(error);
+ build_dbus_proxy(client);
return;
}
@@ -296,10 +424,12 @@ build_proxies (DbusmenuClient * client)
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_warning("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->propproxy), (gpointer *)&priv->propproxy);
+ g_signal_connect(G_OBJECT(priv->propproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
priv->menuproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
priv->dbus_name,
@@ -307,10 +437,18 @@ build_proxies (DbusmenuClient * client)
"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_warning("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
g_error_free(error);
return;
}
+ g_object_add_weak_pointer(G_OBJECT(priv->menuproxy), (gpointer *)&priv->menuproxy);
+ g_signal_connect(G_OBJECT(priv->menuproxy), "destroy", G_CALLBACK(proxy_destroyed), client);
+
+ /* If we get here, we don't need the DBus proxy */
+ if (priv->dbusproxy != NULL) {
+ g_object_unref(G_OBJECT(priv->dbusproxy));
+ priv->dbusproxy = NULL;
+ }
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);
@@ -322,6 +460,8 @@ build_proxies (DbusmenuClient * client)
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);
+ update_layout(client);
+
return;
}
@@ -375,10 +515,30 @@ menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError
return;
}
+static void
+menuitem_call_cb (DBusGProxy * proxy, GError * error, gpointer userdata)
+{
+ DbusmenuMenuitem * mi = (DbusmenuMenuitem *)userdata;
+
+ if (error != NULL) {
+ g_warning("Unable to call menu item %d: %s", dbusmenu_menuitem_get_id(mi), error->message);
+ }
+
+ return;
+}
+
+static void
+menuitem_activate (DbusmenuMenuitem * mi, DbusmenuClient * client)
+{
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ org_freedesktop_dbusmenu_call_async (priv->menuproxy, dbusmenu_menuitem_get_id(mi), menuitem_call_cb, mi);
+ 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, DBusGProxy * proxy)
+parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
{
guint id = parse_node_get_id(node);
/* g_debug("Looking at node with id: %d", id); */
@@ -388,6 +548,7 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
dbusmenu_menuitem_child_delete(parent, item);
}
g_object_unref(G_OBJECT(item));
+ item = NULL;
}
if (id == 0) {
@@ -397,6 +558,11 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
/* Build a new item */
item = dbusmenu_menuitem_new_with_id(id);
+ if (parent == NULL) {
+ dbusmenu_menuitem_set_root(item, TRUE);
+ }
+ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(menuitem_activate), client);
+ g_signal_emit(G_OBJECT(client), signals[NEW_MENUITEM], 0, item, TRUE);
/* Get the properties queued up for this item */
org_freedesktop_dbusmenu_get_properties_async(proxy, id, menuitem_get_properties_cb, item);
}
@@ -404,6 +570,7 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
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); */
@@ -420,13 +587,15 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
}
}
- childmi = parse_layout_xml(children, childmi, item, proxy);
+ childmi = parse_layout_xml(client, 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);
@@ -447,12 +616,18 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
xmlNodePtr root = xmlDocGetRootElement(xmldoc);
- priv->root = parse_layout_xml(root, priv->root, NULL, priv->menuproxy);
+ DbusmenuMenuitem * oldroot = priv->root;
+ priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
+ xmlFreeDoc(xmldoc);
+
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);
+ if (priv->root != oldroot) {
+ g_signal_emit(G_OBJECT(client), signals[ROOT_CHANGED], 0, priv->root, TRUE);
+ }
+
return;
}
@@ -466,9 +641,9 @@ update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
GError * error = NULL;
GValue value = {0};
+ priv->layoutcall = NULL;
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;
}
@@ -477,7 +652,6 @@ update_layout_cb (DBusGProxy * proxy, DBusGProxyCall * call, void * data)
/* 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);
@@ -491,6 +665,10 @@ update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return;
+ }
+
if (priv->layoutcall != NULL) {
return;
}
@@ -528,7 +706,6 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
DBUSMENU_CLIENT_PROP_DBUS_NAME, name,
DBUSMENU_CLIENT_PROP_DBUS_OBJECT, object,
NULL);
- update_layout(self);
return self;
}
@@ -546,7 +723,8 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
it could block longer.
Return value: A #DbusmenuMenuitem representing the root of
- menu on the server.
+ menu on the server. If there is no server or there is
+ an error receiving its layout it'll return #NULL.
*/
DbusmenuMenuitem *
dbusmenu_client_get_root (DbusmenuClient * client)
@@ -555,6 +733,10 @@ dbusmenu_client_get_root (DbusmenuClient * client)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return NULL;
+ }
+
if (priv->layoutcall != NULL) {
/* Will end the current call and block on it's completion */
update_layout_cb(priv->propproxy, priv->layoutcall, client);
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
index d591ebb..35f7122 100644
--- a/libdbusmenu-glib/client.h
+++ b/libdbusmenu-glib/client.h
@@ -44,6 +44,8 @@ G_BEGIN_DECLS
#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_SIGNAL_ROOT_CHANGED "root-changed"
+#define DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM "new-menuitem"
#define DBUSMENU_CLIENT_PROP_DBUS_NAME "dbus-name"
#define DBUSMENU_CLIENT_PROP_DBUS_OBJECT "dbus-object"
@@ -66,6 +68,8 @@ struct _DbusmenuClientClass {
GObjectClass parent_class;
void (*layout_updated)(void);
+ void (*root_changed) (DbusmenuMenuitem * newroot);
+ void (*new_menuitem) (DbusmenuMenuitem * newitem);
/* Reserved for future use */
void (*reserved1) (void);
diff --git a/libdbusmenu-glib/menuitem-marshal.list b/libdbusmenu-glib/menuitem-marshal.list
index fc0318f..a32e7e3 100644
--- a/libdbusmenu-glib/menuitem-marshal.list
+++ b/libdbusmenu-glib/menuitem-marshal.list
@@ -1,3 +1,5 @@
VOID: STRING, STRING
+VOID: OBJECT, UINT, UINT
+VOID: OBJECT, UINT
VOID: OBJECT
VOID: VOID
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index 9506cad..aba1f64 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -39,6 +39,7 @@ License version 3 and version 2.1 along with this program. If not, see
@children: A list of #DbusmenuMenuitem objects that are
children to this one.
@properties: All of the properties on this menu item.
+ @root: Whether this node is the root node
These are the little secrets that we don't want getting
out of data that we have. They can still be gotten using
@@ -50,6 +51,7 @@ struct _DbusmenuMenuitemPrivate
guint id;
GList * children;
GHashTable * properties;
+ gboolean root;
};
/* Signals */
@@ -58,6 +60,7 @@ enum {
ITEM_ACTIVATED,
CHILD_ADDED,
CHILD_REMOVED,
+ CHILD_MOVED,
LAST_SIGNAL
};
@@ -129,6 +132,7 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
DbusmenuMenuitem::child-added:
@arg0: The #DbusmenuMenuitem which is the parent.
@arg1: The #DbusmenuMenuitem which is the child.
+ @arg2: The position that the child is being added in.
Signaled when the child menuitem has been added to
the parent.
@@ -138,8 +142,8 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_added),
NULL, NULL,
- _dbusmenu_menuitem_marshal_VOID__OBJECT,
- G_TYPE_NONE, 2, G_TYPE_OBJECT);
+ _dbusmenu_menuitem_marshal_VOID__OBJECT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
/**
DbusmenuMenuitem::child-removed:
@arg0: The #DbusmenuMenuitem which was the parent.
@@ -156,7 +160,24 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_removed),
NULL, NULL,
_dbusmenu_menuitem_marshal_VOID__OBJECT,
- G_TYPE_NONE, 2, G_TYPE_OBJECT);
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ /**
+ DbusmenuMenuitem::child-moved:
+ @arg0: The #DbusmenuMenuitem which is the parent.
+ @arg1: The #DbusmenuMenuitem which is the child.
+ @arg2: The position that the child is being moved to.
+ @arg3: The position that the child is was in.
+
+ Signaled when the child menuitem has had it's location
+ in the list change.
+ */
+ signals[CHILD_MOVED] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_moved),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__OBJECT_UINT_UINT,
+ G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_UINT, G_TYPE_UINT);
g_object_class_install_property (object_class, PROP_ID,
g_param_spec_uint("id", "ID for the menu item",
@@ -178,6 +199,8 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
priv->children = NULL;
priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->root = FALSE;
return;
}
@@ -185,6 +208,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;
@@ -193,6 +224,7 @@ 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) {
@@ -389,7 +421,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);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, g_list_length(priv->children) - 1, 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, 0, TRUE);
return TRUE;
}
@@ -436,7 +490,44 @@ 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);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, position, 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);
+ gint oldpos = g_list_index(priv->children, child);
+
+ if (oldpos == -1) {
+ g_warning("Can not reorder child that isn't actually a child.");
+ return FALSE;
+ }
+ if (oldpos == position) {
+ return TRUE;
+ }
+
+ priv->children = g_list_remove(priv->children, child);
+ priv->children = g_list_insert(priv->children, child, position);
+
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_MOVED], 0, child, position, oldpos, TRUE);
+
return TRUE;
}
@@ -658,6 +749,43 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
}
/**
+ dbusmenu_menuitem_set_root:
+ @mi: #DbusmenuMenuitem to set whether it's root
+ @root: Whether @mi is a root node or not
+
+ This function sets the internal value of whether this is a
+ root node or not.
+
+ Return value: None
+*/
+void
+dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->root = root;
+ return;
+}
+
+/**
+ dbusmenu_menuitem_get_root:
+ @mi: #DbusmenuMenuitem to see whether it's root
+
+ This function returns the internal value of whether this is a
+ root node or not.
+
+ Return value: #TRUE if this is a root node
+*/
+gboolean
+dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ return priv->root;
+}
+
+
+/**
dbusmenu_menuitem_buildxml:
@mi: #DbusmenuMenuitem to represent in XML
@array: A list of string that will be turned into an XML file
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index 3f3e97c..29865c7 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED "child-added"
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED "child-removed"
+#define DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED "child-moved"
/**
DbusmenuMenuitem:
@@ -69,6 +70,7 @@ struct _DbusmenuMenuitem
@item_activated: Slot for #DbusmenuMenuitem::item-activated.
@child_added: Slot for #DbusmenuMenuitem::child-added.
@child_removed: Slot for #DbusmenuMenuitem::child-removed.
+ @child_moved: Slot for #DbusmenuMenuitem::child-moved.
@buildxml: Virtual function that appends the strings required
to represent this menu item in the menu XML file.
@reserved1: Reserved for future use.
@@ -84,8 +86,9 @@ struct _DbusmenuMenuitemClass
/* Signals */
void (*property_changed) (gchar * property, gchar * value);
void (*item_activated) (void);
- void (*child_added) (DbusmenuMenuitem * child);
+ void (*child_added) (DbusmenuMenuitem * child, guint position);
void (*child_removed) (DbusmenuMenuitem * child);
+ void (*child_moved) (DbusmenuMenuitem * child, guint newpos, guint oldpos);
/* Virtual functions */
void (*buildxml) (GPtrArray * stringarray);
@@ -107,8 +110,10 @@ GList * dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUS
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);
@@ -118,6 +123,9 @@ gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar *
GList * dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi) G_GNUC_WARN_UNUSED_RESULT;
GHashTable * dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi);
+void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root);
+gboolean dbusmenu_menuitem_get_root (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);
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 562b2d7..ab5951f 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -86,7 +86,7 @@ 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_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, 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);
@@ -306,7 +306,7 @@ menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, gchar * valu
}
static void
-menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, DbusmenuServer * server)
+menuitem_child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint pos, DbusmenuServer * server)
{
menuitem_signals_create(child, server);
/* TODO: We probably need to group the layout update signals to make the number more reasonble. */
@@ -490,6 +490,7 @@ dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root)
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;
}