aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/client.c161
-rw-r--r--libdbusmenu-glib/client.h4
-rw-r--r--libdbusmenu-glib/menuitem-marshal.list2
-rw-r--r--libdbusmenu-glib/menuitem.c41
-rw-r--r--libdbusmenu-glib/menuitem.h5
5 files changed, 201 insertions, 12 deletions
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index 22f65fa..5c429c8 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,6 +207,10 @@ 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) {
@@ -277,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
@@ -292,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;
}
@@ -305,6 +428,8 @@ build_proxies (DbusmenuClient * client)
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,
@@ -316,6 +441,14 @@ build_proxies (DbusmenuClient * client)
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);
@@ -383,7 +516,7 @@ menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError
/* 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); */
@@ -403,6 +536,7 @@ parse_layout_xml(xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * pa
/* Build a new item */
item = dbusmenu_menuitem_new_with_id(id);
+ 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);
}
@@ -427,7 +561,7 @@ 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);
}
@@ -456,12 +590,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;
}
@@ -500,6 +640,10 @@ update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ if (priv->propproxy == NULL) {
+ return;
+ }
+
if (priv->layoutcall != NULL) {
return;
}
@@ -555,7 +699,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)
@@ -564,6 +709,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 95391a4..57f1832 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -58,6 +58,7 @@ enum {
ITEM_ACTIVATED,
CHILD_ADDED,
CHILD_REMOVED,
+ CHILD_MOVED,
LAST_SIGNAL
};
@@ -129,6 +130,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 +140,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, 1, 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.
@@ -157,6 +159,23 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
NULL, NULL,
_dbusmenu_menuitem_marshal_VOID__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",
@@ -398,7 +417,7 @@ 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;
}
@@ -420,7 +439,7 @@ dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child
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);
+ g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, 0, TRUE);
return TRUE;
}
@@ -467,7 +486,7 @@ 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;
}
@@ -490,9 +509,21 @@ dbusmenu_menuitem_child_reorder(DbusmenuMenuitem * mi, DbusmenuMenuitem * child,
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;
}
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index c4fcf73..bad687b 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);