aboutsummaryrefslogtreecommitdiff
path: root/libdbusmenu-glib
diff options
context:
space:
mode:
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r--libdbusmenu-glib/Makefile.am119
-rw-r--r--libdbusmenu-glib/clean-namespaces.xslt14
-rw-r--r--libdbusmenu-glib/client-marshal.list4
-rw-r--r--libdbusmenu-glib/client-menuitem.c17
-rw-r--r--libdbusmenu-glib/client-menuitem.h12
-rw-r--r--libdbusmenu-glib/client-private.h49
-rw-r--r--libdbusmenu-glib/client.c1380
-rw-r--r--libdbusmenu-glib/client.h126
-rw-r--r--libdbusmenu-glib/dbus-menu.xml156
-rw-r--r--libdbusmenu-glib/dbusmenu-glib-0.4.pc.in (renamed from libdbusmenu-glib/dbusmenu-glib.pc.in)4
-rw-r--r--libdbusmenu-glib/dbusmenu-glib.h37
-rw-r--r--libdbusmenu-glib/defaults.c292
-rw-r--r--libdbusmenu-glib/defaults.h86
-rw-r--r--libdbusmenu-glib/enum-types.c.in116
-rw-r--r--libdbusmenu-glib/enum-types.h.in65
-rw-r--r--libdbusmenu-glib/menuitem-marshal.list3
-rw-r--r--libdbusmenu-glib/menuitem-private.h5
-rw-r--r--libdbusmenu-glib/menuitem-proxy.c52
-rw-r--r--libdbusmenu-glib/menuitem-proxy.h32
-rw-r--r--libdbusmenu-glib/menuitem.c1294
-rw-r--r--libdbusmenu-glib/menuitem.h346
-rw-r--r--libdbusmenu-glib/server-marshal.list2
-rw-r--r--libdbusmenu-glib/server.c1474
-rw-r--r--libdbusmenu-glib/server.h99
-rw-r--r--libdbusmenu-glib/types.h76
25 files changed, 4638 insertions, 1222 deletions
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index 0a6513f..5b04415 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -1,27 +1,42 @@
-CLEANFILES =
-
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
EXTRA_DIST = \
- dbusmenu-glib.pc.in \
+ clean-namespaces.xslt \
+ dbusmenu-glib-0.4.pc.in \
dbus-menu.xml \
client-marshal.list \
menuitem-marshal.list \
server-marshal.list
+include $(top_srcdir)/Makefile.am.enum
+
lib_LTLIBRARIES = \
libdbusmenu-glib.la
-libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.1/libdbusmenu-glib/
+libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.4/libdbusmenu-glib/
-libdbusmenu_glibinclude_HEADERS = \
+
+EXPORTED_OBJECTS = \
+ enum-types.h \
menuitem.h \
menuitem-proxy.h \
server.h \
client.h
+libdbusmenu_glibinclude_HEADERS = \
+ $(EXPORTED_OBJECTS) \
+ dbusmenu-glib.h \
+ types.h
+
libdbusmenu_glib_la_SOURCES = \
- dbusmenu-server.h \
- dbusmenu-client.h \
+ dbus-menu-clean.xml.h \
+ dbus-menu-clean.xml.c \
+ defaults.h \
+ defaults.c \
+ enum-types.h \
+ enum-types.c \
menuitem.h \
menuitem.c \
menuitem-marshal.h \
@@ -37,6 +52,7 @@ libdbusmenu_glib_la_SOURCES = \
client-marshal.c \
client-menuitem.h \
client-menuitem.c \
+ client-private.h \
client.h \
client.c
@@ -51,12 +67,33 @@ libdbusmenu_glib_la_CFLAGS = \
libdbusmenu_glib_la_LIBADD = \
$(DBUSMENUGLIB_LIBS)
-pkgconfig_DATA = dbusmenu-glib.pc
+pkgconfig_DATA = dbusmenu-glib-0.4.pc
pkgconfigdir = $(libdir)/pkgconfig
-BUILT_SOURCES = \
- dbusmenu-client.h \
- dbusmenu-server.h \
+glib_enum_h = enum-types.h
+glib_enum_c = enum-types.c
+glib_enum_headers = $(addprefix $(srcdir)/, $(libdbusmenu_glibinclude_HEADERS))
+
+DISTCLEANFILES += \
+ enum-types.c \
+ enum-types.h
+
+%.xml.h: %.xml
+ echo "extern const char * $(subst -,_,$(subst .,_,$(basename $(notdir $@))));" > $@
+
+%.xml.c: %.xml
+ echo "const char * $(subst -,_,$(subst .,_,$(basename $(notdir $@)))) = " > $@
+ sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@
+ echo ";" >> $@
+
+dbus-menu-clean.xml: dbus-menu.xml
+ $(XSLT_PROC) $(srcdir)/clean-namespaces.xslt $< > $@ || (rm -f $@ && /bin/false)
+
+CLEANFILES += dbus-menu-clean.xml
+
+BUILT_SOURCES += \
+ dbus-menu-clean.xml.c \
+ dbus-menu-clean.xml.h \
client-marshal.h \
client-marshal.c \
menuitem-marshal.h \
@@ -64,19 +101,7 @@ BUILT_SOURCES = \
server-marshal.h \
server-marshal.c
-dbusmenu-server.h: dbus-menu.xml
- dbus-binding-tool \
- --prefix=_dbusmenu_server \
- --mode=glib-server \
- --output=dbusmenu-server.h \
- $(srcdir)/dbus-menu.xml
-
-dbusmenu-client.h: dbus-menu.xml
- dbus-binding-tool \
- --prefix=_dbusmenu_client \
- --mode=glib-client \
- --output=dbusmenu-client.h \
- $(srcdir)/dbus-menu.xml
+CLEANFILES += $(BUILT_SOURCES)
client-marshal.h: $(srcdir)/client-marshal.list
glib-genmarshal --header \
@@ -114,25 +139,41 @@ menuitem-marshal.c: $(srcdir)/menuitem-marshal.list
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
-INTROSPECTION_SCANNER_ARGS = \
- --add-include-path=$(srcdir) \
- $(addprefix --c-include=libdbusmenu-glib/, $(introspection_sources))
+
+if INTROSPECTION_TEN
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
+ --warn-all \
+ --add-include-path=$(srcdir) \
+ $(addprefix --c-include=libdbusmenu-glib/, $(libdbusmenu_glibinclude_HEADERS)) \
+ --symbol-prefix=dbusmenu \
+ --identifier-prefix=Dbusmenu
+else
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
+ --warn-all \
+ --add-include-path=$(srcdir) \
+ $(addprefix --c-include=libdbusmenu-glib/, $(libdbusmenu_glibinclude_HEADERS))
+endif
+
INTROSPECTION_COMPILER_ARGS = --includedir=$(builddir)
if HAVE_INTROSPECTION
-introspection_sources = $(libdbusmenu_glibinclude_HEADERS)
+introspection_sources = \
+ $(libdbusmenu_glibinclude_HEADERS) \
+ $(EXPORTED_OBJECTS:.h=.c)
-Dbusmenu_Glib-0.2.gir: libdbusmenu-glib.la
-Dbusmenu_Glib_0_2_gir_INCLUDES = \
+Dbusmenu-0.4.gir: libdbusmenu-glib.la
+Dbusmenu_0_4_gir_INCLUDES = \
GObject-2.0
-Dbusmenu_Glib_0_2_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS)
-Dbusmenu_Glib_0_2_gir_LIBS = libdbusmenu-glib.la
-Dbusmenu_Glib_0_2_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources))
-Dbusmenu_Glib_0_2_gir_NAMESPACE = Dbusmenu
-Dbusmenu_Glib_0_2_gir_VERSION = Glib-0.2
+Dbusmenu_0_4_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS) -I$(top_srcdir)
+Dbusmenu_0_4_gir_LIBS = libdbusmenu-glib.la
+Dbusmenu_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources))
+Dbusmenu_0_4_gir_NAMESPACE = Dbusmenu
+Dbusmenu_0_4_gir_VERSION = 0.4
+Dbusmenu_0_4_gir_EXPORT_PACKAGES = dbusmenu-glib-0.4
+Dbusmenu_0_4_gir_SCANNER_FLAGS = $(INTROSPECTION_SCANNER_ARGS)
-INTROSPECTION_GIRS += Dbusmenu-Glib-0.2.gir
+INTROSPECTION_GIRS += Dbusmenu-0.4.gir
girdir = $(datadir)/gir-1.0
gir_DATA = $(INTROSPECTION_GIRS)
@@ -151,10 +192,10 @@ endif
if HAVE_INTROSPECTION
vapidir = $(datadir)/vala/vapi
-vapi_DATA = Dbusmenu-Glib-0.2.vapi
+vapi_DATA = Dbusmenu-0.4.vapi
-Dbusmenu-Glib-0.2.vapi: Dbusmenu-Glib-0.2.gir
- $(VALA_API_GEN) --library=Dbusmenu-Glib-0.2 $<
+Dbusmenu-0.4.vapi: Dbusmenu-0.4.gir
+ $(VALA_API_GEN) --library=Dbusmenu-0.4 $<
CLEANFILES += $(vapi_DATA)
diff --git a/libdbusmenu-glib/clean-namespaces.xslt b/libdbusmenu-glib/clean-namespaces.xslt
new file mode 100644
index 0000000..8c0c521
--- /dev/null
+++ b/libdbusmenu-glib/clean-namespaces.xslt
@@ -0,0 +1,14 @@
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dox="http://www.canonical.com/dbus/dox.dtd">
+ <xsl:template match="*|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="*|@*" />
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="@dox:*|dox:*"/>
+ <xsl:template match="*">
+ <xsl:element name="{local-name()}">
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:element>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/libdbusmenu-glib/client-marshal.list b/libdbusmenu-glib/client-marshal.list
index 2e14491..980c5c3 100644
--- a/libdbusmenu-glib/client-marshal.list
+++ b/libdbusmenu-glib/client-marshal.list
@@ -1,2 +1,4 @@
VOID: OBJECT, UINT
-VOID: OBJECT, STRING, POINTER, UINT, POINTER
+VOID: OBJECT, STRING, VARIANT, UINT, POINTER
+VOID: ENUM
+VOID: POINTER
diff --git a/libdbusmenu-glib/client-menuitem.c b/libdbusmenu-glib/client-menuitem.c
index 979cf79..483470a 100644
--- a/libdbusmenu-glib/client-menuitem.c
+++ b/libdbusmenu-glib/client-menuitem.c
@@ -30,6 +30,7 @@ License version 3 and version 2.1 along with this program. If not, see
#endif
#include "client-menuitem.h"
+#include "client-private.h"
typedef struct _DbusmenuClientMenuitemPrivate DbusmenuClientMenuitemPrivate;
@@ -39,14 +40,14 @@ struct _DbusmenuClientMenuitemPrivate
};
#define DBUSMENU_CLIENT_MENUITEM_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_CLIENT_MENUITEM_TYPE, DbusmenuClientMenuitemPrivate))
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT_MENUITEM, DbusmenuClientMenuitemPrivate))
static void dbusmenu_client_menuitem_class_init (DbusmenuClientMenuitemClass *klass);
static void dbusmenu_client_menuitem_init (DbusmenuClientMenuitem *self);
static void dbusmenu_client_menuitem_dispose (GObject *object);
static void dbusmenu_client_menuitem_finalize (GObject *object);
-static void handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
-static void send_about_to_show (DbusmenuMenuitem * mi, dbusmenu_menuitem_about_to_show_cb cb, gpointer cb_data);
+static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp);
+static void send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data);
G_DEFINE_TYPE (DbusmenuClientMenuitem, dbusmenu_client_menuitem, DBUSMENU_TYPE_MENUITEM);
@@ -94,7 +95,7 @@ dbusmenu_client_menuitem_finalize (GObject *object)
DbusmenuClientMenuitem *
dbusmenu_client_menuitem_new (gint id, DbusmenuClient * client)
{
- DbusmenuClientMenuitem * mi = g_object_new(DBUSMENU_CLIENT_MENUITEM_TYPE, "id", id, NULL);
+ DbusmenuClientMenuitem * mi = g_object_new(DBUSMENU_TYPE_CLIENT_MENUITEM, "id", id, NULL);
DbusmenuClientMenuitemPrivate * priv = DBUSMENU_CLIENT_MENUITEM_GET_PRIVATE(mi);
priv->client = client;
return mi;
@@ -102,17 +103,17 @@ dbusmenu_client_menuitem_new (gint id, DbusmenuClient * client)
/* Passes the event signal on through the client. */
static void
-handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp)
{
DbusmenuClientMenuitemPrivate * priv = DBUSMENU_CLIENT_MENUITEM_GET_PRIVATE(mi);
- dbusmenu_client_send_event(priv->client, dbusmenu_menuitem_get_id(mi), name, value, timestamp);
+ dbusmenu_client_send_event(priv->client, dbusmenu_menuitem_get_id(mi), name, variant, timestamp, mi);
return;
}
typedef struct _about_to_show_t about_to_show_t;
struct _about_to_show_t {
DbusmenuMenuitem * mi;
- dbusmenu_menuitem_about_to_show_cb cb;
+ void (*cb) (DbusmenuMenuitem * mi, gpointer user_data);
gpointer cb_data;
};
@@ -131,7 +132,7 @@ about_to_show_cb (gpointer user_data)
/* Passes the about to show signal on through the client. */
static void
-send_about_to_show (DbusmenuMenuitem * mi, dbusmenu_menuitem_about_to_show_cb cb, gpointer cb_data)
+send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data)
{
DbusmenuClientMenuitemPrivate * priv = DBUSMENU_CLIENT_MENUITEM_GET_PRIVATE(mi);
if (cb == NULL) {
diff --git a/libdbusmenu-glib/client-menuitem.h b/libdbusmenu-glib/client-menuitem.h
index ef61213..a89cb11 100644
--- a/libdbusmenu-glib/client-menuitem.h
+++ b/libdbusmenu-glib/client-menuitem.h
@@ -35,12 +35,12 @@ License version 3 and version 2.1 along with this program. If not, see
G_BEGIN_DECLS
-#define DBUSMENU_CLIENT_MENUITEM_TYPE (dbusmenu_client_menuitem_get_type ())
-#define DBUSMENU_CLIENT_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_CLIENT_MENUITEM_TYPE, DbusmenuClientMenuitem))
-#define DBUSMENU_CLIENT_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_CLIENT_MENUITEM_TYPE, DbusmenuClientMenuitemClass))
-#define DBUSMENU_IS_CLIENT_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_CLIENT_MENUITEM_TYPE))
-#define DBUSMENU_IS_CLIENT_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_CLIENT_MENUITEM_TYPE))
-#define DBUSMENU_CLIENT_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_CLIENT_MENUITEM_TYPE, DbusmenuClientMenuitemClass))
+#define DBUSMENU_TYPE_CLIENT_MENUITEM (dbusmenu_client_menuitem_get_type ())
+#define DBUSMENU_CLIENT_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_CLIENT_MENUITEM, DbusmenuClientMenuitem))
+#define DBUSMENU_CLIENT_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_CLIENT_MENUITEM, DbusmenuClientMenuitemClass))
+#define DBUSMENU_IS_CLIENT_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_CLIENT_MENUITEM))
+#define DBUSMENU_IS_CLIENT_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_CLIENT_MENUITEM))
+#define DBUSMENU_CLIENT_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_CLIENT_MENUITEM, DbusmenuClientMenuitemClass))
typedef struct _DbusmenuClientMenuitem DbusmenuClientMenuitem;
typedef struct _DbusmenuClientMenuitemClass DbusmenuClientMenuitemClass;
diff --git a/libdbusmenu-glib/client-private.h b/libdbusmenu-glib/client-private.h
new file mode 100644
index 0000000..44c0066
--- /dev/null
+++ b/libdbusmenu-glib/client-private.h
@@ -0,0 +1,49 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_CLIENT_PRIVATE_H__
+#define __DBUSMENU_CLIENT_PRIVATE_H__
+
+#include "client.h"
+
+G_BEGIN_DECLS
+
+void dbusmenu_client_send_event (DbusmenuClient * client,
+ gint id,
+ const gchar * name,
+ GVariant * variant,
+ guint timestamp,
+ DbusmenuMenuitem * mi);
+void dbusmenu_client_send_about_to_show(DbusmenuClient * client,
+ gint id,
+ void (*cb) (gpointer user_data),
+ gpointer cb_data);
+
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/client.c b/libdbusmenu-glib/client.c
index ca16c9a..588c940 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -30,24 +30,29 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
-#include <dbus/dbus-glib-bindings.h>
-
-#include <libxml/parser.h>
-#include <libxml/tree.h>
+#include <gio/gio.h>
#include "client.h"
+#include "client-private.h"
#include "menuitem.h"
#include "menuitem-private.h"
#include "client-menuitem.h"
-#include "dbusmenu-client.h"
#include "server-marshal.h"
#include "client-marshal.h"
+#include "dbus-menu-clean.xml.h"
+#include "enum-types.h"
+
+/* How many property requests should we queue before
+ sending the message on dbus */
+#define MAX_PROPERTIES_TO_QUEUE 100
/* Properties */
enum {
PROP_0,
PROP_DBUSOBJECT,
- PROP_DBUSNAME
+ PROP_DBUSNAME,
+ PROP_STATUS,
+ PROP_TEXT_DIRECTION
};
/* Signals */
@@ -57,12 +62,14 @@ enum {
NEW_MENUITEM,
ITEM_ACTIVATE,
EVENT_RESULT,
+ ICON_THEME_DIRS,
LAST_SIGNAL
};
+typedef void (*properties_func) (GVariant * properties, GError * error, gpointer user_data);
+
static guint signals[LAST_SIGNAL] = { 0 };
-typedef struct _DbusmenuClientPrivate DbusmenuClientPrivate;
struct _DbusmenuClientPrivate
{
DbusmenuMenuitem * root;
@@ -70,21 +77,29 @@ struct _DbusmenuClientPrivate
gchar * dbus_object;
gchar * dbus_name;
- DBusGConnection * session_bus;
- DBusGProxy * menuproxy;
- DBusGProxy * propproxy;
- DBusGProxyCall * layoutcall;
+ GDBusConnection * session_bus;
+ GCancellable * session_bus_cancel;
+
+ GDBusProxy * menuproxy;
+ GCancellable * menuproxy_cancel;
+
+ GCancellable * layoutcall;
+ GVariant * layout_props;
gint current_revision;
gint my_revision;
- DBusGProxy * dbusproxy;
+ guint dbusproxy;
GHashTable * type_handlers;
GArray * delayed_property_list;
GArray * delayed_property_listeners;
gint delayed_idle;
+
+ DbusmenuTextDirection text_direction;
+ DbusmenuStatus status;
+ GStrv icon_dirs;
};
typedef struct _newItemPropData newItemPropData;
@@ -98,7 +113,7 @@ struct _newItemPropData
typedef struct _properties_listener_t properties_listener_t;
struct _properties_listener_t {
gint id;
- org_ayatana_dbusmenu_get_properties_reply callback;
+ properties_func callback;
gpointer user_data;
gboolean replied;
};
@@ -108,13 +123,28 @@ struct _event_data_t {
DbusmenuClient * client;
DbusmenuMenuitem * menuitem;
gchar * event;
- GValue data;
+ GVariant * variant;
guint timestamp;
};
+typedef struct _type_handler_t type_handler_t;
+struct _type_handler_t {
+ DbusmenuClient * client;
+ DbusmenuClientTypeHandler cb;
+ GDestroyNotify destroy_cb;
+ gpointer user_data;
+ gchar * type;
+};
-#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
+typedef struct _properties_callback_t properties_callback_t;
+struct _properties_callback_t {
+ DbusmenuClient * client;
+ GArray * listeners;
+};
+
+
+#define DBUSMENU_CLIENT_GET_PRIVATE(o) (DBUSMENU_CLIENT(o)->priv)
+#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"
/* GObject Stuff */
static void dbusmenu_client_class_init (DbusmenuClientClass *klass);
@@ -124,19 +154,27 @@ static void dbusmenu_client_finalize (GObject *object);
static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
/* Private Funcs */
-static void layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient * client);
-static void id_prop_update (DBusGProxy * proxy, gint id, gchar * property, GValue * value, DbusmenuClient * client);
-static void id_update (DBusGProxy * proxy, gint id, DbusmenuClient * client);
+static void layout_update (GDBusProxy * proxy, guint revision, gint parent, DbusmenuClient * client);
+static void id_prop_update (GDBusProxy * proxy, gint id, gchar * property, GVariant * value, DbusmenuClient * client);
+static void id_update (GDBusProxy * proxy, gint id, DbusmenuClient * client);
static void build_proxies (DbusmenuClient * client);
-static gint parse_node_get_id (xmlNodePtr node);
-static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy);
-static gint parse_layout (DbusmenuClient * client, const gchar * layout);
-static void update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * in_error, void * data);
+static DbusmenuMenuitem * parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy);
+static gint parse_layout (DbusmenuClient * client, GVariant * layout);
+static void update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data);
static void update_layout (DbusmenuClient * client);
-static void menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data);
-static void get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data);
+static void menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data);
+static void get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, properties_func callback, gpointer user_data);
static GQuark error_domain (void);
-static void item_activated (DBusGProxy * proxy, gint id, guint timestamp, DbusmenuClient * client);
+static void item_activated (GDBusProxy * proxy, gint id, guint timestamp, DbusmenuClient * client);
+static void menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data);
+static void menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties, GStrv invalidated, gpointer user_data);
+static void menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data);
+static void menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data);
+static void type_handler_destroy (gpointer user_data);
+
+/* Globals */
+static GDBusNodeInfo * dbusmenu_node_info = NULL;
+static GDBusInterfaceInfo * dbusmenu_interface_info = NULL;
/* Build a type */
G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT);
@@ -234,8 +272,22 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (DbusmenuClientClass, event_result),
NULL, NULL,
- _dbusmenu_client_marshal_VOID__OBJECT_STRING_POINTER_UINT_POINTER,
- G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_POINTER);
+ _dbusmenu_client_marshal_VOID__OBJECT_STRING_VARIANT_UINT_POINTER,
+ G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_VARIANT, G_TYPE_UINT, G_TYPE_POINTER);
+ /**
+ DbusmenuClient::icon-theme-dirs-changed:
+ @arg0: The #DbusmenuClient object
+ @arg1: A #GStrv of theme directories
+
+ Signaled when the theme directories are changed by the server.
+ */
+ signals[ICON_THEME_DIRS] = g_signal_new(DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (DbusmenuClientClass, icon_theme_dirs),
+ NULL, NULL,
+ _dbusmenu_client_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
g_object_class_install_property (object_class, PROP_DBUSOBJECT,
g_param_spec_string(DBUSMENU_CLIENT_PROP_DBUS_OBJECT, "DBus Object we represent",
@@ -247,13 +299,45 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
"Name of the DBus client we're connecting to.",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_STATUS,
+ g_param_spec_enum(DBUSMENU_CLIENT_PROP_STATUS, "Status of viewing the menus",
+ "Whether the menus should be given special visuals",
+ DBUSMENU_TYPE_STATUS, DBUSMENU_STATUS_NORMAL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_TEXT_DIRECTION,
+ g_param_spec_enum(DBUSMENU_CLIENT_PROP_TEXT_DIRECTION, "Direction text values have",
+ "Signals which direction the default text direction is for the menus",
+ DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ if (dbusmenu_node_info == NULL) {
+ GError * error = NULL;
+
+ dbusmenu_node_info = g_dbus_node_info_new_for_xml(dbus_menu_clean_xml, &error);
+ if (error != NULL) {
+ g_error("Unable to parse DBusmenu Interface description: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (dbusmenu_interface_info == NULL) {
+ dbusmenu_interface_info = g_dbus_node_info_lookup_interface(dbusmenu_node_info, DBUSMENU_INTERFACE);
+
+ if (dbusmenu_interface_info == NULL) {
+ g_error("Unable to find interface '" DBUSMENU_INTERFACE "'");
+ }
+ }
return;
}
+#define LAYOUT_PROPS_COUNT 5
+
static void
dbusmenu_client_init (DbusmenuClient *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate);
+
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(self);
priv->root = NULL;
@@ -262,22 +346,39 @@ dbusmenu_client_init (DbusmenuClient *self)
priv->dbus_name = NULL;
priv->session_bus = NULL;
+ priv->session_bus_cancel = NULL;
+
priv->menuproxy = NULL;
- priv->propproxy = NULL;
+ priv->menuproxy_cancel = NULL;
+
priv->layoutcall = NULL;
+ gchar * layout_props[LAYOUT_PROPS_COUNT + 1];
+ layout_props[0] = DBUSMENU_MENUITEM_PROP_TYPE;
+ layout_props[1] = DBUSMENU_MENUITEM_PROP_LABEL;
+ layout_props[2] = DBUSMENU_MENUITEM_PROP_VISIBLE;
+ layout_props[3] = DBUSMENU_MENUITEM_PROP_ENABLED;
+ layout_props[4] = DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY;
+ layout_props[LAYOUT_PROPS_COUNT] = NULL;
+ priv->layout_props = g_variant_new_strv((const gchar * const *)layout_props, LAYOUT_PROPS_COUNT);
+ g_variant_ref_sink(priv->layout_props);
+
priv->current_revision = 0;
priv->my_revision = 0;
- priv->dbusproxy = NULL;
+ priv->dbusproxy = 0;
priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
+ g_free, type_handler_destroy);
priv->delayed_idle = 0;
priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
priv->delayed_property_listeners = g_array_new(FALSE, FALSE, sizeof(properties_listener_t));
+ priv->text_direction = DBUSMENU_TEXT_DIRECTION_NONE;
+ priv->status = DBUSMENU_STATUS_NORMAL;
+ priv->icon_dirs = NULL;
+
return;
}
@@ -313,7 +414,7 @@ dbusmenu_client_dispose (GObject *object)
if (localerror == NULL) {
g_set_error_literal(&localerror, error_domain(), 0, "DbusmenuClient Shutdown");
}
- listener->callback(priv->menuproxy, NULL, localerror, listener->user_data);
+ listener->callback(NULL, localerror, listener->user_data);
}
}
if (localerror != NULL) {
@@ -325,22 +426,47 @@ dbusmenu_client_dispose (GObject *object)
}
if (priv->layoutcall != NULL) {
- dbus_g_proxy_cancel_call(priv->menuproxy, priv->layoutcall);
+ g_cancellable_cancel(priv->layoutcall);
+ g_object_unref(priv->layoutcall);
priv->layoutcall = NULL;
}
+
+ if (priv->layout_props != NULL) {
+ g_variant_unref(priv->layout_props);
+ priv->layout_props = NULL;
+ }
+
+ /* Bring down the menu proxy, ensure we're not
+ looking for one at the same time. */
+ if (priv->menuproxy_cancel != NULL) {
+ g_cancellable_cancel(priv->menuproxy_cancel);
+ g_object_unref(priv->menuproxy_cancel);
+ priv->menuproxy_cancel = NULL;
+ }
if (priv->menuproxy != NULL) {
+ g_signal_handlers_disconnect_matched(priv->menuproxy,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
g_object_unref(G_OBJECT(priv->menuproxy));
priv->menuproxy = NULL;
}
- if (priv->propproxy != NULL) {
- g_object_unref(G_OBJECT(priv->propproxy));
- priv->propproxy = NULL;
+
+ if (priv->dbusproxy != 0) {
+ g_bus_unwatch_name(priv->dbusproxy);
+ priv->dbusproxy = 0;
}
- if (priv->dbusproxy != NULL) {
- g_object_unref(G_OBJECT(priv->dbusproxy));
- priv->dbusproxy = NULL;
+
+ /* Bring down the session bus, ensure we're not
+ looking for one at the same time. */
+ if (priv->session_bus_cancel != NULL) {
+ g_cancellable_cancel(priv->session_bus_cancel);
+ g_object_unref(priv->session_bus_cancel);
+ priv->session_bus_cancel = NULL;
+ }
+ if (priv->session_bus != NULL) {
+ g_object_unref(priv->session_bus);
+ priv->session_bus = NULL;
}
- priv->session_bus = NULL;
if (priv->root != NULL) {
g_object_unref(G_OBJECT(priv->root));
@@ -363,6 +489,11 @@ dbusmenu_client_finalize (GObject *object)
g_hash_table_destroy(priv->type_handlers);
}
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_client_parent_class)->finalize (object);
return;
}
@@ -405,6 +536,12 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
case PROP_DBUSOBJECT:
g_value_set_string(value, priv->dbus_object);
break;
+ case PROP_STATUS:
+ g_value_set_enum(value, priv->status);
+ break;
+ case PROP_TEXT_DIRECTION:
+ g_value_set_enum(value, priv->text_direction);
+ break;
default:
g_warning("Unknown property %d.", id);
return;
@@ -445,79 +582,86 @@ find_listener (GArray * listeners, guint index, gint id)
/* Call back from getting the group properties, now we need
to unwind and call the various functions. */
static void
-get_properties_callback (DBusGProxy *proxy, GPtrArray *OUT_properties, GError *error, gpointer userdata)
+get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
{
- GArray * listeners = (GArray *)userdata;
+ properties_callback_t * cbdata = (properties_callback_t *)user_data;
+ GArray * listeners = cbdata->listeners;
int i;
+ GError * error = NULL;
+ GVariant * params = NULL;
- #ifdef MASSIVEDEBUGGING
- g_debug("Get properties callback: %d", OUT_properties->len);
- #endif
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(obj), res, &error);
if (error != NULL) {
/* If we get an error, all our callbacks need to hear about it. */
g_warning("Group Properties error: %s", error->message);
for (i = 0; i < listeners->len; i++) {
properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
- listener->callback(proxy, NULL, error, listener->user_data);
+ listener->callback(NULL, error, listener->user_data);
}
- g_array_free(listeners, TRUE);
- return;
+ g_error_free(error);
+ goto out;
}
/* Callback all the folks we can find */
- for (i = 0; i < OUT_properties->len; i++) {
- GValueArray * varray = (GValueArray *)g_ptr_array_index(OUT_properties, i);
-
- if (varray->n_values != 2) {
- g_warning("Value Array is %d entries long but we expected 2.", varray->n_values);
+ GVariant * parent = g_variant_get_child_value(params, 0);
+ GVariantIter iter;
+ g_variant_iter_init(&iter, parent);
+ GVariant * child;
+ while ((child = g_variant_iter_next_value(&iter)) != NULL) {
+ if (g_strcmp0(g_variant_get_type_string(child), "(ia{sv})") != 0) {
+ g_warning("Properties return signature is not '(ia{sv})' it is '%s'", g_variant_get_type_string(child));
+ g_variant_unref(child);
continue;
}
- GValue * vid = g_value_array_get_nth(varray, 0);
- GValue * vproperties = g_value_array_get_nth(varray, 1);
+ GVariant * idv = g_variant_get_child_value(child, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
- if (G_VALUE_TYPE(vid) != G_TYPE_INT) {
- g_warning("ID Entry not holding an int: %s", G_VALUE_TYPE_NAME(vid));
- }
- if (G_VALUE_TYPE(vproperties) != dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) {
- g_warning("Properties Entry not holding an a{sv}: %s", G_VALUE_TYPE_NAME(vproperties));
- }
-
- gint id = g_value_get_int(vid);
- GHashTable * properties = g_value_get_boxed(vproperties);
+ GVariant * properties = g_variant_get_child_value(child, 1);
properties_listener_t * listener = find_listener(listeners, 0, id);
if (listener == NULL) {
g_warning("Unable to find listener for ID %d", id);
+ g_variant_unref(properties);
+ g_variant_unref(child);
continue;
}
if (!listener->replied) {
- listener->callback(proxy, properties, NULL, listener->user_data);
+ listener->callback(properties, NULL, listener->user_data);
listener->replied = TRUE;
} else {
g_warning("Odd, we've already replied to the listener on ID %d", id);
}
+ g_variant_unref(properties);
+ g_variant_unref(child);
}
+ g_variant_unref(parent);
+ g_variant_unref(params);
/* Provide errors for those who we can't */
GError * localerror = NULL;
for (i = 0; i < listeners->len; i++) {
properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
if (!listener->replied) {
+ g_warning("Generating properties error for: %d", listener->id);
if (localerror == NULL) {
g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
}
- listener->callback(proxy, NULL, localerror, listener->user_data);
+ listener->callback(NULL, localerror, listener->user_data);
}
}
if (localerror != NULL) {
g_error_free(localerror);
}
+out:
/* Clean up */
g_array_free(listeners, TRUE);
+ g_object_unref(cbdata->client);
+ g_free(user_data);
return;
}
@@ -527,8 +671,9 @@ get_properties_callback (DBusGProxy *proxy, GPtrArray *OUT_properties, GError *e
static gboolean
get_properties_idle (gpointer user_data)
{
+ properties_callback_t * cbdata = NULL;
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
- //org_ayatana_dbusmenu_get_properties_async(priv->menuproxy, id, properties, callback, user_data);
+ g_return_val_if_fail(priv->menuproxy != NULL, TRUE);
if (priv->delayed_property_listeners->len == 0) {
g_warning("Odd, idle func got no listeners.");
@@ -536,16 +681,42 @@ get_properties_idle (gpointer user_data)
}
/* Build up an ID list to pass */
- GArray * idlist = g_array_new(FALSE, FALSE, sizeof(gint));
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
gint i;
for (i = 0; i < priv->delayed_property_listeners->len; i++) {
- g_array_append_val(idlist, g_array_index(priv->delayed_property_listeners, properties_listener_t, i).id);
- }
-
- org_ayatana_dbusmenu_get_group_properties_async(priv->menuproxy, idlist, (const gchar **)priv->delayed_property_list->data, get_properties_callback, priv->delayed_property_listeners);
-
- /* Free ID List */
- g_array_free(idlist, TRUE);
+ g_variant_builder_add(&builder, "i", g_array_index(priv->delayed_property_listeners, properties_listener_t, i).id);
+ }
+
+ GVariant * variant_ids = g_variant_builder_end(&builder);
+
+ /* Build up a prop list to pass */
+ GVariantType * type = g_variant_type_new("as");
+ g_variant_builder_init(&builder, type);
+ g_variant_type_free(type);
+ /* TODO: need to use delayed property list here */
+ GVariant * variant_props = g_variant_builder_end(&builder);
+
+ /* Combine them into a value for the parameter */
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&builder, variant_ids);
+ g_variant_builder_add_value(&builder, variant_props);
+ GVariant * variant_params = g_variant_builder_end(&builder);
+
+ cbdata = g_new(properties_callback_t, 1);
+ cbdata->listeners = priv->delayed_property_listeners;
+ cbdata->client = DBUSMENU_CLIENT(user_data);
+ g_object_ref(G_OBJECT(user_data));
+
+ g_dbus_proxy_call(priv->menuproxy,
+ "GetGroupProperties",
+ variant_params,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout */
+ NULL, /* cancellable */
+ get_properties_callback,
+ cbdata);
/* Free properties */
gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
@@ -579,22 +750,20 @@ get_properties_flush (DbusmenuClient * client)
get_properties_idle(client);
- dbus_g_connection_flush(priv->session_bus);
-
return;
}
/* A function to group all the get_properties commands to make them
more efficient over dbus. */
static void
-get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data)
+get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, properties_func callback, gpointer user_data)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
if (find_listener(priv->delayed_property_listeners, 0, id) != NULL) {
g_warning("Asking for properties from same ID twice: %d", id);
GError * localerror = NULL;
g_set_error_literal(&localerror, error_domain(), 0, "ID already queued");
- callback(priv->menuproxy, NULL, localerror, user_data);
+ callback(NULL, localerror, user_data);
g_error_free(localerror);
return;
}
@@ -628,12 +797,19 @@ get_properties_globber (DbusmenuClient * client, gint id, const gchar ** propert
priv->delayed_idle = g_idle_add(get_properties_idle, client);
}
+ /* Look at how many proprites we have queued up and
+ make it so that we don't leave too many in one
+ request. */
+ if (priv->delayed_property_listeners->len == MAX_PROPERTIES_TO_QUEUE) {
+ get_properties_flush(client);
+ }
+
return;
}
/* Called when a server item wants to activate the menu */
static void
-item_activated (DBusGProxy * proxy, gint id, guint timestamp, DbusmenuClient * client)
+item_activated (GDBusProxy * proxy, gint id, guint timestamp, DbusmenuClient * client)
{
g_return_if_fail(DBUSMENU_IS_CLIENT(client));
@@ -657,7 +833,7 @@ item_activated (DBusGProxy * proxy, gint id, guint timestamp, DbusmenuClient * c
/* Annoying little wrapper to make the right function update */
static void
-layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient * client)
+layout_update (GDBusProxy * proxy, guint revision, gint parent, DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
priv->current_revision = revision;
@@ -670,16 +846,8 @@ layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient *
/* Signal from the server that a property has changed
on one of our menuitems */
static void
-id_prop_update (DBusGProxy * proxy, gint id, gchar * property, GValue * value, DbusmenuClient * client)
+id_prop_update (GDBusProxy * proxy, gint id, gchar * property, GVariant * value, DbusmenuClient * client)
{
- #ifdef MASSIVEDEBUGGING
- GValue valstr = {0};
- g_value_init(&valstr, G_TYPE_STRING);
- g_value_transform(value, &valstr);
- g_debug("Property change sent to client for item %d property %s value %s", id, property, g_utf8_strlen(g_value_get_string(&valstr), 50) < 25 ? g_value_get_string(&valstr) : "<too long>");
- g_value_unset(&valstr);
- #endif
-
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
g_return_if_fail(priv->root != NULL);
@@ -692,7 +860,7 @@ id_prop_update (DBusGProxy * proxy, gint id, gchar * property, GValue * value, D
return;
}
- dbusmenu_menuitem_property_set_value(menuitem, property, value);
+ dbusmenu_menuitem_property_set_variant(menuitem, property, value);
return;
}
@@ -700,7 +868,7 @@ id_prop_update (DBusGProxy * proxy, gint id, gchar * property, GValue * value, D
/* Oh, lots of updates now. That silly server, they want
to change all kinds of stuff! */
static void
-id_update (DBusGProxy * proxy, gint id, DbusmenuClient * client)
+id_update (GDBusProxy * proxy, gint id, DbusmenuClient * client)
{
#ifdef MASSIVEDEBUGGING
g_debug("Client side ID update: %d", id);
@@ -720,83 +888,39 @@ id_update (DBusGProxy * proxy, gint id, DbusmenuClient * client)
/* 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)
+dbus_owner_change (GDBusConnection * connection, const gchar * name, const gchar * owner, gpointer user_data)
{
- DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- /* g_debug("Owner change: %s %s %s", name, prev, new); */
-
- if (!(new[0] != '\0' && prev[0] == '\0')) {
- /* 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;
- }
+ g_return_if_fail(DBUSMENU_IS_CLIENT(user_data));
- if (g_strcmp0(name, priv->dbus_name)) {
- /* Again, someone else's service. */
- return;
- }
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
/* Woot! A service for us to love and to hold for ever
and ever and ever! */
return build_proxies(client);
}
-/* This is the response to see if the name has an owner. If
- it does, then we should build the proxies here. Race condition
- check. */
-static void
-name_owner_check (DBusGProxy *proxy, gboolean has_owner, GError *error, gpointer userdata)
-{
- if (error != NULL) {
- return;
- }
-
- if (!has_owner) {
- return;
- }
-
- DbusmenuClient * client = DBUSMENU_CLIENT(userdata);
- build_proxies(client);
- return;
-}
-
/* 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);
+ if (priv->dbusproxy != 0) {
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);
+ priv->dbusproxy = g_bus_watch_name_on_connection(priv->session_bus,
+ priv->dbus_name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ dbus_owner_change,
+ NULL,
+ client,
+ NULL);
/* Now let's check to make sure we're not in some race
condition case. */
- org_freedesktop_DBus_name_has_owner_async(priv->dbusproxy,
- priv->dbus_name,
- name_owner_check,
- client);
+ /* TODO: Not sure how to check for names in GDBus */
return;
}
@@ -820,7 +944,11 @@ proxy_destroyed (GObject * gobj_proxy, gpointer userdata)
}
if ((gpointer)priv->menuproxy == (gpointer)gobj_proxy) {
- priv->layoutcall = NULL;
+ if (priv->layoutcall != NULL) {
+ g_cancellable_cancel(priv->layoutcall);
+ g_object_unref(priv->layoutcall);
+ priv->layoutcall = NULL;
+ }
}
priv->current_revision = 0;
@@ -830,119 +958,372 @@ proxy_destroyed (GObject * gobj_proxy, gpointer userdata)
return;
}
+/* Respond to us getting the session bus (hopefully) or handle
+ the error if not */
+void
+session_bus_cb (GObject * object, GAsyncResult * res, gpointer user_data)
+{
+ GError * error = NULL;
+
+ /* NOTE: We're not using any other variables before checking
+ the result because they could be destroyed and thus invalid */
+ GDBusConnection * bus = g_bus_get_finish(res, &error);
+ if (error != NULL) {
+ g_warning("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* If this wasn't cancelled, we should be good */
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ priv->session_bus = bus;
+
+ if (priv->session_bus_cancel != NULL) {
+ g_object_unref(priv->session_bus_cancel);
+ priv->session_bus_cancel = NULL;
+ }
+
+ /* Retry to build the proxies now that we have a bus */
+ build_proxies(DBUSMENU_CLIENT(user_data));
+
+ return;
+}
+
/* When we have a name and an object, build the two proxies and get the
first version of the layout */
static void
build_proxies (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- GError * error = NULL;
g_return_if_fail(priv->dbus_object != NULL);
g_return_if_fail(priv->dbus_name != NULL);
- priv->session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (error != NULL) {
- g_error("Unable to get session bus: %s", error->message);
- g_error_free(error);
- build_dbus_proxy(client);
+ if (priv->session_bus == NULL) {
+ /* We don't have the session bus yet, that's okay, but
+ we need to handle that. */
+
+ /* If we're already running we don't need to look again. */
+ if (priv->session_bus_cancel == NULL) {
+ priv->session_bus_cancel = g_cancellable_new();
+
+ /* Async get the session bus */
+ g_bus_get(G_BUS_TYPE_SESSION, priv->session_bus_cancel, session_bus_cb, client);
+ }
+
+ /* This function exists, it'll be called again when we get
+ the session bus so this condition will be ignored */
return;
}
- priv->propproxy = dbus_g_proxy_new_for_name_owner(priv->session_bus,
- priv->dbus_name,
- priv->dbus_object,
- DBUS_INTERFACE_PROPERTIES,
- &error);
- if (error != NULL) {
- g_warning("Unable to get property proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
- g_error_free(error);
- build_dbus_proxy(client);
- return;
+ /* Build us a menu proxy */
+ if (priv->menuproxy == NULL) {
+
+ /* Check to see if we're already building one */
+ if (priv->menuproxy_cancel == NULL) {
+ priv->menuproxy_cancel = g_cancellable_new();
+
+ g_dbus_proxy_new(priv->session_bus,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ dbusmenu_interface_info,
+ priv->dbus_name,
+ priv->dbus_object,
+ DBUSMENU_INTERFACE,
+ priv->menuproxy_cancel,
+ menuproxy_build_cb,
+ client);
+ }
}
- 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,
- priv->dbus_object,
- "org.ayatana.dbusmenu",
- &error);
+ return;
+}
+
+/* Callback when we know if the menu proxy can be created or
+ not and do something with it! */
+static void
+menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
+{
+ GError * error = NULL;
+
+ /* NOTE: We're not using any other variables before checking
+ the result because they could be destroyed and thus invalid */
+ GDBusProxy * proxy = g_dbus_proxy_new_finish(res, &error);
if (error != NULL) {
- g_warning("Unable to get dbusmenu proxy for %s on %s: %s", priv->dbus_name, priv->dbus_object, error->message);
+ g_warning("Unable to get menu proxy: %s", error->message);
g_error_free(error);
- build_dbus_proxy(client);
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;
+ /* If this wasn't cancelled, we should be good */
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ priv->menuproxy = proxy;
+
+ if (priv->menuproxy_cancel != NULL) {
+ g_object_unref(priv->menuproxy_cancel);
+ priv->menuproxy_cancel = NULL;
+ }
+
+ /* Check the text direction if available */
+ GVariant * textdir = g_dbus_proxy_get_cached_property(priv->menuproxy, "TextDirection");
+ if (textdir != NULL) {
+ if (g_variant_is_of_type(textdir, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(textdir);
+ g_variant_unref(textdir);
+ textdir = tmp;
+ }
+
+ priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(textdir, NULL));
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);
+
+ g_variant_unref(textdir);
+ }
+
+ /* Check the status if available */
+ GVariant * status = g_dbus_proxy_get_cached_property(priv->menuproxy, "Status");
+ if (status != NULL) {
+ if (g_variant_is_of_type(status, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(status);
+ g_variant_unref(status);
+ status = tmp;
+ }
+
+ priv->status = dbusmenu_status_get_value_from_nick(g_variant_get_string(status, NULL));
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);
+
+ g_variant_unref(status);
}
- dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__UINT_INT, G_TYPE_NONE, G_TYPE_UINT, G_TYPE_INT, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(priv->menuproxy, "LayoutUpdated", G_TYPE_UINT, G_TYPE_INT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "LayoutUpdated", G_CALLBACK(layout_update), client, NULL);
+ /* Get the icon theme directories if available */
+ GVariant * icon_dirs = g_dbus_proxy_get_cached_property(priv->menuproxy, "IconThemePath");
+ if (icon_dirs != NULL) {
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
+ priv->icon_dirs = g_variant_dup_strv(icon_dirs, NULL);
+ g_signal_emit(G_OBJECT(client), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);
- dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__INT_STRING_POINTER, G_TYPE_NONE, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(priv->menuproxy, "ItemPropertyUpdated", G_TYPE_INT, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "ItemPropertyUpdated", G_CALLBACK(id_prop_update), client, NULL);
+ g_variant_unref(icon_dirs);
+ }
- dbus_g_proxy_add_signal(priv->menuproxy, "ItemUpdated", G_TYPE_INT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "ItemUpdated", G_CALLBACK(id_update), client, NULL);
+ /* If we get here, we don't need the DBus proxy */
+ if (priv->dbusproxy != 0) {
+ g_bus_unwatch_name(priv->dbusproxy);
+ priv->dbusproxy = 0;
+ }
- dbus_g_object_register_marshaller(_dbusmenu_server_marshal_VOID__INT_UINT, G_TYPE_NONE, G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
- dbus_g_proxy_add_signal(priv->menuproxy, "ItemActivationRequested", G_TYPE_INT, G_TYPE_UINT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->menuproxy, "ItemActivationRequested", G_CALLBACK(item_activated), client, NULL);
+ g_signal_connect(priv->menuproxy, "g-signal", G_CALLBACK(menuproxy_signal_cb), client);
+ g_signal_connect(priv->menuproxy, "notify::g-name-owner", G_CALLBACK(menuproxy_name_changed_cb), client);
+ g_signal_connect(priv->menuproxy, "g-properties-changed", G_CALLBACK(menuproxy_prop_changed_cb), client);
- update_layout(client);
+ gchar * name_owner = g_dbus_proxy_get_name_owner(priv->menuproxy);
+ if (name_owner != NULL) {
+ update_layout(client);
+ g_free(name_owner);
+ }
return;
}
-/* Get the ID attribute of the node, parse it and
- return it. Also we're checking to ensure the node
- is a 'menu' here. */
-static gint
-parse_node_get_id (xmlNodePtr node)
+/* Handle the properites changing */
+static void
+menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties, GStrv invalidated, gpointer user_data)
{
- if (node == NULL) {
- return -1;
- }
- if (node->type != XML_ELEMENT_NODE) {
- return -1;
- }
- if (g_strcmp0((gchar *)node->name, "menu") != 0) {
- /* This kills some nodes early */
- g_warning("XML Node is not 'menu' it is '%s'", node->name);
- return -1;
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
+ DbusmenuTextDirection olddir = priv->text_direction;
+ DbusmenuStatus oldstatus = priv->status;
+ gboolean dirs_changed = FALSE;
+
+ /* Invalidate first */
+ gchar * invalid;
+ gint i = 0;
+ for (invalid = invalidated[i]; invalid != NULL; invalid = invalidated[++i]) {
+ if (g_strcmp0(invalid, "TextDirection") == 0) {
+ priv->text_direction = DBUSMENU_TEXT_DIRECTION_NONE;
+ }
+ if (g_strcmp0(invalid, "Status") == 0) {
+ priv->status = DBUSMENU_STATUS_NORMAL;
+ }
+ if (g_strcmp0(invalid, "IconThemePath") == 0) {
+ if (priv->icon_dirs != NULL) {
+ dirs_changed = TRUE;
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+ }
}
- xmlAttrPtr attrib;
- for (attrib = node->properties; attrib != NULL; attrib = attrib->next) {
- if (g_strcmp0((gchar *)attrib->name, "id") == 0) {
- if (attrib->children != NULL) {
- gint id = (guint)g_ascii_strtoll((gchar *)attrib->children->content, NULL, 10);
- /* g_debug ("Found ID: %d", id); */
- return id;
+ /* Check updates */
+ GVariantIter iters;
+ gchar * key; GVariant * value;
+ g_variant_iter_init(&iters, properties);
+ while (g_variant_iter_loop(&iters, "{sv}", &key, &value)) {
+ if (g_strcmp0(key, "TextDirection") == 0) {
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(value);
+ g_variant_unref(value);
+ value = tmp;
+ }
+
+ priv->text_direction = dbusmenu_text_direction_get_value_from_nick(g_variant_get_string(value, NULL));
+ }
+ if (g_strcmp0(key, "Status") == 0) {
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(value);
+ g_variant_unref(value);
+ value = tmp;
+ }
+
+ priv->status = dbusmenu_status_get_value_from_nick(g_variant_get_string(value, NULL));
+ }
+ if (g_strcmp0(key, "IconThemePath") == 0) {
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
}
- break;
+
+ priv->icon_dirs = g_variant_dup_strv(value, NULL);
+ dirs_changed = TRUE;
}
}
- g_warning("Unable to find an ID on the node");
- return -1;
+ if (olddir != priv->text_direction) {
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_TEXT_DIRECTION);
+ }
+
+ if (oldstatus != priv->status) {
+ g_object_notify(G_OBJECT(user_data), DBUSMENU_CLIENT_PROP_STATUS);
+ }
+
+ if (dirs_changed) {
+ g_signal_emit(G_OBJECT(user_data), signals[ICON_THEME_DIRS], 0, priv->icon_dirs, TRUE);
+ }
+
+ return;
}
-/* A small helper that calls _property_set on each hash table
- entry in the properties hash. */
+/* Handle the case where we change owners */
static void
-get_properties_helper (gpointer key, gpointer value, gpointer data)
+menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data)
{
- dbusmenu_menuitem_property_set_value((DbusmenuMenuitem *)data, (gchar *)key, (GValue *)value);
+ GDBusProxy * proxy = G_DBUS_PROXY(object);
+
+ gchar * owner = g_dbus_proxy_get_name_owner(proxy);
+
+ if (owner == NULL) {
+ /* Oh, no! We lost our owner! */
+ proxy_destroyed(G_OBJECT(proxy), user_data);
+ } else {
+ g_free(owner);
+ update_layout(DBUSMENU_CLIENT(user_data));
+ }
+
+ return;
+}
+
+/* Handle the signals out of the proxy */
+static void
+menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVariant * params, gpointer user_data)
+{
+ g_return_if_fail(DBUSMENU_IS_CLIENT(user_data));
+ DbusmenuClient * client = DBUSMENU_CLIENT(user_data);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+
+ if (g_strcmp0(signal, "LayoutUpdated") == 0) {
+ guint revision; gint parent;
+ g_variant_get(params, "(ui)", &revision, &parent);
+ layout_update(proxy, revision, parent, client);
+ } else if (priv->root == NULL) {
+ /* Drop out here, all the rest of these really need to have a root
+ node so we can just ignore them if there isn't one. */
+ } else if (g_strcmp0(signal, "ItemsPropertiesUpdated") == 0) {
+ /* Remove before adding just incase there is a duplicate, against the
+ rules, but we can handle it so let's do it. */
+ GVariantIter ritems;
+ GVariant * ritemsv = g_variant_get_child_value(params, 1);
+ g_variant_iter_init(&ritems, ritemsv);
+
+ GVariant * ritem;
+ while ((ritem = g_variant_iter_next_value(&ritems)) != NULL) {
+ GVariant * idv = g_variant_get_child_value(ritem, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
+ DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (menuitem == NULL) {
+ continue;
+ }
+
+ GVariantIter properties;
+ GVariant * propv = g_variant_get_child_value(ritem, 1);
+ g_variant_iter_init(&properties, propv);
+ gchar * property;
+
+ while (g_variant_iter_loop(&properties, "s", &property)) {
+ /* g_debug("Removing property '%s' on %d", property, id); */
+ dbusmenu_menuitem_property_remove(menuitem, property);
+ }
+ g_variant_unref(ritem);
+ g_variant_unref(propv);
+ }
+ g_variant_unref(ritemsv);
+
+ GVariantIter items;
+ GVariant * itemsv = g_variant_get_child_value(params, 0);
+ g_variant_iter_init(&items, itemsv);
+
+ GVariant * item;
+ while ((item = g_variant_iter_next_value(&items)) != NULL) {
+ GVariant * idv = g_variant_get_child_value(item, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
+
+ GVariantIter properties;
+ GVariant * propv = g_variant_get_child_value(item, 1);
+ g_variant_iter_init(&properties, propv);
+ gchar * property;
+ GVariant * value;
+
+ while (g_variant_iter_loop(&properties, "{sv}", &property, &value)) {
+ GVariant * internalvalue = value;
+ if (G_LIKELY(g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT))) {
+ /* Unboxing if needed */
+ internalvalue = g_variant_get_variant(value);
+ }
+
+ id_prop_update(proxy, id, property, internalvalue, client);
+
+ if (internalvalue != value) {
+ /* If we unboxed, we need to drop it, otherwise the
+ iter_loop function will unref for us */
+ g_variant_unref(internalvalue);
+ }
+ }
+ g_variant_unref(propv);
+ g_variant_unref(item);
+ }
+ g_variant_unref(itemsv);
+ } else if (g_strcmp0(signal, "ItemPropertyUpdated") == 0) {
+ gint id; gchar * property; GVariant * value;
+ g_variant_get(params, "(isv)", &id, &property, &value);
+ id_prop_update(proxy, id, property, value, client);
+ g_free(property);
+ g_variant_unref(value);
+ } else if (g_strcmp0(signal, "ItemUpdated") == 0) {
+ gint id;
+ g_variant_get(params, "(i)", &id);
+ id_update(proxy, id, client);
+ } else if (g_strcmp0(signal, "ItemActivationRequested") == 0) {
+ gint id; guint timestamp;
+ g_variant_get(params, "(iu)", &id, &timestamp);
+ item_activated(proxy, id, timestamp, client);
+ } else {
+ g_warning("Received signal '%s' from menu proxy that is unknown", signal);
+ }
+
return;
}
@@ -952,17 +1333,38 @@ get_properties_helper (gpointer key, gpointer value, gpointer data)
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)
+menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(data));
+ DbusmenuMenuitem * item = DBUSMENU_MENUITEM(data);
+
if (error != NULL) {
g_warning("Error getting properties on a menuitem: %s", error->message);
- g_object_unref(data);
- return;
+ goto out;
}
- g_hash_table_foreach(properties, get_properties_helper, data);
- g_hash_table_destroy(properties);
+
+ if (properties == NULL) {
+ goto out;
+ }
+
+ if (!g_variant_is_of_type(properties, G_VARIANT_TYPE("a{sv}"))) {
+ g_warning("Properties are of type '%s' instead of type '%s'", g_variant_get_type_string(properties), "a{sv}");
+ goto out;
+ }
+
+ GVariantIter iter;
+ gchar * key;
+ GVariant * value;
+
+ g_variant_iter_init(&iter, properties);
+
+ while (g_variant_iter_loop(&iter, "{sv}", &key, &value)) {
+ dbusmenu_menuitem_property_set_variant(item, key, value);
+ }
+
+out:
g_object_unref(data);
+
return;
}
@@ -970,7 +1372,7 @@ menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError
is getting recycled with the update, but we think might have prop
changes. */
static void
-menuitem_get_properties_replace_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
+menuitem_get_properties_replace_cb (GVariant * properties, GError * error, gpointer data)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(data));
gboolean have_error = FALSE;
@@ -979,18 +1381,42 @@ menuitem_get_properties_replace_cb (DBusGProxy * proxy, GHashTable * properties,
g_warning("Unable to replace properties on %d: %s", dbusmenu_menuitem_get_id(DBUSMENU_MENUITEM(data)), error->message);
have_error = TRUE;
}
+
+ if (properties == NULL) {
+ have_error = TRUE;
+ }
- GList * current_props = NULL;
+ /* Get the list of the current properties */
+ GList * current_props = dbusmenu_menuitem_properties_list(DBUSMENU_MENUITEM(data));
+ GList * tmp = NULL;
- for (current_props = dbusmenu_menuitem_properties_list(DBUSMENU_MENUITEM(data));
- current_props != NULL ; current_props = g_list_next(current_props)) {
- if (have_error || g_hash_table_lookup(properties, current_props->data) == NULL) {
- dbusmenu_menuitem_property_remove(DBUSMENU_MENUITEM(data), (const gchar *)current_props->data);
+ if (!have_error && g_variant_is_of_type(properties, G_VARIANT_TYPE("a{sv}"))) {
+ GVariantIter iter;
+ g_variant_iter_init(&iter, properties);
+ gchar * name; GVariant * value;
+
+ /* Remove the entries from the current list that we have new
+ values for. This way we don't create signals of them being
+ removed with the duplication of the value being changed. */
+ while (g_variant_iter_loop(&iter, "{sv}", &name, &value)) {
+ for (tmp = current_props; tmp != NULL; tmp = g_list_next(tmp)) {
+ if (g_strcmp0((gchar *)tmp->data, name) == 0) {
+ current_props = g_list_delete_link(current_props, tmp);
+ break;
+ }
+ }
}
}
+ /* Remove all entries that we're not getting values for, we can
+ assume that they no longer exist */
+ for (tmp = current_props; tmp != NULL && have_error == FALSE; tmp = g_list_next(tmp)) {
+ dbusmenu_menuitem_property_remove(DBUSMENU_MENUITEM(data), (const gchar *)tmp->data);
+ }
+ g_list_free(current_props);
+
if (!have_error) {
- menuitem_get_properties_cb(proxy, properties, error, data);
+ menuitem_get_properties_cb(properties, error, data);
} else {
g_object_unref(data);
}
@@ -1001,38 +1427,41 @@ menuitem_get_properties_replace_cb (DBusGProxy * proxy, GHashTable * properties,
/* This is a different get properites call back that also sends
new signals. It basically is a small wrapper around the original. */
static void
-menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
+menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer data)
{
g_return_if_fail(data != NULL);
newItemPropData * propdata = (newItemPropData *)data;
if (error != NULL) {
g_warning("Error getting properties on a new menuitem: %s", error->message);
- g_object_unref(propdata->item);
- g_free(data);
- return;
+ goto out;
+ }
+
+ if (properties == NULL) {
+ g_warning("Not realizing new item as properties for it were unavailable");
+ goto out;
}
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(propdata->client);
/* Extra ref as get_properties will unref once itself */
g_object_ref(propdata->item);
- menuitem_get_properties_cb (proxy, properties, error, propdata->item);
+ menuitem_get_properties_cb (properties, error, propdata->item);
gboolean handled = FALSE;
const gchar * type;
- DbusmenuClientTypeHandler newfunc = NULL;
+ type_handler_t * th = NULL;
type = dbusmenu_menuitem_property_get(propdata->item, DBUSMENU_MENUITEM_PROP_TYPE);
if (type != NULL) {
- newfunc = g_hash_table_lookup(priv->type_handlers, type);
+ th = (type_handler_t *)g_hash_table_lookup(priv->type_handlers, type);
} else {
- newfunc = g_hash_table_lookup(priv->type_handlers, DBUSMENU_CLIENT_TYPES_DEFAULT);
+ th = (type_handler_t *)g_hash_table_lookup(priv->type_handlers, DBUSMENU_CLIENT_TYPES_DEFAULT);
}
- if (newfunc != NULL) {
- handled = newfunc(propdata->item, propdata->parent, propdata->client);
+ if (th != NULL && th->cb != NULL) {
+ handled = th->cb(propdata->item, propdata->parent, propdata->client, th->user_data);
}
#ifdef MASSIVEDEBUGGING
@@ -1044,6 +1473,7 @@ menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GEr
g_signal_emit(G_OBJECT(propdata->client), signals[NEW_MENUITEM], 0, propdata->item, TRUE);
}
+out:
g_object_unref(propdata->item);
g_free(propdata);
@@ -1053,61 +1483,73 @@ menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GEr
/* Respond to the call function to make sure that the other side
got it, or print a warning. */
static void
-menuitem_call_cb (DBusGProxy * proxy, GError * error, gpointer userdata)
+menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
{
+ GError * error = NULL;
event_data_t * edata = (event_data_t *)userdata;
+ GVariant * params;
+
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
if (error != NULL) {
g_warning("Unable to call event '%s' on menu item %d: %s", edata->event, dbusmenu_menuitem_get_id(edata->menuitem), error->message);
}
- g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, &edata->data, edata->timestamp, error, TRUE);
+ g_signal_emit(edata->client, signals[EVENT_RESULT], 0, edata->menuitem, edata->event, edata->variant, edata->timestamp, error, TRUE);
- g_value_unset(&edata->data);
+ g_variant_unref(edata->variant);
g_free(edata->event);
g_object_unref(edata->menuitem);
+ g_object_unref(edata->client);
g_free(edata);
+ if (G_UNLIKELY(error != NULL)) {
+ g_error_free(error);
+ }
+ if (G_LIKELY(params != NULL)) {
+ g_variant_unref(params);
+ }
+
return;
}
/* Sends the event over DBus to the server on the other side
of the bus. */
void
-dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, const GValue * value, guint timestamp)
+dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name, GVariant * variant, guint timestamp, DbusmenuMenuitem * mi)
{
g_return_if_fail(DBUSMENU_IS_CLIENT(client));
g_return_if_fail(id >= 0);
g_return_if_fail(name != NULL);
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
g_warning("Asked to activate a menuitem %d that we don't know about", id);
return;
}
- if (value == NULL) {
- GValue internalval = {0};
- g_value_init(&internalval, G_TYPE_INT);
- g_value_set_int(&internalval, 0);
- value = &internalval;
+ if (variant == NULL) {
+ variant = g_variant_new_int32(0);
}
event_data_t * edata = g_new0(event_data_t, 1);
edata->client = client;
+ g_object_ref(client);
edata->menuitem = mi;
g_object_ref(edata->menuitem);
edata->event = g_strdup(name);
- g_value_init(&edata->data, G_VALUE_TYPE(value));
- g_value_copy(value, &edata->data);
edata->timestamp = timestamp;
-
- DBusGAsyncData *stuff;
- stuff = g_slice_new (DBusGAsyncData);
- stuff->cb = G_CALLBACK (menuitem_call_cb);
- stuff->userdata = edata;
- dbus_g_proxy_begin_call_with_timeout (priv->menuproxy, "Event", org_ayatana_dbusmenu_event_async_callback, stuff, _dbus_glib_async_data_free, 1000, G_TYPE_INT, id, G_TYPE_STRING, name, G_TYPE_VALUE, value, G_TYPE_UINT, timestamp, G_TYPE_INVALID);
+ edata->variant = variant;
+ g_variant_ref_sink(variant);
+
+ g_dbus_proxy_call(priv->menuproxy,
+ "Event",
+ g_variant_new("(isvu)", id, name, variant, timestamp),
+ G_DBUS_CALL_FLAGS_NONE,
+ 1000, /* timeout */
+ NULL, /* cancellable */
+ menuitem_call_cb,
+ edata);
return;
}
@@ -1122,14 +1564,24 @@ struct _about_to_show_t {
/* Reports errors and responds to update request that were a result
of sending the about to show signal. */
static void
-about_to_show_cb (DBusGProxy * proxy, gboolean need_update, GError * error, gpointer userdata)
+about_to_show_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
{
+ gboolean need_update = FALSE;
+ GError * error = NULL;
about_to_show_t * data = (about_to_show_t *)userdata;
+ GVariant * params = NULL;
+
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
if (error != NULL) {
g_warning("Unable to send about_to_show: %s", error->message);
/* Note: we're just ensuring only the callback gets called */
need_update = FALSE;
+ g_error_free(error);
+ error = NULL;
+ } else {
+ g_variant_get(params, "(b)", &need_update);
+ g_variant_unref(params);
}
/* If we need to update, do that first. */
@@ -1160,7 +1612,14 @@ dbusmenu_client_send_about_to_show(DbusmenuClient * client, gint id, void (*cb)(
data->cb_data = cb_data;
g_object_ref(client);
- org_ayatana_dbusmenu_about_to_show_async (priv->menuproxy, id, about_to_show_cb, data);
+ g_dbus_proxy_call(priv->menuproxy,
+ "AboutToShow",
+ g_variant_new("(i)", id),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout */
+ NULL, /* cancellable */
+ about_to_show_cb,
+ data);
return;
}
@@ -1206,10 +1665,16 @@ parse_layout_update (DbusmenuMenuitem * item, DbusmenuClient * client)
/* Parse recursively through the XML and make it into
objects as need be */
static DbusmenuMenuitem *
-parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
+parse_layout_xml(DbusmenuClient * client, GVariant * layout, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, GDBusProxy * proxy)
{
+ if (layout == NULL) {
+ return NULL;
+ }
+
/* First verify and figure out what we've got */
- gint id = parse_node_get_id(node);
+ GVariant * idv = g_variant_get_child_value(layout, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
if (id < 0) {
return NULL;
}
@@ -1221,20 +1686,34 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
g_return_val_if_fail(id == dbusmenu_menuitem_get_id(item), NULL);
/* Some variables */
- xmlNodePtr children;
- guint position;
+ GVariantIter children;
+ GVariant * childrenv;
+
+ childrenv = g_variant_get_child_value(layout, 2);
+ g_variant_iter_init(&children, childrenv);
+
+ guint position = 0;
GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item));
/* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
/* Go through all the XML Nodes and make sure that we have menuitems
to cover those XML nodes. */
- for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
+ GVariant * child;
+ while ((child = g_variant_iter_next_value(&children)) != NULL) {
/* g_debug("Looking at child: %d", position); */
- gint childid = parse_node_get_id(children);
+ if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(child);
+ g_variant_unref(child);
+ child = tmp;
+ }
+
+ GVariant * childidv = g_variant_get_child_value(child, 0);
+ gint childid = g_variant_get_int32(childidv);
+ g_variant_unref(childidv);
if (childid < 0) {
/* Don't increment the position when there isn't a valid
node in the XML tree. It's probably a comment. */
- position--;
+ g_variant_unref(child);
continue;
}
DbusmenuMenuitem * childmi = NULL;
@@ -1267,6 +1746,35 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
dbusmenu_menuitem_child_reorder(item, childmi, position);
parse_layout_update(childmi, client);
}
+
+ /* Apply known properties sent in the structure to the
+ menu item. Sometimes they may just be copies */
+ if (childmi != NULL) {
+ GVariantIter iter;
+ gchar * prop;
+ GVariant * value;
+ GVariant * child_props;
+
+ /* Set the type first as it can manage the behavior of
+ all other properties. */
+ child_props = g_variant_get_child_value(child, 1);
+ g_variant_iter_init(&iter, child_props);
+ while (g_variant_iter_loop(&iter, "{sv}", &prop, &value)) {
+ if (g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TYPE) == 0) {
+ dbusmenu_menuitem_property_set_variant(childmi, prop, value);
+ }
+ }
+
+ /* Now go through and do all the properties. */
+ g_variant_iter_init(&iter, child_props);
+ while (g_variant_iter_loop(&iter, "{sv}", &prop, &value)) {
+ dbusmenu_menuitem_property_set_variant(childmi, prop, value);
+ }
+ g_variant_unref(child_props);
+ }
+
+ position++;
+ g_variant_unref(child);
}
/* Remove any children that are no longer used by this version of
@@ -1283,18 +1791,31 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
/* We've got everything built up at this node and reconcilled */
- /* Flush the properties requests */
- get_properties_flush(client);
+ /* Flush the properties requests if this is the first level */
+ if (parent != NULL && dbusmenu_menuitem_get_id(parent) == 0) {
+ get_properties_flush(client);
+ }
/* now it's time to recurse down the tree. */
- children = node->children;
+ g_variant_iter_init(&children, childrenv);
+
+ child = g_variant_iter_next_value(&children);
GList * childmis = dbusmenu_menuitem_get_children(item);
- while (children != NULL && childmis != NULL) {
- gint xmlid = parse_node_get_id(children);
+ while (child != NULL && childmis != NULL) {
+ if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
+ GVariant * tmp = g_variant_get_variant(child);
+ g_variant_unref(child);
+ child = tmp;
+ }
+
+ GVariant * xmlidv = g_variant_get_child_value(child, 0);
+ gint xmlid = g_variant_get_int32(xmlidv);
+ g_variant_unref(xmlidv);
/* If this isn't a valid menu item we need to move on
until we have one. This avoids things like comments. */
if (xmlid < 0) {
- children = children->next;
+ g_variant_unref(child);
+ child = g_variant_iter_next_value(&children);
continue;
}
@@ -1303,13 +1824,17 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
g_debug("Recursing parse_layout_xml. XML ID: %d MI ID: %d", xmlid, miid);
#endif
- parse_layout_xml(client, children, DBUSMENU_MENUITEM(childmis->data), item, proxy);
+ parse_layout_xml(client, child, DBUSMENU_MENUITEM(childmis->data), item, proxy);
- children = children->next;
+ g_variant_unref(child);
+ child = g_variant_iter_next_value(&children);
childmis = g_list_next(childmis);
}
- if (children != NULL) {
- g_warning("Sync failed, now we've got extra XML nodes.");
+
+ g_variant_unref(childrenv);
+
+ if (child != NULL) {
+ g_warning("Sync failed, now we've got extra layout nodes.");
}
if (childmis != NULL) {
g_warning("Sync failed, now we've got extra menu items.");
@@ -1321,7 +1846,7 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
/* Take the layout passed to us over DBus and turn it into
a set of beautiful objects */
static gint
-parse_layout (DbusmenuClient * client, const gchar * layout)
+parse_layout (DbusmenuClient * client, GVariant * layout)
{
#ifdef MASSIVEDEBUGGING
g_debug("Client Parsing a new layout");
@@ -1329,17 +1854,6 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- xmlDocPtr xmldoc;
-
- /* No one should need more characters than this! */
- xmldoc = xmlReadMemory(layout, g_utf8_strlen(layout, 1024*1024), "dbusmenu.xml", NULL, 0);
-
- xmlNodePtr root = xmlDocGetRootElement(xmldoc);
-
- if (root == NULL) {
- g_warning("Unable to get root node of menu XML");
- }
-
DbusmenuMenuitem * oldroot = priv->root;
if (priv->root == NULL) {
@@ -1348,11 +1862,10 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
parse_layout_update(priv->root, client);
}
- priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
- xmlFreeDoc(xmldoc);
+ priv->root = parse_layout_xml(client, layout, 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);
+ g_warning("Unable to parse layout on client %s object %s: %s", priv->dbus_name, priv->dbus_object, g_variant_print(layout, TRUE));
}
if (priv->root != oldroot) {
@@ -1377,24 +1890,38 @@ parse_layout (DbusmenuClient * client, const gchar * layout)
/* When the layout property returns, here's where we take care of that. */
static void
-update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, void * data)
+update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
{
DbusmenuClient * client = DBUSMENU_CLIENT(data);
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ GError * error = NULL;
+ GVariant * params = NULL;
+ GVariant * layout = NULL;
+
+ params = g_dbus_proxy_call_finish(G_DBUS_PROXY(proxy), res, &error);
+
if (error != NULL) {
- g_warning("Getting layout failed on client %s object %s: %s", priv->dbus_name, priv->dbus_object, error->message);
- return;
+ g_warning("Getting layout failed: %s", error->message);
+ g_error_free(error);
+ goto out;
}
- if (!parse_layout(client, xml)) {
+ GVariant * revv = g_variant_get_child_value(params, 0);
+ guint rev = g_variant_get_uint32(revv);
+ g_variant_unref(revv);
+
+ layout = g_variant_get_child_value(params, 1);
+
+ guint parseable = parse_layout(client, layout);
+
+ if (parseable == 0) {
g_warning("Unable to parse layout!");
- return;
+ goto out;
}
priv->my_revision = rev;
/* g_debug("Root is now: 0x%X", (unsigned int)priv->root); */
- priv->layoutcall = NULL;
#ifdef MASSIVEDEBUGGING
g_debug("Client signaling layout has changed.");
#endif
@@ -1406,6 +1933,21 @@ update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * error, vo
update_layout(client);
}
+out:
+ if (priv->layoutcall != NULL) {
+ g_object_unref(priv->layoutcall);
+ priv->layoutcall = NULL;
+ }
+
+ if (layout != NULL) {
+ g_variant_unref(layout);
+ }
+
+ if (params != NULL) {
+ g_variant_unref(params);
+ }
+
+ g_object_unref(G_OBJECT(client));
return;
}
@@ -1415,36 +1957,60 @@ static void
update_layout (DbusmenuClient * client)
{
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ g_return_if_fail(priv->layout_props != NULL);
if (priv->menuproxy == NULL) {
return;
}
+ gchar * name_owner = g_dbus_proxy_get_name_owner(priv->menuproxy);
+ if (name_owner == NULL) {
+ return;
+ }
+ g_free(name_owner);
+
if (priv->layoutcall != NULL) {
return;
}
- priv->layoutcall = org_ayatana_dbusmenu_get_layout_async(priv->menuproxy,
- 0, /* Parent is the root */
- update_layout_cb,
- client);
+ priv->layoutcall = g_cancellable_new();
+
+ GVariantBuilder tupleb;
+ g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(0)); // root
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(-1)); // recurse
+ g_variant_builder_add_value(&tupleb, priv->layout_props); // props
+
+ GVariant * args = g_variant_builder_end(&tupleb);
+ // g_debug("Args (type: %s): %s", g_variant_get_type_string(args), g_variant_print(args, TRUE));
+
+ g_object_ref(G_OBJECT(client));
+ g_dbus_proxy_call(priv->menuproxy,
+ "GetLayout",
+ args,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* timeout */
+ priv->layoutcall, /* cancellable */
+ update_layout_cb,
+ client);
return;
}
/* Public API */
/**
- dbusmenu_client_new:
- @name: The DBus name for the server to connect to
- @object: The object on the server to monitor
-
- This function creates a new client that connects to a specific
- server on DBus. That server is at a specific location sharing
- a known object. The interface is assumed by the code to be
- the DBus menu interface. The newly created client will start
- sending out events as it syncs up with the server.
-
- Return value: A brand new #DbusmenuClient
+ * dbusmenu_client_new:
+ * @name: The DBus name for the server to connect to
+ * @object: The object on the server to monitor
+ *
+ * This function creates a new client that connects to a specific
+ * server on DBus. That server is at a specific location sharing
+ * a known object. The interface is assumed by the code to be
+ * the DBus menu interface. The newly created client will start
+ * sending out events as it syncs up with the server.
+ *
+ * Return value: A brand new #DbusmenuClient
*/
DbusmenuClient *
dbusmenu_client_new (const gchar * name, const gchar * object)
@@ -1458,21 +2024,21 @@ dbusmenu_client_new (const gchar * name, const gchar * object)
}
/**
- dbusmenu_client_get_root:
- @client: The #DbusmenuClient to get the root node from
-
- Grabs the root node for the specified client @client. This
- function may block. It will block if there is currently a
- call to update the layout, it will block on that layout
- updated and then return the newly updated layout. Chances
- are that this update is in the queue for the mainloop as
- it would have been requested some time ago, but in theory
- it could block longer.
-
- Return value: A #DbusmenuMenuitem representing the root of
- menu on the server. If there is no server or there is
- an error receiving its layout it'll return #NULL.
-*/
+ * dbusmenu_client_get_root:
+ * @client: The #DbusmenuClient to get the root node from
+ *
+ * Grabs the root node for the specified client @client. This
+ * function may block. It will block if there is currently a
+ * call to update the layout, it will block on that layout
+ * updated and then return the newly updated layout. Chances
+ * are that this update is in the queue for the mainloop as
+ * it would have been requested some time ago, but in theory
+ * it could block longer.
+ *
+ * Return value: (transfer none): A #DbusmenuMenuitem representing the root of
+ * 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)
{
@@ -1480,10 +2046,6 @@ dbusmenu_client_get_root (DbusmenuClient * client)
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- if (priv->propproxy == NULL) {
- return NULL;
- }
-
#ifdef MASSIVEDEBUGGING
g_debug("Client get root: %X", (guint)priv->root);
#endif
@@ -1491,30 +2053,74 @@ dbusmenu_client_get_root (DbusmenuClient * client)
return priv->root;
}
+/* Remove the type handler when we're all done with it */
+static void
+type_handler_destroy (gpointer user_data)
+{
+ type_handler_t * th = (type_handler_t *)user_data;
+ if (th->destroy_cb != NULL) {
+ th->destroy_cb(th->user_data);
+ }
+ g_free(th->type);
+ g_free(th);
+ return;
+}
+
/**
- dbusmenu_client_add_type_handler:
- @client: Client where we're getting types coming in
- @type: A text string that will be matched with the 'type'
- property on incoming menu items
- @newfunc: The function that will be executed with those new
- items when they come in.
-
- This function connects into the type handling of the #DbusmenuClient.
- Every new menuitem that comes in immediately gets asked for it's
- properties. When we get those properties we check the 'type'
- property and look to see if it matches a handler that is known
- by the client. If so, the @newfunc function is executed on that
- #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem
- signal is sent.
-
- In the future the known types will be sent to the server so that it
- can make choices about the menu item types availble.
-
- Return value: If registering the new type was successful.
+ * dbusmenu_client_add_type_handler:
+ * @client: Client where we're getting types coming in
+ * @type: A text string that will be matched with the 'type'
+ * property on incoming menu items
+ * @newfunc: (scope notified): The function that will be executed with those new
+ * items when they come in.
+ *
+ * This function connects into the type handling of the #DbusmenuClient.
+ * Every new menuitem that comes in immediately gets asked for it's
+ * properties. When we get those properties we check the 'type'
+ * property and look to see if it matches a handler that is known
+ * by the client. If so, the @newfunc function is executed on that
+ * #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem
+ * signal is sent.
+ *
+ * In the future the known types will be sent to the server so that it
+ * can make choices about the menu item types availble.
+ *
+ * Return value: If registering the new type was successful.
*/
gboolean
dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, DbusmenuClientTypeHandler newfunc)
{
+ return dbusmenu_client_add_type_handler_full(client, type, newfunc, NULL, NULL);
+}
+
+/**
+ * dbusmenu_client_add_type_handler_full:
+ * @client: Client where we're getting types coming in
+ * @type: A text string that will be matched with the 'type'
+ * property on incoming menu items
+ * @newfunc: (scope notified): The function that will be executed with those new
+ * items when they come in.
+ * @user_data: Data passed to @newfunc when it is called
+ * @destroy_func: A function that is called when the type handler is
+ * removed (usually on client destruction) which will free
+ * the resources in @user_data.
+ *
+ * This function connects into the type handling of the #DbusmenuClient.
+ * Every new menuitem that comes in immediately gets asked for it's
+ * properties. When we get those properties we check the 'type'
+ * property and look to see if it matches a handler that is known
+ * by the client. If so, the @newfunc function is executed on that
+ * #DbusmenuMenuitem. If not, then the DbusmenuClient::new-menuitem
+ * signal is sent.
+ *
+ * In the future the known types will be sent to the server so that it
+ * can make choices about the menu item types availble.
+ *
+ * Return value: If registering the new type was successful.
+*/
+gboolean
+dbusmenu_client_add_type_handler_full (DbusmenuClient * client, const gchar * type, DbusmenuClientTypeHandler newfunc, gpointer user_data, GDestroyNotify destroy_func)
+{
g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), FALSE);
g_return_val_if_fail(type != NULL, FALSE);
@@ -1535,6 +2141,70 @@ dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, D
return FALSE;
}
- g_hash_table_insert(priv->type_handlers, g_strdup(type), newfunc);
+ type_handler_t * th = g_new0(type_handler_t, 1);
+ th->client = client;
+ th->cb = newfunc;
+ th->destroy_cb = destroy_func;
+ th->user_data = user_data;
+ th->type = g_strdup(type);
+
+ g_hash_table_insert(priv->type_handlers, g_strdup(type), th);
return TRUE;
}
+
+/**
+ dbusmenu_client_get_text_direction:
+ @client: #DbusmenuClient to check the text direction on
+
+ Gets the text direction that the server is exporting. If
+ the server is not exporting a direction then the value
+ #DBUSMENU_TEXT_DIRECTION_NONE will be returned.
+
+ Return value: Text direction being exported.
+*/
+DbusmenuTextDirection
+dbusmenu_client_get_text_direction (DbusmenuClient * client)
+{
+ g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), DBUSMENU_TEXT_DIRECTION_NONE);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ return priv->text_direction;
+}
+
+/**
+ dbusmenu_client_get_status:
+ @client: #DbusmenuClient to check the status on
+
+ Gets the recommended current status that the server
+ is exporting for the menus. In situtations where the
+ value is #DBUSMENU_STATUS_NOTICE it is recommended that
+ the client show the menus to the user an a more noticible
+ way.
+
+ Return value: Status being exported.
+*/
+DbusmenuStatus
+dbusmenu_client_get_status (DbusmenuClient * client)
+{
+ g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), DBUSMENU_STATUS_NORMAL);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ return priv->status;
+}
+
+/**
+ * dbusmenu_client_get_icon_paths:
+ * @client: The #DbusmenuClient to get the icon paths from
+ *
+ * Gets the stored and exported icon paths from the client.
+ *
+ * Return value: (transfer none): A NULL-terminated list of icon paths with
+ * memory managed by the client. Duplicate if you want
+ * to keep them.
+ */
+const GStrv
+dbusmenu_client_get_icon_paths (DbusmenuClient * client)
+{
+ g_return_val_if_fail(DBUSMENU_IS_CLIENT(client), NULL);
+ DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
+ return priv->icon_dirs;
+}
+
diff --git a/libdbusmenu-glib/client.h b/libdbusmenu-glib/client.h
index 5d4b5c3..1cb9ee5 100644
--- a/libdbusmenu-glib/client.h
+++ b/libdbusmenu-glib/client.h
@@ -33,6 +33,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include <glib-object.h>
#include "menuitem.h"
+#include "types.h"
G_BEGIN_DECLS
@@ -43,28 +44,107 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_CLIENT))
#define DBUSMENU_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_CLIENT, DbusmenuClientClass))
+/**
+ * DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED:
+ *
+ * String to attach to signal #DbusmenuClient::layout-updated
+ */
#define DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED "layout-updated"
+/**
+ * DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED:
+ *
+ * String to attach to signal #DbusmenuClient::root-changed
+ */
#define DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED "root-changed"
+/**
+ * DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM:
+ *
+ * String to attach to signal #DbusmenuClient::new-menuitem
+ */
#define DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM "new-menuitem"
+/**
+ * DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE:
+ *
+ * String to attach to signal #DbusmenuClient::item-activate
+ */
#define DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE "item-activate"
+/**
+ * DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT:
+ *
+ * String to attach to signal #DbusmenuClient::event-result
+ */
#define DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT "event-result"
+/**
+ * DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED:
+ *
+ * String to attach to signal #DbusmenuClient::icon-theme-dirs-changed
+ */
+#define DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED "icon-theme-dirs-changed"
+/**
+ * DBUSMENU_CLIENT_PROP_DBUS_NAME:
+ *
+ * String to access property #DbusmenuClient:dbus-name
+ */
#define DBUSMENU_CLIENT_PROP_DBUS_NAME "dbus-name"
+/**
+ * DBUSMENU_CLIENT_PROP_DBUS_OBJECT:
+ *
+ * String to access property #DbusmenuClient:dbus-object
+ */
#define DBUSMENU_CLIENT_PROP_DBUS_OBJECT "dbus-object"
+/**
+ * DBUSMENU_CLIENT_PROP_STATUS:
+ *
+ * String to access property #DbusmenuClient:status
+ */
+#define DBUSMENU_CLIENT_PROP_STATUS "status"
+/**
+ * DBUSMENU_CLIENT_PROP_TEXT_DIRECTION:
+ *
+ * String to access property #DbusmenuClient:text-direction
+ */
+#define DBUSMENU_CLIENT_PROP_TEXT_DIRECTION "text-direction"
+/**
+ * DBUSMENU_CLIENT_TYPES_DEFAULT:
+ *
+ * Used to set the 'type' property on a menu item to create
+ * a standard menu item.
+ */
#define DBUSMENU_CLIENT_TYPES_DEFAULT "standard"
+/**
+ * DBUSMENU_CLIENT_TYPES_SEPARATOR:
+ *
+ * Used to set the 'type' property on a menu item to create
+ * a separator menu item.
+ */
#define DBUSMENU_CLIENT_TYPES_SEPARATOR "separator"
+/**
+ * DBUSMENU_CLIENT_TYPES_IMAGE:
+ *
+ * Used to set the 'type' property on a menu item to create
+ * an image menu item. Deprecated as standard menu items now
+ * support images as well.
+ */
#define DBUSMENU_CLIENT_TYPES_IMAGE "standard"
+typedef struct _DbusmenuClientPrivate DbusmenuClientPrivate;
+
/**
DbusmenuClientClass:
@parent_class: #GObjectClass
@layout_updated: Slot for #DbusmenuClient::layout-updated.
+ @root_changed: Slot for #DbusmenuClient::root-changed.
@new_menuitem: Slot for #DbusmenuClient::new-menuitem.
@item_activate: Slot for #DbusmenuClient::item-activate.
@event_result: Slot for #DbusmenuClient::event-error.
+ @icon_theme_dirs: Slot for #DbusmenuClient::icon-theme-dirs-changed.
@reserved1: Reserved for future use.
@reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+ @reserved5: Reserved for future use.
A simple class that takes all of the information from a
#DbusmenuServer over DBus and makes the same set of
@@ -78,18 +158,19 @@ struct _DbusmenuClientClass {
void (*root_changed) (DbusmenuMenuitem * newroot);
void (*new_menuitem) (DbusmenuMenuitem * newitem);
void (*item_activate) (DbusmenuMenuitem * item, guint timestamp);
- void (*event_result) (DbusmenuMenuitem * item, gchar * event, GValue * data, guint timestamp, GError * error);
+ void (*event_result) (DbusmenuMenuitem * item, gchar * event, GVariant * data, guint timestamp, GError * error);
+ void (*icon_theme_dirs) (DbusmenuMenuitem * item, gpointer theme_dirs, GError * error);
- /* Reserved for future use */
+ /*< Private >*/
void (*reserved1) (void);
void (*reserved2) (void);
- /* void (*reserved3) (void); */
- /* void (*reserved4) (void); */
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+ void (*reserved5) (void);
};
/**
DbusmenuClient:
- @parent: #GObject.
The client for a #DbusmenuServer creating a shared
object set of #DbusmenuMenuitem objects.
@@ -97,9 +178,25 @@ struct _DbusmenuClientClass {
typedef struct _DbusmenuClient DbusmenuClient;
struct _DbusmenuClient {
GObject parent;
+
+ /*< Private >*/
+ DbusmenuClientPrivate * priv;
};
-typedef gboolean (*DbusmenuClientTypeHandler) (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
+/**
+ DbusmenuClientTypeHandler:
+ @newitem: The #DbusmenuMenuitem that was created
+ @parent: The parent of @newitem or #NULL if none
+ @client: A pointer to the #DbusmenuClient
+ @user_data: The data you gave us
+
+ The type handler is called when a dbusmenu item is created
+ with a matching type as setup in #dbusmenu_client_add_type_handler
+
+ Return value: #TRUE if the type has been handled. #FALSE if this
+ function was somehow unable to handle it.
+*/
+typedef gboolean (*DbusmenuClientTypeHandler) (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
GType dbusmenu_client_get_type (void);
DbusmenuClient * dbusmenu_client_new (const gchar * name,
@@ -108,15 +205,14 @@ DbusmenuMenuitem * dbusmenu_client_get_root (DbusmenuClient * client)
gboolean dbusmenu_client_add_type_handler (DbusmenuClient * client,
const gchar * type,
DbusmenuClientTypeHandler newfunc);
-void dbusmenu_client_send_event (DbusmenuClient * client,
- gint id,
- const gchar * name,
- const GValue * value,
- guint timestamp);
-void dbusmenu_client_send_about_to_show(DbusmenuClient * client,
- gint id,
- void (*cb) (gpointer user_data),
- gpointer cb_data);
+gboolean dbusmenu_client_add_type_handler_full (DbusmenuClient * client,
+ const gchar * type,
+ DbusmenuClientTypeHandler newfunc,
+ gpointer user_data,
+ GDestroyNotify destroy_func);
+DbusmenuTextDirection dbusmenu_client_get_text_direction (DbusmenuClient * client);
+DbusmenuStatus dbusmenu_client_get_status (DbusmenuClient * client);
+const GStrv dbusmenu_client_get_icon_paths (DbusmenuClient * client);
/**
SECTION:client
diff --git a/libdbusmenu-glib/dbus-menu.xml b/libdbusmenu-glib/dbus-menu.xml
index 9e8013c..4b5a5d8 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -28,15 +28,15 @@ You should have received a copy of both the GNU Lesser General Public
License version 3 and version 2.1 along with this program. If not, see
<http://www.gnu.org/licenses/>
-->
-<node name="/" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd">
+<node name="/" xmlns:dox="http://www.canonical.com/dbus/dox.dtd">
<dox:d><![CDATA[
@mainpage
The goal of DBusMenu is to expose menus on DBus.
- Main interface is documented here: @ref org::ayatana::dbusmenu
+ Main interface is documented here: @ref com::canonical::dbusmenu
]]></dox:d>
- <interface name="org.ayatana.dbusmenu">
+ <interface name="com.canonical.dbusmenu">
<dox:d><![CDATA[
A DBus interface to expose menus on DBus.
@@ -157,6 +157,19 @@ License version 3 and version 2.1 along with this program. If not, see
</td>
<td>""</td>
</tr>
+ <tr>
+ <td>disposition</td>
+ <td>string</td>
+ <td>
+ How the menuitem feels the information it's displaying to the
+ user should be presented.
+ - "normal" a standard menu item
+ - "informative" providing additional information to the user
+ - "warning" looking at potentially harmful results
+ - "alert" something bad could potentially happen
+ </td>
+ <td>"normal"</td>
+ </tr>
</table>
Vendor specific properties can be added by prefixing them with
@@ -164,51 +177,83 @@ License version 3 and version 2.1 along with this program. If not, see
]]></dox:d>
<!-- Properties -->
- <property name="version" type="u" access="read">
+ <property name="Version" type="u" access="read">
<dox:d>
Provides the version of the DBusmenu API that this API is
implementing.
</dox:d>
</property>
+ <property name="TextDirection" type="s" access="read">
+ <dox:d>
+ Represents the way the text direction of the application. This
+ allows the server to handle mismatches intelligently. For left-
+ to-right the string is "ltr" for right-to-left it is "rtl".
+ </dox:d>
+ </property>
+
+ <property name="Status" type="s" access="read">
+ <dox:d>
+ Tells if the menus are in a normal state or they believe that they
+ could use some attention. Cases for showing them would be if help
+ were referring to them or they accessors were being highlighted.
+ This property can have two values: "normal" in almost all cases and
+ "notice" when they should have a higher priority to be shown.
+ </dox:d>
+ </property>
+
+ <property name="IconThemePath" type="as" access="read">
+ <dox:d>
+ A list of directories that should be used for finding icons using
+ the icon naming spec. Idealy there should only be one for the icon
+ theme, but additional ones are often added by applications for
+ app specific icons.
+ </dox:d>
+ </property>
+
<!-- Functions -->
<method name="GetLayout">
- <dox:d><![CDATA[
- Provides an XML representation of the menu hierarchy
-
- XML syntax:
-
- @verbatim
-<menu id="0"> # Root container
- <menu id="1"> # First level menu, for example "File"
- <menu id="2"/> ~ Second level menu, for example "Open"
- <menu id="3"/>
- ...
- </menu>
- <menu id="4"> # Another first level menu, say "Edit"
- ...
- </menu>
- ...
-</menu>
- @endverbatim
- ]]></dox:d>
+ <dox:d>
+ Provides the layout and propertiers that are attached to the entries
+ that are in the layout. It only gives the items that are children
+ of the item that is specified in @a parentId. It will return all of the
+ properties or specific ones depending of the value in @a propertyNames.
+
+ The format is recursive, where the second 'v' is in the same format
+ as the original 'a(ia{sv}av)'. Its content depends on the value
+ of @a recursionDepth.
+ </dox:d>
<arg type="i" name="parentId" direction="in">
<dox:d>The ID of the parent node for the layout. For
grabbing the layout from the root node use zero.</dox:d>
</arg>
+ <arg type="i" name="recursionDepth" direction="in">
+ <dox:d>
+ The amount of levels of recursion to use. This affects the
+ content of the second variant array.
+ - -1: deliver all the items under the @a parentId.
+ - 0: no recursion, the array will be empty.
+ - n: array will contains items up to 'n' level depth.
+ </dox:d>
+ </arg>
+ <arg type="as" name="propertyNames" direction="in" >
+ <dox:d>
+ The list of item properties we are
+ interested in. If there are no entries in the list all of
+ the properties will be sent.
+ </dox:d>
+ </arg>
<arg type="u" name="revision" direction="out">
<dox:d>The revision number of the layout. For matching
with layoutUpdated signals.</dox:d>
</arg>
- <arg type="s" name="layout" direction="out">
- <dox:d>The layout as an XML string of IDs.</dox:d>
+ <arg type="(ia{sv}av)" name="layout" direction="out">
+ <dox:d>The layout, as a recursive structure.</dox:d>
</arg>
</method>
<method name="GetGroupProperties">
- <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QVariantList"/>
- <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
<dox:d>
Returns the list of items which are children of @a parentId.
</dox:d>
@@ -236,33 +281,21 @@ License version 3 and version 2.1 along with this program. If not, see
</arg>
</method>
- <method name="GetChildren">
- <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
- <arg type="i" name="id" direction="in" />
- <arg type="as" name="propertyNames" direction="in" />
- <arg type="a(ia{sv})" name="properties" direction="out" />
- </method>
-
<method name="GetProperty">
- <arg type="i" name="id" direction="in" />
- <arg type="s" name="name" direction="in" />
- <arg type="v" name="value" direction="out" />
- </method>
-
- <method name="GetProperties">
<dox:d>
- Returns multiple properties in one call. This is more efficient than
- GetProperty.
-
+ Get a signal property on a single item. This is not useful if you're
+ going to implement this interface, it should only be used if you're
+ debugging via a commandline tool.
</dox:d>
- <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
- <arg type="i" name="id" direction="in" >
- <dox:d>The item whose properties we want to retrieve.</dox:d>
+ <arg type="i" name="id" direction="in">
+ <dox:d>the id of the item which received the event</dox:d>
</arg>
- <arg type="as" name="propertyNames" direction="in" >
- <dox:d>List of string name of the properties we want. If the list contains no entries, all properties are sent.</dox:d>
+ <arg type="s" name="name" direction="in">
+ <dox:d>the name of the property to get</dox:d>
+ </arg>
+ <arg type="v" name="value" direction="out">
+ <dox:d>the value of the property</dox:d>
</arg>
- <arg type="a{sv}" name="properties" direction="out" />
</method>
<method name="Event">
@@ -274,6 +307,8 @@ License version 3 and version 2.1 along with this program. If not, see
@li "clicked"
@li "hovered"
+ @li "opened"
+ @li "closed"
Vendor specific events can be added by prefixing them with "x-<vendor>-"
]]></dox:d>
@@ -309,25 +344,16 @@ License version 3 and version 2.1 along with this program. If not, see
</method>
<!-- Signals -->
- <signal name="ItemPropertyUpdated">
- <dox:d>
- Triggered by the application to notify the applet that the property @a property
- from item @a id has changed to @a value.
- </dox:d>
- <arg type="i" name="id" direction="out" />
- <arg type="s" name="prop" direction="out" />
- <arg type="v" name="value" direction="out" />
- </signal>
-
- <signal name="ItemUpdated">
+ <signal name="ItemsPropertiesUpdated">
<dox:d>
- Triggered by the application to notify the applet that all properties of item
+ Triggered when there are lots of property updates across many items
+ so they all get grouped into a single dbus message. The format is
+ the ID of the item with a hashtable of names and values for those
+ properties.
</dox:d>
- <arg type="i" name="id" direction="out" >
- <dox:d>id which should be considered outdated</dox:d>
- </arg>
+ <arg type="a(ia{sv})" name="updatedProps" direction="out" />
+ <arg type="a(ias)" name="removedProps" direction="out" />
</signal>
-
<signal name="LayoutUpdated">
<dox:d>
Triggered by the application to notify display of a layout update, up to
diff --git a/libdbusmenu-glib/dbusmenu-glib.pc.in b/libdbusmenu-glib/dbusmenu-glib-0.4.pc.in
index dacd903..31a1eac 100644
--- a/libdbusmenu-glib/dbusmenu-glib.pc.in
+++ b/libdbusmenu-glib/dbusmenu-glib-0.4.pc.in
@@ -4,8 +4,8 @@ libdir=@libdir@
bindir=@bindir@
includedir=@includedir@
-Cflags: -I${includedir}/libdbusmenu-0.1
-Requires: dbus-glib-1
+Cflags: -I${includedir}/libdbusmenu-0.4
+Requires:
Libs: -L${libdir} -ldbusmenu-glib
Name: libdbusmenu-glib
diff --git a/libdbusmenu-glib/dbusmenu-glib.h b/libdbusmenu-glib/dbusmenu-glib.h
new file mode 100644
index 0000000..9c377ca
--- /dev/null
+++ b/libdbusmenu-glib/dbusmenu-glib.h
@@ -0,0 +1,37 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_GLIB_H__
+#define __DBUSMENU_GLIB_H__
+
+#include <libdbusmenu-glib/client.h>
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/menuitem-proxy.h>
+#include <libdbusmenu-glib/server.h>
+
+#endif /* __DBUSMENU_GLIB_H__ */
diff --git a/libdbusmenu-glib/defaults.c b/libdbusmenu-glib/defaults.c
new file mode 100644
index 0000000..a5caf0b
--- /dev/null
+++ b/libdbusmenu-glib/defaults.c
@@ -0,0 +1,292 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n-lib.h>
+
+#include "defaults.h"
+#include "menuitem.h"
+#include "client.h"
+
+struct _DbusmenuDefaultsPrivate {
+ GHashTable * types;
+};
+
+typedef struct _DefaultEntry DefaultEntry;
+struct _DefaultEntry {
+ GVariantType * type;
+ GVariant * value;
+};
+
+#define DBUSMENU_DEFAULTS_GET_PRIVATE(o) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsPrivate))
+
+static void dbusmenu_defaults_class_init (DbusmenuDefaultsClass *klass);
+static void dbusmenu_defaults_init (DbusmenuDefaults *self);
+static void dbusmenu_defaults_dispose (GObject *object);
+static void dbusmenu_defaults_finalize (GObject *object);
+
+static DefaultEntry * entry_create (const GVariantType * type, GVariant * variant);
+static void entry_destroy (gpointer entry);
+
+G_DEFINE_TYPE (DbusmenuDefaults, dbusmenu_defaults, G_TYPE_OBJECT);
+
+static void
+dbusmenu_defaults_class_init (DbusmenuDefaultsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (DbusmenuDefaultsPrivate));
+
+ object_class->dispose = dbusmenu_defaults_dispose;
+ object_class->finalize = dbusmenu_defaults_finalize;
+ return;
+}
+
+static void
+dbusmenu_defaults_init (DbusmenuDefaults *self)
+{
+ self->priv = DBUSMENU_DEFAULTS_GET_PRIVATE(self);
+
+ self->priv->types = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy);
+
+ /* Standard defaults */
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_VISIBLE, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE));
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_ENABLED, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE));
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_LABEL, G_VARIANT_TYPE_STRING, g_variant_new_string(_("Label Empty")));
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_ICON_NAME, G_VARIANT_TYPE_STRING, NULL);
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, G_VARIANT_TYPE_STRING, NULL);
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, G_VARIANT_TYPE_INT32, NULL);
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_SHORTCUT, G_VARIANT_TYPE("aas"), NULL);
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, G_VARIANT_TYPE_STRING, NULL);
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_DEFAULT, DBUSMENU_MENUITEM_PROP_DISPOSITION, G_VARIANT_TYPE_STRING, g_variant_new_string(DBUSMENU_MENUITEM_DISPOSITION_NORMAL));
+
+ /* Separator defaults */
+ dbusmenu_defaults_default_set(self, DBUSMENU_CLIENT_TYPES_SEPARATOR, DBUSMENU_MENUITEM_PROP_VISIBLE, G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(TRUE));
+
+ return;
+}
+
+static void
+dbusmenu_defaults_dispose (GObject *object)
+{
+ DbusmenuDefaults * self = DBUSMENU_DEFAULTS(object);
+
+ if (self->priv->types != NULL) {
+ g_hash_table_destroy(self->priv->types);
+ self->priv->types = NULL;
+ }
+
+ G_OBJECT_CLASS (dbusmenu_defaults_parent_class)->dispose (object);
+ return;
+}
+
+static void
+dbusmenu_defaults_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (dbusmenu_defaults_parent_class)->finalize (object);
+ return;
+}
+
+/* Create a new entry based on the info provided */
+static DefaultEntry *
+entry_create (const GVariantType * type, GVariant * variant)
+{
+ DefaultEntry * defentry = g_new0(DefaultEntry, 1);
+
+ if (type != NULL) {
+ defentry->type = g_variant_type_copy(type);
+ }
+
+ if (variant != NULL) {
+ defentry->value = variant;
+ g_variant_ref_sink(variant);
+ }
+
+ return defentry;
+}
+
+/* Destroy an entry */
+static void
+entry_destroy (gpointer entry)
+{
+ DefaultEntry * defentry = (DefaultEntry *)entry;
+
+ if (defentry->type != NULL) {
+ g_variant_type_free(defentry->type);
+ defentry->type = NULL;
+ }
+
+ if (defentry->value != NULL) {
+ g_variant_unref(defentry->value);
+ defentry->value = NULL;
+ }
+
+ g_free(defentry);
+ return;
+}
+
+static DbusmenuDefaults * default_defaults = NULL;
+
+/*
+ * dbusmenu_defaults_ref_default:
+ *
+ * Get a reference to the default instance. If it doesn't exist this
+ * function will create it.
+ *
+ * Return value: (transfer full): A reference to the defaults
+ */
+DbusmenuDefaults *
+dbusmenu_defaults_ref_default (void)
+{
+ if (default_defaults == NULL) {
+ default_defaults = DBUSMENU_DEFAULTS(g_object_new(DBUSMENU_TYPE_DEFAULTS, NULL));
+ g_object_add_weak_pointer(G_OBJECT(default_defaults), (gpointer *)&default_defaults);
+ } else {
+ g_object_ref(default_defaults);
+ }
+
+ return default_defaults;
+}
+
+/*
+ * dbusmenu_defaults_default_set:
+ * @defaults: The #DbusmenuDefaults object to add to
+ * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT
+ * @property: Property name for the default
+ * @prop_type: (allow-none): Type of the property for runtime checking. To disable checking set to #NULL.
+ * @value: (allow-none): Default value for @property. #NULL if by default it is unset, but you want type checking from @prop_type.
+ *
+ * Sets up an entry in the defaults database for a given @property
+ * and menuitem type @type. @prop_type and @value can both be #NULL
+ * but both of them can not.
+ */
+void
+dbusmenu_defaults_default_set (DbusmenuDefaults * defaults, const gchar * type, const gchar * property, const GVariantType * prop_type, GVariant * value)
+{
+ g_return_if_fail(DBUSMENU_IS_DEFAULTS(defaults));
+ g_return_if_fail(property != NULL);
+ g_return_if_fail(prop_type != NULL || value != NULL);
+
+ if (type == NULL) {
+ type = DBUSMENU_CLIENT_TYPES_DEFAULT;
+ }
+
+ GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type);
+
+ /* We've never had a default for this type, so we need
+ to create a table for it. */
+ if (prop_table == NULL) {
+ prop_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, entry_destroy);
+
+ g_hash_table_insert(prop_table, g_strdup(property), entry_create(prop_type, value));
+ g_hash_table_insert(defaults->priv->types, g_strdup(type), prop_table);
+ } else {
+ g_hash_table_replace(prop_table, g_strdup(property), entry_create(prop_type, value));
+ }
+
+ return;
+}
+
+/*
+ * dbusmenu_defaults_default_get:
+ * @defaults: The default database to use
+ * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT
+ * @property: Property name to lookup
+ *
+ * Gets an entry in the database for a give @property and @type.
+ *
+ * Return value: (transfer none): Returns a variant that does not
+ * have it's ref count increased. If you want to keep it, you should
+ * do that.
+ */
+GVariant *
+dbusmenu_defaults_default_get (DbusmenuDefaults * defaults, const gchar * type, const gchar * property)
+{
+ g_return_val_if_fail(DBUSMENU_IS_DEFAULTS(defaults), NULL);
+ g_return_val_if_fail(property != NULL, NULL);
+
+ if (type == NULL) {
+ type = DBUSMENU_CLIENT_TYPES_DEFAULT;
+ }
+
+ GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type);
+
+ if (prop_table == NULL) {
+ return NULL;
+ }
+
+ DefaultEntry * entry = (DefaultEntry *)g_hash_table_lookup(prop_table, property);
+
+ if (entry == NULL) {
+ return NULL;
+ }
+
+ return entry->value;
+}
+
+/*
+ * dbusmenu_defaults_default_get_type:
+ * @defaults: The default database to use
+ * @type: (allow-none): The #DbusmenuMenuitem type for this default if #NULL will default to #DBUSMENU_CLIENT_TYPE_DEFAULT
+ * @property: Property name to lookup
+ *
+ * Gets the type for an entry in the database for a give @property and @type.
+ *
+ * Return value: (transfer none): Returns a type for the given
+ * @property value.
+ */
+GVariantType *
+dbusmenu_defaults_default_get_type (DbusmenuDefaults * defaults, const gchar * type, const gchar * property)
+{
+ g_return_val_if_fail(DBUSMENU_IS_DEFAULTS(defaults), NULL);
+ g_return_val_if_fail(property != NULL, NULL);
+
+ if (type == NULL) {
+ type = DBUSMENU_CLIENT_TYPES_DEFAULT;
+ }
+
+ GHashTable * prop_table = (GHashTable *)g_hash_table_lookup(defaults->priv->types, type);
+
+ if (prop_table == NULL) {
+ return NULL;
+ }
+
+ DefaultEntry * entry = (DefaultEntry *)g_hash_table_lookup(prop_table, property);
+
+ if (entry == NULL) {
+ return NULL;
+ }
+
+ return entry->type;
+}
+
diff --git a/libdbusmenu-glib/defaults.h b/libdbusmenu-glib/defaults.h
new file mode 100644
index 0000000..a1a8158
--- /dev/null
+++ b/libdbusmenu-glib/defaults.h
@@ -0,0 +1,86 @@
+/*
+A library to communicate a menu object set accross DBus and
+track updates and maintain consistency.
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_DEFAULTS_H__
+#define __DBUSMENU_DEFAULTS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define DBUSMENU_TYPE_DEFAULTS (dbusmenu_defaults_get_type ())
+#define DBUSMENU_DEFAULTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaults))
+#define DBUSMENU_DEFAULTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsClass))
+#define DBUSMENU_IS_DEFAULTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_DEFAULTS))
+#define DBUSMENU_IS_DEFAULTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_DEFAULTS))
+#define DBUSMENU_DEFAULTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_DEFAULTS, DbusmenuDefaultsClass))
+
+typedef struct _DbusmenuDefaults DbusmenuDefaults;
+typedef struct _DbusmenuDefaultsClass DbusmenuDefaultsClass;
+typedef struct _DbusmenuDefaultsPrivate DbusmenuDefaultsPrivate;
+
+/*
+ * DbusmenuDefaultsClass:
+ *
+ * All of the signals and functions for #DbusmenuDefaults
+ */
+struct _DbusmenuDefaultsClass {
+ GObjectClass parent_class;
+};
+
+/*
+ * DbusmenuDefaults:
+ *
+ * A singleton to hold all of the defaults for the menuitems
+ * so they can use those easily.
+ */
+struct _DbusmenuDefaults {
+ GObject parent;
+
+ /*< Private >*/
+ DbusmenuDefaultsPrivate * priv;
+};
+
+GType dbusmenu_defaults_get_type (void);
+DbusmenuDefaults * dbusmenu_defaults_ref_default (void);
+void dbusmenu_defaults_default_set (DbusmenuDefaults * defaults,
+ const gchar * type,
+ const gchar * property,
+ const GVariantType * prop_type,
+ GVariant * value);
+GVariant * dbusmenu_defaults_default_get (DbusmenuDefaults * defaults,
+ const gchar * type,
+ const gchar * property);
+GVariantType * dbusmenu_defaults_default_get_type (DbusmenuDefaults * defaults,
+ const gchar * type,
+ const gchar * property);
+
+G_END_DECLS
+
+#endif
diff --git a/libdbusmenu-glib/enum-types.c.in b/libdbusmenu-glib/enum-types.c.in
new file mode 100644
index 0000000..40f1759
--- /dev/null
+++ b/libdbusmenu-glib/enum-types.c.in
@@ -0,0 +1,116 @@
+/*** BEGIN file-header ***/
+/*
+Enums from the dbusmenu headers
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+ Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+ the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#include "enum-types.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@basename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+/**
+ @enum_name@_get_type:
+
+ Builds a GLib type for the #@EnumName@ enumeration.
+
+ Return value: A unique #GType for the #@EnumName@ enum.
+*/
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL}
+ };
+
+ etype = g_@type@_register_static (g_intern_static_string("@EnumName@"), values);
+ }
+
+ return etype;
+}
+
+/**
+ @enum_name@_get_nick:
+ @value: The value of @EnumName@ to get the nick of
+
+ Looks up in the enum table for the nick of @value.
+
+ Return value: The nick for the given value or #NULL on error
+*/
+const gchar *
+@enum_name@_get_nick (@EnumName@ value)
+{
+ GEnumClass * class = G_ENUM_CLASS(g_type_class_ref(@enum_name@_get_type()));
+ g_return_val_if_fail(class != NULL, NULL);
+
+ const gchar * ret = NULL;
+ GEnumValue * val = g_enum_get_value(class, value);
+ if (val != NULL) {
+ ret = val->value_nick;
+ }
+
+ g_type_class_unref(class);
+ return ret;
+}
+
+/**
+ @enum_name@_get_value_from_nick:
+ @nick: The enum nick to lookup
+
+ Looks up in the enum table for the value of @nick.
+
+ Return value: The value for the given @nick
+*/
+@EnumName@
+@enum_name@_get_value_from_nick (const gchar * nick)
+{
+ GEnumClass * class = G_ENUM_CLASS(g_type_class_ref(@enum_name@_get_type()));
+ g_return_val_if_fail(class != NULL, 0);
+
+ @EnumName@ ret = 0;
+ GEnumValue * val = g_enum_get_value_by_nick(class, nick);
+ if (val != NULL) {
+ ret = val->value;
+ }
+
+ g_type_class_unref(class);
+ return ret;
+}
+
+
+/*** END value-tail ***/
diff --git a/libdbusmenu-glib/enum-types.h.in b/libdbusmenu-glib/enum-types.h.in
new file mode 100644
index 0000000..5758438
--- /dev/null
+++ b/libdbusmenu-glib/enum-types.h.in
@@ -0,0 +1,65 @@
+/*** BEGIN file-header ***/
+/*
+Enums from the dbusmenu headers
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+ Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+ the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_ENUM_TYPES_H__
+#define __DBUSMENU_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+#endif /* __DBUSMENU_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN file-production ***/
+/* Enumerations from file: "@filename@" */
+#include "@basename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+const gchar * @enum_name@_get_nick (@EnumName@ value) G_GNUC_CONST;
+@EnumName@ @enum_name@_get_value_from_nick (const gchar * nick) G_GNUC_CONST;
+
+/**
+ DBUSMENU_TYPE_@ENUMSHORT@:
+
+ Gets the #GType value for the type associated with the
+ #@EnumName@ enumerated type.
+*/
+#define DBUSMENU_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+
+/*** END value-header ***/
diff --git a/libdbusmenu-glib/menuitem-marshal.list b/libdbusmenu-glib/menuitem-marshal.list
index 654c91b..4382ed7 100644
--- a/libdbusmenu-glib/menuitem-marshal.list
+++ b/libdbusmenu-glib/menuitem-marshal.list
@@ -1,6 +1,7 @@
-VOID: STRING, POINTER
+VOID: STRING, VARIANT
VOID: OBJECT, UINT, UINT
VOID: OBJECT, UINT
VOID: OBJECT
VOID: VOID
VOID: UINT
+BOOLEAN: STRING, VARIANT, UINT
diff --git a/libdbusmenu-glib/menuitem-private.h b/libdbusmenu-glib/menuitem-private.h
index 3a0c026..336769d 100644
--- a/libdbusmenu-glib/menuitem-private.h
+++ b/libdbusmenu-glib/menuitem-private.h
@@ -33,9 +33,12 @@ License version 3 and version 2.1 along with this program. If not, see
G_BEGIN_DECLS
-void dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array);
+GVariant * dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse);
gboolean dbusmenu_menuitem_realized (DbusmenuMenuitem * mi);
void dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi);
+GVariant * dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties);
+gboolean dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property);
+gboolean dbusmenu_menuitem_exposed (DbusmenuMenuitem * mi);
G_END_DECLS
diff --git a/libdbusmenu-glib/menuitem-proxy.c b/libdbusmenu-glib/menuitem-proxy.c
index 2dd5ada..ae6a334 100644
--- a/libdbusmenu-glib/menuitem-proxy.c
+++ b/libdbusmenu-glib/menuitem-proxy.c
@@ -32,7 +32,6 @@ License version 3 and version 2.1 along with this program. If not, see
#include "menuitem-proxy.h"
-typedef struct _DbusmenuMenuitemProxyPrivate DbusmenuMenuitemProxyPrivate;
struct _DbusmenuMenuitemProxyPrivate {
DbusmenuMenuitem * mi;
gulong sig_property_changed;
@@ -49,8 +48,7 @@ enum {
#define PROP_MENU_ITEM_S "menu-item"
-#define DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyPrivate))
+#define DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(o) (DBUSMENU_MENUITEM_PROXY(o)->priv)
static void dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass);
static void dbusmenu_menuitem_proxy_init (DbusmenuMenuitemProxy *self);
@@ -58,7 +56,7 @@ static void dbusmenu_menuitem_proxy_dispose (GObject *object);
static void dbusmenu_menuitem_proxy_finalize (GObject *object);
static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
-static void handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
+static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp);
static void add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi);
static void remove_menuitem (DbusmenuMenuitemProxy * pmi);
@@ -92,6 +90,8 @@ dbusmenu_menuitem_proxy_class_init (DbusmenuMenuitemProxyClass *klass)
static void
dbusmenu_menuitem_proxy_init (DbusmenuMenuitemProxy *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyPrivate);
+
DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(self);
priv->mi = NULL;
@@ -162,21 +162,21 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
/* Takes the event and passes it along to the item that we're
playing proxy for. */
static void
-handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM_PROXY(mi));
DbusmenuMenuitemProxyPrivate * priv = DBUSMENU_MENUITEM_PROXY_GET_PRIVATE(mi);
g_return_if_fail(priv->mi != NULL);
- return dbusmenu_menuitem_handle_event(priv->mi, name, value, timestamp);
+ return dbusmenu_menuitem_handle_event(priv->mi, name, variant, timestamp);
}
/* Watches a property change and makes sure to put that value
into our property list. */
static void
-proxy_item_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, gpointer user_data)
+proxy_item_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, gpointer user_data)
{
DbusmenuMenuitemProxy * pmi = DBUSMENU_MENUITEM_PROXY(user_data);
- dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), property, value);
+ dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(pmi), property, variant);
return;
}
@@ -273,7 +273,7 @@ add_menuitem (DbusmenuMenuitemProxy * pmi, DbusmenuMenuitem * mi)
GList * prop;
for (prop = props; prop != NULL; prop = g_list_next(prop)) {
gchar * prop_name = (gchar *)prop->data;
- dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(pmi), prop_name, dbusmenu_menuitem_property_get_value(priv->mi, prop_name));
+ dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(pmi), prop_name, dbusmenu_menuitem_property_get_variant(priv->mi, prop_name));
}
g_list_free(props);
@@ -325,14 +325,14 @@ remove_menuitem (DbusmenuMenuitemProxy * pmi)
}
/**
- dbusmenu_menuitem_proxy_new:
- @mi: The #DbusmenuMenuitem to proxy
-
- Builds a new #DbusmenuMenuitemProxy object that proxies
- all of the values for @mi.
-
- Return value: A new #DbusmenuMenuitemProxy object.
-*/
+ * dbusmenu_menuitem_proxy_new:
+ * @mi: The #DbusmenuMenuitem to proxy
+ *
+ * Builds a new #DbusmenuMenuitemProxy object that proxies
+ * all of the values for @mi.
+ *
+ * Return value: A new #DbusmenuMenuitemProxy object.
+ */
DbusmenuMenuitemProxy *
dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi)
{
@@ -344,15 +344,15 @@ dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi)
}
/**
- dbusmenu_menuitem_proxy_get_wrapped:
- @pmi: #DbusmenuMenuitemProxy to look into
-
- Accesses the private variable of which #DbusmenuMenuitem
- we are doing the proxying for.
-
- Return value: A #DbusmenuMenuitem object or a #NULL if we
- don't have one or there is an error.
-*/
+ * dbusmenu_menuitem_proxy_get_wrapped:
+ * @pmi: #DbusmenuMenuitemProxy to look into
+ *
+ * Accesses the private variable of which #DbusmenuMenuitem
+ * we are doing the proxying for.
+ *
+ * Return value: (transfer none): A #DbusmenuMenuitem object or a #NULL if we
+ * don't have one or there is an error.
+ */
DbusmenuMenuitem *
dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi)
{
diff --git a/libdbusmenu-glib/menuitem-proxy.h b/libdbusmenu-glib/menuitem-proxy.h
index 56c4941..ee988d8 100644
--- a/libdbusmenu-glib/menuitem-proxy.h
+++ b/libdbusmenu-glib/menuitem-proxy.h
@@ -42,33 +42,57 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_MENUITEM_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM_PROXY))
#define DBUSMENU_MENUITEM_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM_PROXY, DbusmenuMenuitemProxyClass))
-typedef struct _DbusmenuMenuitemProxy DbusmenuMenuitemProxy;
-typedef struct _DbusmenuMenuitemProxyClass DbusmenuMenuitemProxyClass;
+typedef struct _DbusmenuMenuitemProxy DbusmenuMenuitemProxy;
+typedef struct _DbusmenuMenuitemProxyClass DbusmenuMenuitemProxyClass;
+typedef struct _DbusmenuMenuitemProxyPrivate DbusmenuMenuitemProxyPrivate;
/**
DbusmenuMenuitemProxyClass:
@parent_class: The Class of #DbusmeneMenuitem
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
Functions and signal slots for #DbusmenuMenuitemProxy.
*/
struct _DbusmenuMenuitemProxyClass {
DbusmenuMenuitemClass parent_class;
+
+ /*< Private >*/
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
};
/**
- DbusmeneMenuitemProxy:
- @parent: The instance of #DbusmenuMenuitem
+ DbusmenuMenuitemProxy:
Public instance data for a #DbusmenuMenuitemProxy.
*/
struct _DbusmenuMenuitemProxy {
DbusmenuMenuitem parent;
+
+ /*< Private >*/
+ DbusmenuMenuitemProxyPrivate * priv;
};
GType dbusmenu_menuitem_proxy_get_type (void);
DbusmenuMenuitemProxy * dbusmenu_menuitem_proxy_new (DbusmenuMenuitem * mi);
DbusmenuMenuitem * dbusmenu_menuitem_proxy_get_wrapped (DbusmenuMenuitemProxy * pmi);
+/**
+ * SECTION:menuitem-proxy
+ * @short_description: A menuitem that proxies from another menuitem
+ * @stability: Unstable
+ * @include: libdbusmenu-glib/menuitem-proxy.h
+ *
+ * This small object allows for proxying all the properties from a remote
+ * menuitem to a new object that can be moved around appropriately within
+ * the new menu structure.
+ */
+
G_END_DECLS
#endif
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c
index ea69776..4e037ee 100644
--- a/libdbusmenu-glib/menuitem.c
+++ b/libdbusmenu-glib/menuitem.c
@@ -33,6 +33,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include "menuitem.h"
#include "menuitem-marshal.h"
#include "menuitem-private.h"
+#include "defaults.h"
#ifdef MASSIVEDEBUGGING
#define LABEL(x) dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(x), DBUSMENU_MENUITEM_PROP_LABEL)
@@ -52,7 +53,6 @@ License version 3 and version 2.1 along with this program. If not, see
out of data that we have. They can still be gotten using
accessor functions, but are protected appropriately.
*/
-typedef struct _DbusmenuMenuitemPrivate DbusmenuMenuitemPrivate;
struct _DbusmenuMenuitemPrivate
{
gint id;
@@ -60,6 +60,9 @@ struct _DbusmenuMenuitemPrivate
GHashTable * properties;
gboolean root;
gboolean realized;
+ DbusmenuDefaults * defaults;
+ gboolean exposed;
+ DbusmenuMenuitem * parent;
};
/* Signals */
@@ -71,6 +74,8 @@ enum {
CHILD_MOVED,
REALIZED,
SHOW_TO_USER,
+ ABOUT_TO_SHOW,
+ EVENT,
LAST_SIGNAL
};
@@ -84,8 +89,7 @@ enum {
#define PROP_ID_S "id"
-#define DBUSMENU_MENUITEM_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemPrivate))
+#define DBUSMENU_MENUITEM_GET_PRIVATE(o) (DBUSMENU_MENUITEM(o)->priv)
/* Prototypes */
static void dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass);
@@ -96,7 +100,8 @@ static void set_property (GObject * obj, guint id, const GValue * value, GParamS
static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
static void g_value_transform_STRING_BOOLEAN (const GValue * in, GValue * out);
static void g_value_transform_STRING_INT (const GValue * in, GValue * out);
-static void handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
+static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp);
+static void send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data);
/* GObject stuff */
G_DEFINE_TYPE (DbusmenuMenuitem, dbusmenu_menuitem, G_TYPE_OBJECT);
@@ -114,6 +119,7 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
object_class->get_property = get_property;
klass->handle_event = handle_event;
+ klass->send_about_to_show = send_about_to_show;
/**
DbusmenuMenuitem::property-changed:
@@ -129,8 +135,8 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(DbusmenuMenuitemClass, property_changed),
NULL, NULL,
- _dbusmenu_menuitem_marshal_VOID__STRING_POINTER,
- G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+ _dbusmenu_menuitem_marshal_VOID__STRING_VARIANT,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
/**
DbusmenuMenuitem::item-activated:
@arg0: The #DbusmenuMenuitem object.
@@ -229,6 +235,38 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, G_TYPE_UINT, G_TYPE_NONE);
+ /**
+ DbusmenuMenuitem::about-to-show:
+ @arg0: The #DbusmenuMenuitem object.
+
+ Emitted when the submenu for this item
+ is about to be shown
+ */
+ signals[ABOUT_TO_SHOW] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, about_to_show),
+ NULL, NULL,
+ _dbusmenu_menuitem_marshal_VOID__VOID,
+ G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
+ /**
+ DbusmenuMenuitem::event:
+ @arg0: The #DbusmenuMenuitem object.
+ @arg1: Name of the event
+ @arg2: Information passed along with the event
+ @arg3: X11 timestamp of when the event happened
+
+ Emitted when an event is passed through. The event is signalled
+ after handle_event is called.
+ */
+ signals[EVENT] = g_signal_new(DBUSMENU_MENUITEM_SIGNAL_EVENT,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET(DbusmenuMenuitemClass, event),
+ g_signal_accumulator_true_handled, NULL,
+ _dbusmenu_menuitem_marshal_BOOLEAN__STRING_VARIANT_UINT,
+ G_TYPE_BOOLEAN, 3, G_TYPE_STRING, G_TYPE_VARIANT, G_TYPE_UINT);
+
g_object_class_install_property (object_class, PROP_ID,
g_param_spec_int(PROP_ID_S, "ID for the menu item",
"This is a unique indentifier for the menu item.",
@@ -271,15 +309,12 @@ g_value_transform_STRING_INT (const GValue * in, GValue * out)
static gint menuitem_next_id = 1;
-/* A small little function to both clear the insides of a
- value as well as the memory it itself uses. */
+/* Make the unref function match the prototype need for the
+ hashtable destructor */
static void
-_g_value_free (gpointer data)
+_g_variant_unref (gpointer data)
{
- if (data == NULL) return;
- GValue * value = (GValue*)data;
- g_value_unset(value);
- g_free(data);
+ g_variant_unref((GVariant *)data);
return;
}
@@ -288,15 +323,20 @@ _g_value_free (gpointer data)
static void
dbusmenu_menuitem_init (DbusmenuMenuitem *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemPrivate);
+
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(self);
priv->id = -1;
priv->children = NULL;
- priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _g_value_free);
+ priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _g_variant_unref);
priv->root = FALSE;
priv->realized = FALSE;
+
+ priv->defaults = dbusmenu_defaults_ref_default();
+ priv->exposed = FALSE;
return;
}
@@ -313,6 +353,16 @@ dbusmenu_menuitem_dispose (GObject *object)
g_list_free(priv->children);
priv->children = NULL;
+ if (priv->defaults != NULL) {
+ g_object_unref(priv->defaults);
+ priv->defaults = NULL;
+ }
+
+ if (priv->parent) {
+ g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent);
+ priv->parent = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object);
return;
}
@@ -378,24 +428,60 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
/* Handles the activate event if it is sent. */
static void
-handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp)
{
- if (g_strcmp0(name, "clicked") == 0) {
+ if (g_strcmp0(name, DBUSMENU_MENUITEM_EVENT_ACTIVATED) == 0) {
g_signal_emit(G_OBJECT(mi), signals[ITEM_ACTIVATED], 0, timestamp, TRUE);
}
return;
}
-/* Public interface */
+/* Handles our about to show signal on items that submenus
+ exist. This is sending just activate now, but we should
+ probably consider a special signal in the future if GTK
+ gets more sophisticated about this. */
+static void
+send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data)
+{
+ g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
-/**
- dbusmenu_menuitem_new:
+ if (dbusmenu_menuitem_get_children(mi) == NULL && g_strcmp0(DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU, dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) != 0) {
+ g_warning("About to Show called on an item wihtout submenus. We're ignoring it.");
+ } else {
+ gboolean dummy;
+ g_signal_emit(G_OBJECT(mi), signals[ABOUT_TO_SHOW], 0, &dummy);
+ }
- Create a new #DbusmenuMenuitem with all default values.
+ if (cb != NULL) {
+ cb(mi, cb_data);
+ }
- Return value: A newly allocated #DbusmenuMenuitem.
-*/
+ return;
+}
+
+/* A helper function to get the type of the menuitem, this might
+ be a candidate for optimization in the future. */
+static const gchar *
+menuitem_get_type (DbusmenuMenuitem * mi)
+{
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ GVariant * currentval = (GVariant *)g_hash_table_lookup(priv->properties, DBUSMENU_MENUITEM_PROP_TYPE);
+ if (currentval != NULL) {
+ return g_variant_get_string(currentval, NULL);
+ }
+ return NULL;
+}
+
+/* Public interface */
+
+/**
+ * dbusmenu_menuitem_new:
+ *
+ * Create a new #DbusmenuMenuitem with all default values.
+ *
+ * Return value: A newly allocated #DbusmenuMenuitem.
+ */
DbusmenuMenuitem *
dbusmenu_menuitem_new (void)
{
@@ -403,13 +489,13 @@ dbusmenu_menuitem_new (void)
}
/**
- dbusmenu_menuitem_new_with_id:
- @id: ID to use for this menuitem
-
- This creates a blank #DbusmenuMenuitem with a specific ID.
-
- Return value: A newly allocated #DbusmenuMenuitem.
-*/
+ * dbusmenu_menuitem_new_with_id:
+ * @id: ID to use for this menuitem
+ *
+ * This creates a blank #DbusmenuMenuitem with a specific ID.
+ *
+ * Return value: A newly allocated #DbusmenuMenuitem.
+ */
DbusmenuMenuitem *
dbusmenu_menuitem_new_with_id (gint id)
{
@@ -419,13 +505,13 @@ dbusmenu_menuitem_new_with_id (gint id)
}
/**
- dbusmenu_menuitem_get_id:
- @mi: The #DbusmenuMenuitem to query.
-
- Gets the unique ID for @mi.
-
- Return value: The ID of the @mi.
-*/
+ * dbusmenu_menuitem_get_id:
+ * @mi: The #DbusmenuMenuitem to query.
+ *
+ * Gets the unique ID for @mi.
+ *
+ * Return value: The ID of the @mi.
+ */
gint
dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi)
{
@@ -442,17 +528,17 @@ dbusmenu_menuitem_get_id (DbusmenuMenuitem * mi)
}
/**
- dbusmenu_menuitem_realized:
- @mi: #DbusmenuMenuitem to check on
-
- This function returns whether the menuitem has been realized or
- not. This is significant mostly in client implementations that
- can use this additional state to see if the second layers of
- the implementation have been built yet.
-
- Return value: Returns whether or not the menu item has been realized
- yet or not.
-*/
+ * dbusmenu_menuitem_realized:
+ * @mi: #DbusmenuMenuitem to check on
+ *
+ * This function returns whether the menuitem has been realized or
+ * not. This is significant mostly in client implementations that
+ * can use this additional state to see if the second layers of
+ * the implementation have been built yet.
+ *
+ * Return value: Returns whether or not the menu item has been realized
+ * yet or not.
+ */
gboolean
dbusmenu_menuitem_realized (DbusmenuMenuitem * mi)
{
@@ -462,12 +548,12 @@ dbusmenu_menuitem_realized (DbusmenuMenuitem * mi)
}
/**
- dbusmenu_menuitem_set_realized:
- @mi: #DbusmenuMenuitem to realize
-
- Sets the internal variable tracking whether it's been realized and
- signals the DbusmenuMenuitem::realized event.
-*/
+ * dbusmenu_menuitem_set_realized:
+ * @mi: #DbusmenuMenuitem to realize
+ *
+ * Sets the internal variable tracking whether it's been realized and
+ * signals the DbusmenuMenuitem::realized event.
+ */
void
dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi)
{
@@ -482,15 +568,15 @@ dbusmenu_menuitem_set_realized (DbusmenuMenuitem * mi)
}
/**
- dbusmenu_menuitem_get_children:
- @mi: The #DbusmenuMenuitem to query.
-
- Returns simply the list of children that this menu item
- has. The list is valid until another child related function
- is called, where it might be changed.
-
- Return value: A #GList of pointers to #DbusmenuMenuitem objects.
-*/
+ * dbusmenu_menuitem_get_children:
+ * @mi: The #DbusmenuMenuitem to query.
+ *
+ * Returns simply the list of children that this menu item
+ * has. The list is valid until another child related function
+ * is called, where it might be changed.
+ *
+ * Return value: (transfer none) (element-type Dbusmenu.Menuitem): A #GList of pointers to #DbusmenuMenuitem objects.
+ */
GList *
dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi)
{
@@ -503,28 +589,29 @@ dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi)
/* For all the taken children we need to signal
that they were removed */
static void
-take_children_signal (gpointer data, gpointer user_data)
+take_children_helper (gpointer data, gpointer user_data)
{
#ifdef MASSIVEDEBUGGING
g_debug("Menuitem %d (%s) signalling child removed %d (%s)", ID(user_data), LABEL(user_data), ID(data), LABEL(data));
#endif
+ dbusmenu_menuitem_unparent(DBUSMENU_MENUITEM(data));
g_signal_emit(G_OBJECT(user_data), signals[CHILD_REMOVED], 0, DBUSMENU_MENUITEM(data), TRUE);
- g_object_unref(G_OBJECT(data));
return;
}
/**
- dbusmenu_menuitem_take_children:
- @mi: The #DbusmenMenuitem to take the children from.
-
- While the name sounds devious that's exactly what this function
- does. It takes the list of children from the @mi and clears the
- internal list. The calling function is now in charge of the ref's
- on the children it has taken. A lot of responsibility involved
- in taking children.
-
- Return value: A #GList of pointers to #DbusmenuMenuitem objects.
-*/
+ * dbusmenu_menuitem_take_children:
+ * @mi: The #DbusmenMenuitem to take the children from.
+ *
+ * While the name sounds devious that's exactly what this function
+ * does. It takes the list of children from the @mi and clears the
+ * internal list. The calling function is now in charge of the ref's
+ * on the children it has taken. A lot of responsibility involved
+ * in taking children.
+ *
+ * Return value: (transfer full) (element-type Dbusmenu.Menuitem):
+ * A #GList of pointers to #DbusmenuMenuitem objects.
+ */
GList *
dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi)
{
@@ -533,7 +620,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);
+ g_list_foreach(children, take_children_helper, mi);
dbusmenu_menuitem_property_remove(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY);
@@ -541,16 +628,16 @@ dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi)
}
/**
- dbusmenu_menuitem_get_position:
- @mi: The #DbusmenuMenuitem to find the position of
- @parent: The #DbusmenuMenuitem who's children contain @mi
-
- This function returns the position of the menu item @mi
- in the children of @parent. It will return zero if the
- menu item can't be found.
-
- Return value: The position of @mi in the children of @parent.
-*/
+ * dbusmenu_menuitem_get_position:
+ * @mi: The #DbusmenuMenuitem to find the position of
+ * @parent: The #DbusmenuMenuitem who's children contain @mi
+ *
+ * This function returns the position of the menu item @mi
+ * in the children of @parent. It will return zero if the
+ * menu item can't be found.
+ *
+ * Return value: The position of @mi in the children of @parent.
+ */
guint
dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent)
{
@@ -580,15 +667,15 @@ dbusmenu_menuitem_get_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent
}
/**
- dbusmenu_menuitem_get_position_realized:
- @mi: The #DbusmenuMenuitem to find the position of
- @parent: The #DbusmenuMenuitem who's children contain @mi
-
- This function is very similar to #dbusmenu_menuitem_get_position
- except that it only counts in the children that have been realized.
-
- Return value: The position of @mi in the realized children of @parent.
-*/
+ * dbusmenu_menuitem_get_position_realized:
+ * @mi: The #DbusmenuMenuitem to find the position of
+ * @parent: The #DbusmenuMenuitem who's children contain @mi
+ *
+ * This function is very similar to #dbusmenu_menuitem_get_position
+ * except that it only counts in the children that have been realized.
+ *
+ * Return value: The position of @mi in the realized children of @parent.
+ */
guint
dbusmenu_menuitem_get_position_realized (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent)
{
@@ -624,15 +711,15 @@ dbusmenu_menuitem_get_position_realized (DbusmenuMenuitem * mi, DbusmenuMenuitem
}
/**
- dbusmenu_menuitem_child_append:
- @mi: The #DbusmenuMenuitem which will become a new parent
- @child: The #DbusmenMenuitem that will be a child
-
- This function adds @child to the list of children on @mi at
- the end of that list.
-
- Return value: Whether the child has been added successfully.
-*/
+ * dbusmenu_menuitem_child_append:
+ * @mi: The #DbusmenuMenuitem which will become a new parent
+ * @child: The #DbusmenMenuitem that will be a child
+ *
+ * This function adds @child to the list of children on @mi at
+ * the end of that list.
+ *
+ * Return value: Whether the child has been added successfully.
+ */
gboolean
dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
{
@@ -642,6 +729,10 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE);
+ if (!dbusmenu_menuitem_set_parent(child, mi)) {
+ return FALSE;
+ }
+
if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) {
dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU);
}
@@ -656,15 +747,15 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
}
/**
- 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.
-*/
+ * 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)
{
@@ -674,6 +765,10 @@ dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE);
+ if (!dbusmenu_menuitem_set_parent(child, mi)) {
+ return FALSE;
+ }
+
if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) {
dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU);
}
@@ -688,24 +783,30 @@ dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child
}
/**
- dbusmenu_menuitem_child_delete:
- @mi: The #DbusmenuMenuitem which has @child as a child
- @child: The child #DbusmenuMenuitem that you want to no longer
- be a child of @mi.
-
- This function removes @child from the children list of @mi. It does
- not call #g_object_unref on @child.
-
- Return value: If we were able to delete @child.
-*/
+ * dbusmenu_menuitem_child_delete:
+ * @mi: The #DbusmenuMenuitem which has @child as a child
+ * @child: The child #DbusmenuMenuitem that you want to no longer
+ * be a child of @mi.
+ *
+ * This function removes @child from the children list of @mi. It does
+ * not call #g_object_unref on @child.
+ *
+ * Return value: If we were able to delete @child.
+ */
gboolean
dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(child), FALSE);
+ if (dbusmenu_menuitem_get_parent(child) != mi) {
+ g_warning("Trying to remove a child that doesn't believe we're it's parent.");
+ return FALSE;
+ }
+
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
priv->children = g_list_remove(priv->children, child);
+ dbusmenu_menuitem_unparent(child);
#ifdef MASSIVEDEBUGGING
g_debug("Menuitem %d (%s) signalling child removed %d (%s)", ID(mi), LABEL(mi), ID(child), LABEL(child));
#endif
@@ -720,17 +821,17 @@ dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)
}
/**
- dbusmenu_menuitem_child_add_position:
- @mi: The #DbusmenuMenuitem that we're adding the child @child to.
- @child: The #DbusmenuMenuitem to make a child of @mi.
- @position: Where in @mi object's list of chidren @child should be placed.
-
- Puts @child in the list of children for @mi at the location
- specified in @position. If there is not enough entires available
- then @child will be placed at the end of the list.
-
- Return value: Whether @child was added successfully.
-*/
+ * dbusmenu_menuitem_child_add_position:
+ * @mi: The #DbusmenuMenuitem that we're adding the child @child to.
+ * @child: The #DbusmenuMenuitem to make a child of @mi.
+ * @position: Where in @mi object's list of chidren @child should be placed.
+ *
+ * Puts @child in the list of children for @mi at the location
+ * specified in @position. If there is not enough entires available
+ * then @child will be placed at the end of the list.
+ *
+ * Return value: Whether @child was added successfully.
+ */
gboolean
dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)
{
@@ -740,6 +841,10 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem *
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE);
+ if (!dbusmenu_menuitem_set_parent(child, mi)) {
+ return FALSE;
+ }
+
if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) {
dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU);
}
@@ -754,17 +859,17 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem *
}
/**
- 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.
-*/
+ * dbusmenu_menuitem_child_reorder:
+ * @mi: 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)
{
@@ -794,16 +899,16 @@ dbusmenu_menuitem_child_reorder(DbusmenuMenuitem * mi, DbusmenuMenuitem * child,
}
/**
- dbusmenu_menuitem_child_find:
- @mi: The #DbusmenuMenuitem who's children to look on
- @id: The ID of the child that we're looking for.
-
- Search the children of @mi to find one with the ID of @id.
- If it doesn't exist then we return #NULL.
-
- Return value: The menu item with the ID @id or #NULL if it
- can't be found.
-*/
+ * dbusmenu_menuitem_child_find:
+ * @mi: The #DbusmenuMenuitem who's children to look on
+ * @id: The ID of the child that we're looking for.
+ *
+ * Search the children of @mi to find one with the ID of @id.
+ * If it doesn't exist then we return #NULL.
+ *
+ * Return value: (transfer none): The menu item with the ID @id or #NULL if it
+ * can't be found.
+ */
DbusmenuMenuitem *
dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, gint id)
{
@@ -847,18 +952,18 @@ find_id_helper (gpointer in_mi, gpointer in_find_id)
}
/**
- 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.
-*/
+ * 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: (transfer none): 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, gint id)
{
@@ -875,239 +980,397 @@ dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id)
}
/**
- 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.
-*/
+ * dbusmenu_menuitem_set_parent:
+ * @mi: The #DbusmenuMenuitem for which to set the parent
+ * @parent: The new parent #DbusmenuMenuitem
+ *
+ * Sets the parent of @mi to @parent. If @mi already
+ * has a parent, then this call will fail. The parent will
+ * be set automatically when using the usual methods to add a
+ * child menuitem, so this function should not normally be
+ * called directly
+ *
+ * Return value: Whether the parent was set successfully
+ */
+gboolean
+dbusmenu_menuitem_set_parent (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+
+ if (priv->parent != NULL) {
+ g_warning ("Menu item already has a parent");
+ return FALSE;
+ }
+
+ priv->parent = parent;
+ g_object_add_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent);
+
+ return TRUE;
+}
+
+/**
+ * dbusmenu_menuitem_unparent:
+ * @mi: The #DbusmenuMenuitem to unparent
+ *
+ * Unparents the menu item @mi. If @mi doesn't have a
+ * parent, then this call will fail. The menuitem will
+ * be unparented automatically when using the usual methods
+ * to delete a child menuitem, so this function should not
+ * normally be called directly
+ *
+ * Return value: Whether the menu item was unparented successfully
+ */
+gboolean
+dbusmenu_menuitem_unparent (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+
+ if (priv->parent == NULL) {
+ g_warning("Menu item doesn't have a parent");
+ return FALSE;
+ }
+
+ g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent);
+ priv->parent = NULL;
+
+ return TRUE;
+}
+
+/**
+ * dbusmenu_menuitem_get_parent:
+ * @mi: The #DbusmenuMenuitem for which to inspect the parent
+ *
+ * This function looks up the parent of @mi
+ *
+ * Return value: (transfer none): The parent of this menu item
+ */
+DbusmenuMenuitem *
+dbusmenu_menuitem_get_parent (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+
+ return priv->parent;
+}
+
+/**
+ * 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)
{
- GValue val = {0};
- g_value_init(&val, G_TYPE_STRING);
- g_value_set_static_string(&val, value);
- return dbusmenu_menuitem_property_set_value(mi, property, &val);
+ GVariant * variant = NULL;
+ if (value != NULL) {
+ variant = g_variant_new_string(value);
+ }
+ return dbusmenu_menuitem_property_set_variant(mi, property, variant);
}
/**
- dbusmenu_menuitem_property_set_bool:
- @mi: The #DbusmenuMenuitem to set the property on.
- @property: Name of the property to set.
- @value: The value of the property.
-
- Takes a boolean @value and sets it on @property 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.
-*/
+ * dbusmenu_menuitem_property_set_bool:
+ * @mi: The #DbusmenuMenuitem to set the property on.
+ * @property: Name of the property to set.
+ * @value: The value of the property.
+ *
+ * Takes a boolean @value and sets it on @property 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_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value)
{
- GValue val = {0};
- g_value_init(&val, G_TYPE_BOOLEAN);
- g_value_set_boolean(&val, value);
- return dbusmenu_menuitem_property_set_value(mi, property, &val);
+ GVariant * variant = g_variant_new("b", value);
+ return dbusmenu_menuitem_property_set_variant(mi, property, variant);
}
/**
- dbusmenu_menuitem_property_set_int:
- @mi: The #DbusmenuMenuitem to set the property on.
- @property: Name of the property to set.
- @value: The value of the property.
-
- Takes a boolean @value and sets it on @property 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.
-*/
+ * dbusmenu_menuitem_property_set_int:
+ * @mi: The #DbusmenuMenuitem to set the property on.
+ * @property: Name of the property to set.
+ * @value: The value of the property.
+ *
+ * Takes a boolean @value and sets it on @property 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_int (DbusmenuMenuitem * mi, const gchar * property, const gint value)
{
- GValue val = {0};
- g_value_init(&val, G_TYPE_INT);
- g_value_set_int(&val, value);
- return dbusmenu_menuitem_property_set_value(mi, property, &val);
+ GVariant * variant = g_variant_new("i", value);
+ return dbusmenu_menuitem_property_set_variant(mi, property, variant);
}
/**
- 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.
-*/
+ * dbusmenu_menuitem_property_set_variant:
+ * @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_value (DbusmenuMenuitem * mi, const gchar * property, const GValue * value)
+dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * property, GVariant * value)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
g_return_val_if_fail(property != NULL, FALSE);
- g_return_val_if_fail(G_IS_VALUE(value), FALSE);
+ g_return_val_if_fail(g_utf8_validate(property, -1, NULL), FALSE);
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
- /* g_debug("Setting a property. ID: %d Prop: %s Value: %s", priv->id, property, value); */
+ GVariant * default_value = NULL;
+
+ const gchar * type = menuitem_get_type(mi);
+
+ if (value != NULL) {
+ /* Check the expected type to see if we want to have a warning */
+ GVariantType * default_type = dbusmenu_defaults_default_get_type(priv->defaults, type, property);
+ if (default_type != NULL) {
+ /* If we have an expected type we should check to see if
+ the value we've been given is of the same type and generate
+ a warning if it isn't */
+ if (!g_variant_is_of_type(value, default_type)) {
+ g_warning("Setting menuitem property '%s' with value of type '%s' when expecting '%s'", property, g_variant_get_type_string(value), g_variant_type_peek_string(default_type));
+ }
+ }
+ }
- #if 0
- 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;
+ /* Check the defaults database to see if we have a default
+ for this property. */
+ default_value = dbusmenu_defaults_default_get(priv->defaults, type, property);
+ if (default_value != NULL && value != NULL) {
+ /* Now see if we're setting this to the same value as the
+ default. If we are then we just want to swallow this variant
+ and make the function behave like we're clearing it. */
+ if (g_variant_equal(default_value, value)) {
+ g_variant_ref_sink(value);
+ g_variant_unref(value);
+ value = NULL;
+ }
}
- #endif
- gchar * lprop = g_strdup(property);
- GValue * lval = g_new0(GValue, 1);
- g_value_init(lval, G_VALUE_TYPE(value));
- g_value_copy(value, lval);
+ gboolean replaced = FALSE;
+ gboolean remove = FALSE;
+ gchar * hash_key = NULL;
+ GVariant * hash_variant = NULL;
+ gboolean inhash = g_hash_table_lookup_extended(priv->properties, property, (gpointer *)&hash_key, (gpointer *)&hash_variant);
- g_hash_table_replace(priv->properties, lprop, lval);
- #ifdef MASSIVEDEBUGGING
- gchar * valstr = g_strdup_value_contents(lval);
- g_debug("Menuitem %d (%s) signalling property '%s' changed to '%s'", ID(mi), LABEL(mi), property, g_utf8_strlen(valstr, 50) < 25 ? valstr : "<too long>");
- g_free(valstr);
- #endif
+ if (inhash && hash_variant == NULL) {
+ g_warning("The property '%s' is in the hash with a NULL variant", property);
+ inhash = FALSE;
+ }
+
+ if (value != NULL) {
+ /* NOTE: We're only marking this as replaced if this is true
+ but we're actually replacing it no matter. This is so that
+ the variant passed in sticks around which the caller may
+ expect. They shouldn't, but it's low cost to remove bugs. */
+ if (!inhash || !g_variant_equal(hash_variant, value)) {
+ replaced = TRUE;
+ }
+
+ gchar * lprop = g_strdup(property);
+ g_variant_ref_sink(value);
- g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, lprop, lval, TRUE);
+ /* Really important that this is _insert as that means the value
+ that we just created in the _strdup is free'd and not the one
+ currently in the hashtable. That could be the same as the one
+ being passed in and then the signal emit would be done with a
+ bad value */
+ g_hash_table_insert(priv->properties, lprop, value);
+ } else {
+ if (inhash) {
+ /* So the question you should be asking if you're paying attention
+ is "Why not just do the remove here?" It's a good question with
+ an interesting answer. Bascially it's the same reason as above,
+ in a couple cases the passed in properties is the value in the hash
+ table so we can avoid strdup'ing it by removing it (and thus free'ing
+ it) after the signal emition */
+ remove = TRUE;
+ replaced = TRUE;
+ g_hash_table_steal(priv->properties, property);
+ }
+ }
+
+ /* NOTE: The actual value is invalid at this point
+ becuse it has been unref'd when replaced in the hash
+ table. But the fact that there was a value is
+ the imporant part. */
+ if (!inhash || replaced) {
+ GVariant * signalval = value;
+
+ if (signalval == NULL) {
+ /* Might also be NULL, but if it is we're definitely
+ clearing this thing. */
+ signalval = default_value;
+ }
+
+ g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, signalval, TRUE);
+ }
+
+ if (remove) {
+ g_free(hash_key);
+ g_variant_unref(hash_variant);
+ }
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 or is not a string.
-*/
+ * 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: (transfer none): A string with the value of the property
+ * that shouldn't be free'd. Or #NULL if the property
+ * is not set or is not a string.
+ */
const gchar *
dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property)
{
- const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
- if (value == NULL) return NULL;
- if (G_VALUE_TYPE(value) != G_TYPE_STRING) return NULL;
- return g_value_get_string(value);
+ GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
+ if (variant == NULL) return NULL;
+ if (!g_variant_type_equal(g_variant_get_type(variant), G_VARIANT_TYPE_STRING)) return NULL;
+ return g_variant_get_string(variant, NULL);
}
/**
- dbusmenu_menuitem_property_get_value:
- @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 GValue for the property.
-*/
-const GValue *
-dbusmenu_menuitem_property_get_value (DbusmenuMenuitem * mi, const gchar * property)
+ * dbusmenu_menuitem_property_get_variant:
+ * @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: (transfer none): A GVariant for the property.
+ */
+GVariant *
+dbusmenu_menuitem_property_get_variant (DbusmenuMenuitem * mi, const gchar * property)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
g_return_val_if_fail(property != NULL, NULL);
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
- return (const GValue *)g_hash_table_lookup(priv->properties, property);
-}
+ GVariant * currentval = (GVariant *)g_hash_table_lookup(priv->properties, property);
-/**
- dbusmenu_menuitem_property_get_bool:
- @mi: The #DbusmenuMenuitem to look for the property on.
- @property: The property to grab.
+ if (currentval == NULL) {
+ currentval = dbusmenu_defaults_default_get(priv->defaults, menuitem_get_type(mi), property);
+ }
- Look up a property on @mi and return the value of it if
- it exits. Returns #FALSE if the property doesn't exist.
+ return currentval;
+}
- Return value: The value of the property or #FALSE.
-*/
+/**
+ * dbusmenu_menuitem_property_get_bool:
+ * @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. Returns #FALSE if the property doesn't exist.
+ *
+ * Return value: The value of the property or #FALSE.
+ */
gboolean
dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * property)
{
- const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
- if (value == NULL) return FALSE;
- if (G_VALUE_TYPE(value) != G_TYPE_BOOLEAN) {
- if (g_value_type_transformable(G_VALUE_TYPE(value), G_TYPE_BOOLEAN)) {
- GValue boolval = {0};
- g_value_init(&boolval, G_TYPE_BOOLEAN);
- g_value_transform(value, &boolval);
- return g_value_get_boolean(&boolval);
+ GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
+ if (variant == NULL) return FALSE;
+
+ if (g_variant_type_equal(g_variant_get_type(variant), G_VARIANT_TYPE_BOOLEAN)) {
+ return g_variant_get_boolean(variant);
+ }
+
+ if (g_variant_type_equal(g_variant_get_type(variant), G_VARIANT_TYPE_STRING)) {
+ const gchar * string = g_variant_get_string(variant, NULL);
+
+ if (!g_strcmp0(string, "TRUE") || !g_strcmp0(string, "true") || !g_strcmp0(string, "True")) {
+ return TRUE;
} else {
return FALSE;
}
}
- return g_value_get_boolean(value);
+
+ g_warning("Property '%s' has been requested as an boolean but is not one.", property);
+ return FALSE;
}
/**
- dbusmenu_menuitem_property_get_int:
- @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. Returns zero if the property doesn't exist.
-
- Return value: The value of the property or zero.
-*/
+ * dbusmenu_menuitem_property_get_int:
+ * @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. Returns zero if the property doesn't exist.
+ *
+ * Return value: The value of the property or zero.
+ */
gint
dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * property)
{
- const GValue * value = dbusmenu_menuitem_property_get_value(mi, property);
- if (value == NULL) return 0;
- if (G_VALUE_TYPE(value) != G_TYPE_INT) {
- if (g_value_type_transformable(G_VALUE_TYPE(value), G_TYPE_INT)) {
- GValue intval = {0};
- g_value_init(&intval, G_TYPE_INT);
- g_value_transform(value, &intval);
- return g_value_get_int(&intval);
- } else {
- return 0;
- }
+ GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
+ if (variant == NULL) return 0;
+
+ if (g_variant_type_equal(g_variant_get_type(variant), G_VARIANT_TYPE_INT32)) {
+ return g_variant_get_int32(variant);
}
- return g_value_get_int(value);
-}
+ if (g_variant_type_equal(g_variant_get_type(variant), G_VARIANT_TYPE_STRING)) {
+ const gchar * string = g_variant_get_string(variant, NULL);
+ return atoi(string);
+ }
-/**
- dbusmenu_menuitem_property_exit:
- @mi: The #DbusmenuMenuitem to look for the property on.
- @property: The property to look for.
+ g_warning("Property '%s' has been requested as an int but is not one.", property);
+ return 0;
+}
- 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
-*/
+/**
+ * dbusmenu_menuitem_property_exist:
+ * @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)
{
@@ -1122,35 +1385,34 @@ dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property)
}
/**
- dbusmenu_menuitem_property_remove:
- @mi: The #DbusmenuMenuitem to remove the property on.
- @property: The property to look for.
-
- Removes a property from the menuitem.
-*/
+ * dbusmenu_menuitem_property_remove:
+ * @mi: The #DbusmenuMenuitem to remove the property on.
+ * @property: The property to look for.
+ *
+ * Removes a property from the menuitem.
+ */
void
dbusmenu_menuitem_property_remove (DbusmenuMenuitem * mi, const gchar * property)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
g_return_if_fail(property != NULL);
- DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
-
- g_hash_table_remove(priv->properties, property);
+ dbusmenu_menuitem_property_set_variant(mi, property, NULL);
return;
}
/**
- 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.
+ * 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: (transfer container): A list of strings or NULL if there are
+ * none.
*/
GList *
dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi)
@@ -1161,32 +1423,38 @@ dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi)
return g_hash_table_get_keys(priv->properties);
}
+/* Copy the keys and make references to the variants that are
+ in the new table. They'll be free'd and unref'd when the
+ Hashtable gets destroyed. */
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);
+ gchar * key = (gchar *)in_key;
+ GVariant * value = (GVariant *)in_value;
+ g_variant_ref_sink(value);
+ g_hash_table_insert(table, g_strdup(key), 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.
+ * 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: (transfer full): A brand new #GHashTable that contains all of
+ * theroperties 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);
+ GHashTable * ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, _g_variant_unref);
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), ret);
@@ -1196,16 +1464,84 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
return ret;
}
+/* Looks at each value in the hashtable and tries to convert it
+ into a variant and add it to our variant builder */
+static void
+variant_helper (gpointer in_key, gpointer in_value, gpointer user_data)
+{
+ GVariant * value = g_variant_new_dict_entry(g_variant_new_string((gchar *)in_key),
+ g_variant_new_variant((GVariant *)in_value));
+ g_variant_builder_add_value((GVariantBuilder *)user_data, value);
+ return;
+}
+
/**
- dbusmenu_menuitem_set_root:
- @mi: #DbusmenuMenuitem to set whether it's root
- @root: Whether @mi is a root node or not
+ * dbusmenu_menuitem_properties_variant:
+ * @mi: #DbusmenuMenuitem to get properties from
+ *
+ * Grabs the properties of the menuitem as a GVariant with the
+ * type "a{sv}".
+ *
+ * Return Value: (transfer full): A GVariant of type "a{sv}" or NULL on error.
+ */
+GVariant *
+dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
- This function sets the internal value of whether this is a
- root node or not.
+ GVariant * final_variant = NULL;
- Return value: None
-*/
+ if ((properties == NULL || properties[0] == NULL) && g_hash_table_size(priv->properties) > 0) {
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
+ g_hash_table_foreach(priv->properties, variant_helper, &builder);
+
+ final_variant = g_variant_builder_end(&builder);
+ }
+
+ if (properties != NULL) {
+ GVariantBuilder builder;
+ gboolean builder_init = FALSE;
+ int i = 0; const gchar * prop;
+
+ for (prop = properties[i]; prop != NULL; prop = properties[++i]) {
+ GVariant * propvalue = dbusmenu_menuitem_property_get_variant(mi, prop);
+
+ if (propvalue == NULL) {
+ continue;
+ }
+
+ if (!builder_init) {
+ builder_init = TRUE;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+ }
+
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string((gchar *)prop),
+ g_variant_new_variant((GVariant *)propvalue));
+ g_variant_builder_add_value(&builder, dict);
+ }
+
+ if (builder_init) {
+ final_variant = g_variant_builder_end(&builder);
+ }
+ }
+
+ return final_variant;
+}
+
+/**
+ * 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)
{
@@ -1216,14 +1552,14 @@ dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root)
}
/**
- 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
-*/
+ * 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)
{
@@ -1234,38 +1570,64 @@ dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi)
/**
- dbusmenu_menuitem_buildxml:
- @mi: #DbusmenuMenuitem to represent in XML
- @array: (element-type utf8): A list of string that will be turned into an XML file
-
- This function will add strings to the array @array. It will put
- at least one entry if this menu item has no children. If it has
- children it will put two for this entry, one representing the
- start tag and one that is a closing tag. It will allow it's
- children to place their own tags in the array in between those two.
+ * dbusmenu_menuitem_buildvariant:
+ * @mi: #DbusmenuMenuitem to represent in a variant
+ * @properties: (element-type utf8): A list of string that will be put into
+ * a variant
+ *
+ * This function will put at least one entry if this menu item has no children.
+ * If it has children it will put two for this entry, one representing the
+ * start tag and one that is a closing tag. It will allow it's
+ * children to place their own tags in the array in between those two.
+ *
+ * Return value: (transfer full): Variant representing @properties
*/
-void
-dbusmenu_menuitem_buildxml (DbusmenuMenuitem * mi, GPtrArray * array)
+GVariant *
+dbusmenu_menuitem_build_variant (DbusmenuMenuitem * mi, const gchar ** properties, gint recurse)
{
- g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ priv->exposed = TRUE;
gint id = 0;
if (!dbusmenu_menuitem_get_root(mi)) {
id = dbusmenu_menuitem_get_id(mi);
}
+ /* This is the tuple that'll build up being a representation of
+ this entry */
+ GVariantBuilder tupleb;
+ g_variant_builder_init(&tupleb, G_VARIANT_TYPE_TUPLE);
+
+ /* Add our ID */
+ g_variant_builder_add_value(&tupleb, g_variant_new_int32(id));
+
+ /* Figure out the properties */
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, properties);
+ if (props != NULL) {
+ g_variant_builder_add_value(&tupleb, props);
+ } else {
+ g_variant_builder_add_value(&tupleb, g_variant_parse(G_VARIANT_TYPE("a{sv}"), "[ ]", NULL, NULL, NULL));
+ }
+
+ /* Pillage the children */
GList * children = dbusmenu_menuitem_get_children(mi);
- if (children == NULL) {
- g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\"/>", id));
+ if (children == NULL || recurse == 0) {
+ g_variant_builder_add_value(&tupleb, g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0));
} else {
- g_ptr_array_add(array, g_strdup_printf("<menu id=\"%d\">", id));
+ GVariantBuilder childrenbuilder;
+ g_variant_builder_init(&childrenbuilder, G_VARIANT_TYPE_ARRAY);
+
for ( ; children != NULL; children = children->next) {
- dbusmenu_menuitem_buildxml(DBUSMENU_MENUITEM(children->data), array);
+ GVariant * child = dbusmenu_menuitem_build_variant(DBUSMENU_MENUITEM(children->data), properties, recurse - 1);
+
+ g_variant_builder_add_value(&childrenbuilder, g_variant_new_variant(child));
}
- g_ptr_array_add(array, g_strdup("</menu>"));
+
+ g_variant_builder_add_value(&tupleb, g_variant_builder_end(&childrenbuilder));
}
- return;
+ return g_variant_builder_end(&tupleb);
}
typedef struct {
@@ -1281,15 +1643,15 @@ foreach_helper (gpointer data, gpointer user_data)
}
/**
- dbusmenu_menuitem_foreach:
- @mi: The #DbusmenItem to start from
- @func: Function to call on every node in the tree
- @data: (closure): 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.
+ * dbusmenu_menuitem_foreach:
+ * @mi: The #DbusmenItem to start from
+ * @func: Function to call on every node in the tree
+ * @data: (closure): 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)
@@ -1305,26 +1667,26 @@ dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem
}
/**
- dbusmenu_menuitem_handle_event:
- @mi: The #DbusmenuMenuitem to send the signal on.
- @name: The name of the signal
- @value: A value that could be set for the event
- @timestamp: The timestamp of when the event happened
-
- This function is called to create an event. It is likely
- to be overrided by subclasses. The default menu item
- will respond to the activate signal and do:
-
- Emits the #DbusmenuMenuitem::item-activate signal on this
- menu item. Called by server objects when they get the
- appropriate DBus signals from the client.
-
- If you subclass this function you should really think
- about calling the parent function unless you have a good
- reason not to.
+ * dbusmenu_menuitem_handle_event:
+ * @mi: The #DbusmenuMenuitem to send the signal on.
+ * @name: The name of the signal
+ * @variant: A value that could be set for the event
+ * @timestamp: The timestamp of when the event happened
+ *
+ * This function is called to create an event. It is likely
+ * to be overrided by subclasses. The default menu item
+ * will respond to the activate signal and do:
+ *
+ * Emits the #DbusmenuMenuitem::item-activate signal on this
+ * menu item. Called by server objects when they get the
+ * appropriate DBus signals from the client.
+ *
+ * If you subclass this function you should really think
+ * about calling the parent function unless you have a good
+ * reason not to.
*/
void
-dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp)
+dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
#ifdef MASSIVEDEBUGGING
@@ -1332,25 +1694,35 @@ dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, const
#endif
DbusmenuMenuitemClass * class = DBUSMENU_MENUITEM_GET_CLASS(mi);
- if (class->handle_event != NULL) {
- return class->handle_event(mi, name, value, timestamp);
+ gboolean handled = FALSE;
+ if (variant == NULL) {
+ variant = g_variant_new_int32(0);
}
- return;
+
+ g_variant_ref_sink(variant);
+
+ g_signal_emit(G_OBJECT(mi), signals[EVENT], g_quark_from_string(name), name, variant, timestamp, &handled);
+
+ if (!handled && class->handle_event != NULL) {
+ class->handle_event(mi, name, variant, timestamp);
+ }
+
+ g_variant_unref(variant);
}
/**
- dbusmenu_menuitem_send_about_to_show:
- @mi: The #DbusmenuMenuitem to send the signal on.
- @cb: Callback to call when the call has returned.
- @cb_data: (closure): Data to pass to the callback.
-
- This function is used to send the even that the submenu
- of this item is about to be shown. Callers to this event
- should delay showing the menu until their callback is
- called if possible.
-*/
+ * dbusmenu_menuitem_send_about_to_show:
+ * @mi: The #DbusmenuMenuitem to send the signal on.
+ * @cb: Callback to call when the call has returned.
+ * @cb_data: (closure): Data to pass to the callback.
+ *
+ * This function is used to send the even that the submenu
+ * of this item is about to be shown. Callers to this event
+ * should delay showing the menu until their callback is
+ * called if possible.
+ */
void
-dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, dbusmenu_menuitem_about_to_show_cb cb, gpointer cb_data)
+dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data)
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
#ifdef MASSIVEDEBUGGING
@@ -1368,14 +1740,14 @@ dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, dbusmenu_menuitem_a
}
/**
- dbusmenu_menuitem_show_to_user:
- @mi: #DbusmenuMenuitem to show
- @timestamp: The time that the user requested it to be shown
-
- Signals that this menu item should be shown to the user. If this is
- server side the server will then take it and send it over the
- bus.
-*/
+ * dbusmenu_menuitem_show_to_user:
+ * @mi: #DbusmenuMenuitem to show
+ * @timestamp: The time that the user requested it to be shown
+ *
+ * Signals that this menu item should be shown to the user. If this is
+ * server side the server will then take it and send it over the
+ * bus.
+ */
void
dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp)
{
@@ -1385,3 +1757,31 @@ dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp)
return;
}
+
+/* Checks to see if the value of this property is unique or just the
+ default value. */
+gboolean
+dbusmenu_menuitem_property_is_default (DbusmenuMenuitem * mi, const gchar * property)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+
+ GVariant * currentval = (GVariant *)g_hash_table_lookup(priv->properties, property);
+ if (currentval != NULL) {
+ /* If we're storing it locally, then it shouldn't be a default */
+ return FALSE;
+ }
+
+ /* If we haven't stored it locally, then it's the default */
+ return TRUE;
+}
+
+/* Check to see if this menu item has been sent into the bus yet or
+ not. If no one cares we can give less info */
+gboolean
+dbusmenu_menuitem_exposed (DbusmenuMenuitem * mi)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
+ DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ return priv->exposed;
+}
diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h
index 9be938b..985e1a3 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -41,45 +41,329 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_MENUITEM))
#define DBUSMENU_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_MENUITEM, DbusmenuMenuitemClass))
-
+/* ***************************************** */
+/* ********* GLib Object Signals ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED:
+ *
+ * String to attach to signal #DbusmenuServer::property-changed
+ */
#define DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED "property-changed"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED:
+ *
+ * String to attach to signal #DbusmenuServer::item-activated
+ */
#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED:
+ *
+ * String to attach to signal #DbusmenuServer::child-added
+ */
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED "child-added"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED:
+ *
+ * String to attach to signal #DbusmenuServer::child-removed
+ */
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED "child-removed"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED:
+ *
+ * String to attach to signal #DbusmenuServer::child-moved
+ */
#define DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED "child-moved"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_REALIZED:
+ *
+ * String to attach to signal #DbusmenuServer::realized
+ */
#define DBUSMENU_MENUITEM_SIGNAL_REALIZED "realized"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_REALIZED_ID:
+ *
+ * ID to attach to signal #DbusmenuServer::realized
+ */
#define DBUSMENU_MENUITEM_SIGNAL_REALIZED_ID (g_signal_lookup(DBUSMENU_MENUITEM_SIGNAL_REALIZED, DBUSMENU_TYPE_MENUITEM))
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_SHOW_TO_USER:
+ *
+ * String to attach to signal #DbusmenuServer::show-to-user
+ */
#define DBUSMENU_MENUITEM_SIGNAL_SHOW_TO_USER "show-to-user"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW:
+ *
+ * String to attach to signal #DbusmenuServer::about-to-show
+ */
+#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show"
+/**
+ * DBUSMENU_MENUITEM_SIGNAL_EVENT:
+ *
+ * String to attach to signal #DbusmenuServer::event
+ */
+#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event"
+/* ***************************************** */
+/* ********* Menuitem Properties ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_PROP_TYPE:
+ *
+ * #DbusmenuMenuitem property used to represent what type of menuitem
+ * this object represents. Type: #G_VARIANT_TYPE_STRING.
+ */
#define DBUSMENU_MENUITEM_PROP_TYPE "type"
+/**
+ * DBUSMENU_MENUITEM_PROP_VISIBLE:
+ *
+ * #DbusmenuMenuitem property used to represent whether the menuitem
+ * should be shown or not. Type: #G_VARIANT_TYPE_BOOLEAN.
+ */
#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible"
+/**
+ * DBUSMENU_MENUITEM_PROP_ENABLED:
+ *
+ * #DbusmenuMenuitem property used to represent whether the menuitem
+ * is clickable or not. Type: #G_VARIANT_TYPE_BOOLEAN.
+ */
#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled"
+/**
+ * DBUSMENU_MENUITEM_PROP_LABEL:
+ *
+ * #DbusmenuMenuitem property used for the text on the menu item.
+ * Type: #G_VARIANT_TYPE_STRING
+ */
#define DBUSMENU_MENUITEM_PROP_LABEL "label"
+/**
+ * DBUSMENU_MENUITEM_PROP_ICON_NAME:
+ *
+ * #DbusmenuMenuitem property that is the name of the icon under the
+ * Freedesktop.org icon naming spec. Type: #G_VARIANT_TYPE_STRING
+ */
#define DBUSMENU_MENUITEM_PROP_ICON_NAME "icon-name"
+/**
+ * DBUSMENU_MENUITEM_PROP_ICON_DATA:
+ *
+ * #DbusmenuMenuitem property that is the raw data of a custom icon
+ * used in the application. Type: #G_VARIANT_TYPE_VARIANT
+ *
+ * It is recommended that this is not set directly but instead the
+ * libdbusmenu-gtk library is used with the function dbusmenu_menuitem_property_set_image()
+ */
#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data"
+/**
+ * DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE:
+ *
+ * #DbusmenuMenuitem property that says what type of toggle entry should
+ * be shown in the menu. Should be either #DBUSMENU_MENUITEM_TOGGLE_CHECK
+ * or #DBUSMENU_MENUITEM_TOGGLE_RADIO. Type: #G_VARIANT_TYPE_STRING
+ */
#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type"
+/**
+ * DBUSMENU_MENUITEM_PROP_TOGGLE_STATE:
+ *
+ * #DbusmenuMenuitem property that says what state a toggle entry should
+ * be shown as the menu. Should be either #DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED
+ * #DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED or #DBUSMENU_MENUITEM_TOGGLE_STATUE_UNKNOWN.
+ * Type: #G_VARIANT_TYPE_INT32
+ */
#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state"
+/**
+ * DBUSMENU_MENUITEM_PROP_SHORTCUT:
+ *
+ * #DbusmenuMenuitem property that is the entries that represent a shortcut
+ * to activate the menuitem. It is an array of arrays of strings.
+ * Type: #G_VARIANT_TYPE_ARRAY
+ *
+ * It is recommended that this is not set directly but instead the
+ * libdbusmenu-gtk library is used with the function dbusmenu_menuitem_property_set_shortcut()
+ */
#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut"
+/**
+ * DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY:
+ *
+ * #DbusmenuMenuitem property that tells how the children of this menuitem
+ * should be displayed. Most likely this will be unset or of the value
+ * #DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU. Type: #G_VARIANT_TYPE_STRING
+ */
#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display"
+/**
+ * DBUSMENU_MENUITEM_PROP_DISPOSITION:
+ *
+ * #DbusmenuMenuitem property to tell what type of information that the
+ * menu item is displaying to the user. Type: #G_VARIANT_TYPE_STRING
+ */
+#define DBUSMENU_MENUITEM_PROP_DISPOSITION "disposition"
+/* ***************************************** */
+/* ********* Toggle Values ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_TOGGLE_CHECK:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE to be a standard
+ * check mark item.
+ */
#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark"
+/**
+ * DBUSMENU_MENUITEM_TOGGLE_RADIO:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE to be a standard
+ * radio item.
+ */
#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio"
+/* ***************************************** */
+/* ********* Toggle States ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_STATE so that the menu's
+ * toggle item is empty.
+ */
#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0
+/**
+ * DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_STATE so that the menu's
+ * toggle item is filled.
+ */
#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1
+/**
+ * DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_STATE so that the menu's
+ * toggle item is undecided.
+ */
#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN -1
+/* ***************************************** */
+/* ********* Icon specials ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_ICON_NAME_BLANK:
+ *
+ * Used to set #DBUSMENU_MENUITEM_PROP_TOGGLE_STATE so that the menu's
+ * toggle item is undecided.
+ */
#define DBUSMENU_MENUITEM_ICON_NAME_BLANK "blank-icon"
+/* ***************************************** */
+/* ********* Shortcut Modifiers ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_SHORTCUT_CONTROL:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_SHORTCUT to represent the
+ * control key.
+ */
#define DBUSMENU_MENUITEM_SHORTCUT_CONTROL "Control"
+/**
+ * DBUSMENU_MENUITEM_SHORTCUT_ALT:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_SHORTCUT to represent the
+ * alternate key.
+ */
#define DBUSMENU_MENUITEM_SHORTCUT_ALT "Alt"
+/**
+ * DBUSMENU_MENUITEM_SHORTCUT_SHIFT:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_SHORTCUT to represent the
+ * shift key.
+ */
#define DBUSMENU_MENUITEM_SHORTCUT_SHIFT "Shift"
+/**
+ * DBUSMENU_MENUITEM_SHORTCUT_SUPER:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_SHORTCUT to represent the
+ * super key.
+ */
#define DBUSMENU_MENUITEM_SHORTCUT_SUPER "Super"
+/* ***************************************** */
+/* ********* Child Display Types ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY to have the
+ * subitems displayed as a submenu.
+ */
#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu"
+/* ***************************************** */
+/* ********* Menuitem Dispositions ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_DISPOSITION_NORMAL:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu
+ * item displayed in the normal manner. Default value.
+ */
+#define DBUSMENU_MENUITEM_DISPOSITION_NORMAL "normal"
+/**
+ * DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu
+ * item displayed in a way that conveys it's giving additional
+ * information to the user.
+ */
+#define DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE "informative"
+/**
+ * DBUSMENU_MENUITEM_DISPOSITION_WARNING:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu
+ * item displayed in a way that conveys it's giving a warning
+ * to the user.
+ */
+#define DBUSMENU_MENUITEM_DISPOSITION_WARNING "warning"
+/**
+ * DBUSMENU_MENUITEM_DISPOSITION_ALERT:
+ *
+ * Used in #DBUSMENU_MENUITEM_PROP_DISPOSITION to have a menu
+ * item displayed in a way that conveys it's giving an alert
+ * to the user.
+ */
+#define DBUSMENU_MENUITEM_DISPOSITION_ALERT "alert"
+
+/* ***************************************** */
+/* ********* Dbusmenu Events ********* */
+/* ***************************************** */
+/**
+ * DBUSMENU_MENUITEM_EVENT_ACTIVATED:
+ *
+ * String for the event identifier when a menu item is clicked
+ * on by the user.
+ */
+#define DBUSMENU_MENUITEM_EVENT_ACTIVATED "clicked"
+
+/**
+ * DBUSMENU_MENUITEM_EVENT_OPENED:
+ *
+ * String for the event identifier when a menu is opened and
+ * displayed to the user. Only valid for items that contain
+ * submenus.
+ */
+#define DBUSMENU_MENUITEM_EVENT_OPENED "opened"
+
+/**
+ * DBUSMENU_MENUITEM_EVENT_CLOSED:
+ *
+ * String for the event identifier when a menu is closed and
+ * displayed to the user. Only valid for items that contain
+ * submenus.
+ */
+#define DBUSMENU_MENUITEM_EVENT_CLOSED "closed"
+
+typedef struct _DbusmenuMenuitemPrivate DbusmenuMenuitemPrivate;
+
/**
* DbusmenuMenuitem:
+ * @parent: Parent object
+ * @priv: Private data
*
* This is the #GObject based object that represents a menu
* item. It gets created the same on both the client and
@@ -92,6 +376,9 @@ typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
struct _DbusmenuMenuitem
{
GObject parent;
+
+ /*< Private >*/
+ DbusmenuMenuitemPrivate * priv;
};
/**
@@ -105,27 +392,40 @@ struct _DbusmenuMenuitem
typedef void (*dbusmenu_menuitem_about_to_show_cb) (DbusmenuMenuitem * mi, gpointer user_data);
/**
- * dbusmenu_menuitem_buildxml_slot_t:
+ * dbusmenu_menuitem_buildvariant_slot_t:
* @mi: (in): Menu item that should be built from
- * @stringarray: (inout) (transfer none) (array) (element-type utf8): An array of strings that can be combined into an XML file.
+ * @properties: (allow-none): A list of properties that should be the only ones in the resulting variant structure
*
* This is the function that is called to represent this menu item
- * as an XML fragment. Should call it's own children.
+ * as a variant. Should call it's own children.
+ *
+ * Return value: (transfer full): A variant representing this item and it's children
*/
-typedef void (*dbusmenu_menuitem_buildxml_slot_t) (DbusmenuMenuitem * mi, GPtrArray* stringarray);
+typedef GVariant * (*dbusmenu_menuitem_buildvariant_slot_t) (DbusmenuMenuitem * mi, gchar ** properties);
/**
* DbusmenuMenuitemClass:
+ * @parent_class: Functions and signals from our parent
* @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.
* @child_moved: Slot for #DbusmenuMenuitem::child-moved.
* @realized: Slot for #DbusmenuMenuitem::realized.
- * @buildxml: Virtual function that appends the strings required to represent this menu item in the menu XML file.
+ * @about_to_show: Slot for #DbusmenuMenuitem::about-to-show.
+ * @buildvariant: Virtual function that appends the strings required to represent this menu item in the menu variant.
* @handle_event: This function is to override how events are handled by subclasses. Look at #dbusmenu_menuitem_handle_event for lots of good information.
* @send_about_to_show: Virtual function that notifies server that the client is about to show a menu.
* @show_to_user: Slot for #DbusmenuMenuitem::show-to-user.
+ * @event: Slot for #DbsumenuMenuitem::event.
+ * @reserved1: Reserved for future use.
+ * @reserved2: Reserved for future use.
+ * @reserved3: Reserved for future use.
+ * @reserved4: Reserved for future use.
+ * @reserved5: Reserved for future use.
+ *
+ * Functions and signals that every menuitem should know something
+ * about.
*/
typedef struct _DbusmenuMenuitemClass DbusmenuMenuitemClass;
struct _DbusmenuMenuitemClass
@@ -133,7 +433,7 @@ struct _DbusmenuMenuitemClass
GObjectClass parent_class;
/* Signals */
- void (*property_changed) (gchar * property, GValue * value);
+ void (*property_changed) (gchar * property, GVariant * value);
void (*item_activated) (guint timestamp);
void (*child_added) (DbusmenuMenuitem * child, guint position);
void (*child_removed) (DbusmenuMenuitem * child);
@@ -141,15 +441,21 @@ struct _DbusmenuMenuitemClass
void (*realized) (void);
/* Virtual functions */
- dbusmenu_menuitem_buildxml_slot_t buildxml;
- void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
- void (*send_about_to_show) (DbusmenuMenuitem * mi, dbusmenu_menuitem_about_to_show_cb cb, gpointer cb_data);
+ dbusmenu_menuitem_buildvariant_slot_t buildvariant;
+ void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp);
+ void (*send_about_to_show) (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data);
void (*show_to_user) (DbusmenuMenuitem * mi, guint timestamp, gpointer cb_data);
- /* void (*reserved1) (void); */
- /* void (*reserved2) (void); */
- /* void (*reserved3) (void); */
- /* void (*reserved4) (void); -- realized, realloc when bumping lib version */
+ gboolean (*about_to_show) (void);
+
+ void (*event) (const gchar * name, GVariant * value, guint timestamp);
+
+ /*< Private >*/
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+ void (*reserved5) (void);
};
GType dbusmenu_menuitem_get_type (void);
@@ -171,12 +477,16 @@ gboolean dbusmenu_menuitem_child_reorder (DbusmenuMenuitem * mi, DbusmenuMenuite
DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, gint id);
DbusmenuMenuitem * dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id);
+gboolean dbusmenu_menuitem_set_parent (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent);
+gboolean dbusmenu_menuitem_unparent (DbusmenuMenuitem *mi);
+DbusmenuMenuitem * dbusmenu_menuitem_get_parent (DbusmenuMenuitem * mi);
+
gboolean dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value);
-gboolean dbusmenu_menuitem_property_set_value (DbusmenuMenuitem * mi, const gchar * property, const GValue * value);
+gboolean dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * property, GVariant * value);
gboolean dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value);
gboolean dbusmenu_menuitem_property_set_int (DbusmenuMenuitem * mi, const gchar * property, const gint value);
const gchar * dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property);
-const GValue * dbusmenu_menuitem_property_get_value (DbusmenuMenuitem * mi, const gchar * property);
+GVariant * dbusmenu_menuitem_property_get_variant (DbusmenuMenuitem * mi, const gchar * property);
gboolean dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * property);
gint dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * property);
gboolean dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property);
@@ -188,8 +498,8 @@ void dbusmenu_menuitem_set_root (DbusmenuMenuitem * mi, gboolean root);
gboolean dbusmenu_menuitem_get_root (DbusmenuMenuitem * mi);
void dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data);
-void dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, guint timestamp);
-void dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, dbusmenu_menuitem_about_to_show_cb cb, gpointer cb_data);
+void dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * variant, guint timestamp);
+void dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data);
void dbusmenu_menuitem_show_to_user (DbusmenuMenuitem * mi, guint timestamp);
diff --git a/libdbusmenu-glib/server-marshal.list b/libdbusmenu-glib/server-marshal.list
index 0d68c4e..08ebf93 100644
--- a/libdbusmenu-glib/server-marshal.list
+++ b/libdbusmenu-glib/server-marshal.list
@@ -1,3 +1,3 @@
-VOID: INT, STRING, POINTER
+VOID: INT, STRING, VARIANT
VOID: UINT, INT
VOID: INT, UINT
diff --git a/libdbusmenu-glib/server.c b/libdbusmenu-glib/server.c
index 68afb92..c7057df 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -30,41 +30,42 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
#include "menuitem-private.h"
#include "server.h"
#include "server-marshal.h"
+#include "enum-types.h"
-/* DBus Prototypes */
-static gboolean _dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error);
-static gboolean _dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error);
-static gboolean _dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error);
-static gboolean _dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error);
-static gboolean _dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error);
-static gboolean _dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error);
-static gboolean _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error);
-/* DBus Helpers */
-static void _gvalue_array_append_int(GValueArray *array, gint i);
-static void _gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict);
-
-#include "dbusmenu-server.h"
+#include "dbus-menu-clean.xml.h"
static void layout_update_signal (DbusmenuServer * server);
-#define DBUSMENU_VERSION_NUMBER 2
+#define DBUSMENU_VERSION_NUMBER 2
+#define DBUSMENU_INTERFACE "com.canonical.dbusmenu"
/* Privates, I'll show you mine... */
-typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
-
struct _DbusmenuServerPrivate
{
DbusmenuMenuitem * root;
gchar * dbusobject;
gint layout_revision;
guint layout_idle;
+
+ GDBusConnection * bus;
+ GCancellable * bus_lookup;
+ guint dbus_registration;
+
+ DbusmenuTextDirection text_direction;
+ DbusmenuStatus status;
+ GStrv icon_dirs;
+
+ GArray * prop_array;
+ guint property_idle;
};
-#define DBUSMENU_SERVER_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate))
+#define DBUSMENU_SERVER_GET_PRIVATE(o) (DBUSMENU_SERVER(o)->priv)
/* Signals */
enum {
@@ -82,7 +83,10 @@ enum {
PROP_0,
PROP_DBUS_OBJECT,
PROP_ROOT_NODE,
- PROP_VERSION
+ PROP_VERSION,
+ PROP_TEXT_DIRECTION,
+ PROP_STATUS,
+ PROP_ICON_THEME_DIRS
};
/* Errors */
@@ -91,22 +95,112 @@ enum {
INVALID_PROPERTY_NAME,
UNKNOWN_DBUS_ERROR,
NOT_IMPLEMENTED,
+ NO_VALID_LAYOUT,
LAST_ERROR
};
+/* Method Table */
+typedef void (*MethodTableFunc) (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation);
+
+typedef struct _method_table_t method_table_t;
+struct _method_table_t {
+ const gchar * interned_name;
+ MethodTableFunc func;
+};
+
+enum {
+ METHOD_GET_LAYOUT = 0,
+ METHOD_GET_GROUP_PROPERTIES,
+ METHOD_GET_CHILDREN,
+ METHOD_GET_PROPERTY,
+ METHOD_GET_PROPERTIES,
+ METHOD_EVENT,
+ METHOD_ABOUT_TO_SHOW,
+ /* Counter, do not remove! */
+ METHOD_COUNT
+};
+
/* Prototype */
-static void dbusmenu_server_class_init (DbusmenuServerClass *class);
-static void dbusmenu_server_init (DbusmenuServer *self);
-static void dbusmenu_server_dispose (GObject *object);
-static void dbusmenu_server_finalize (GObject *object);
-static void set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec);
-static void get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec);
-static void menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, 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);
-static GQuark error_quark (void);
+static void dbusmenu_server_class_init (DbusmenuServerClass *class);
+static void dbusmenu_server_init (DbusmenuServer *self);
+static void dbusmenu_server_dispose (GObject *object);
+static void dbusmenu_server_finalize (GObject *object);
+static void set_property (GObject * obj,
+ guint id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void get_property (GObject * obj,
+ guint id,
+ GValue * value,
+ GParamSpec * pspec);
+static void default_text_direction (DbusmenuServer * server);
+static void register_object (DbusmenuServer * server);
+static void bus_got_cb (GObject * obj,
+ GAsyncResult * result,
+ gpointer user_data);
+static void bus_method_call (GDBusConnection * connection,
+ const gchar * sender,
+ const gchar * path,
+ const gchar * interface,
+ const gchar * method,
+ GVariant * params,
+ GDBusMethodInvocation * invocation,
+ gpointer user_data);
+static GVariant * bus_get_prop (GDBusConnection * connection,
+ const gchar * sender,
+ const gchar * path,
+ const gchar * interface,
+ const gchar * property,
+ GError ** error,
+ gpointer user_data);
+static void menuitem_property_changed (DbusmenuMenuitem * mi,
+ gchar * property,
+ GVariant * variant,
+ 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);
+static GQuark error_quark (void);
+static void prop_array_teardown (GArray * prop_array);
+static void bus_get_layout (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_group_properties (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_children (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_property (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_get_properties (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_event (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+static void bus_about_to_show (DbusmenuServer * server,
+ GVariant * params,
+ GDBusMethodInvocation * invocation);
+
+/* Globals */
+static GDBusNodeInfo * dbusmenu_node_info = NULL;
+static GDBusInterfaceInfo * dbusmenu_interface_info = NULL;
+static const GDBusInterfaceVTable dbusmenu_interface_table = {
+ method_call: bus_method_call,
+ get_property: bus_get_prop,
+ set_property: NULL /* No properties that can be set */
+};
+static method_table_t dbusmenu_method_table[METHOD_COUNT];
G_DEFINE_TYPE (DbusmenuServer, dbusmenu_server, G_TYPE_OBJECT);
@@ -137,8 +231,8 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(DbusmenuServerClass, id_prop_update),
NULL, NULL,
- _dbusmenu_server_marshal_VOID__INT_STRING_POINTER,
- G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VALUE);
+ _dbusmenu_server_marshal_VOID__INT_STRING_VARIANT,
+ G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_VARIANT);
/**
DbusmenuServer::id-update:
@arg0: The #DbusmenuServer emitting the signal.
@@ -193,7 +287,7 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
g_object_class_install_property (object_class, PROP_DBUS_OBJECT,
g_param_spec_string(DBUSMENU_SERVER_PROP_DBUS_OBJECT, "DBus object path",
"The object that represents this set of menus on DBus",
- "/org/ayatana/dbusmenu",
+ "/com/canonical/dbusmenu",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_ROOT_NODE,
g_param_spec_object(DBUSMENU_SERVER_PROP_ROOT_NODE, "Root menu node",
@@ -205,8 +299,56 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
"The version of the DBusmenu API that we're implementing.",
DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER, DBUSMENU_VERSION_NUMBER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_TEXT_DIRECTION,
+ g_param_spec_enum(DBUSMENU_SERVER_PROP_TEXT_DIRECTION, "The default direction of text",
+ "The object that represents this set of menus on DBus",
+ DBUSMENU_TYPE_TEXT_DIRECTION, DBUSMENU_TEXT_DIRECTION_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_STATUS,
+ g_param_spec_enum(DBUSMENU_SERVER_PROP_STATUS, "Status of viewing the menus",
+ "Exports over DBus whether the menus should be given special visuals",
+ DBUSMENU_TYPE_STATUS, DBUSMENU_STATUS_NORMAL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ if (dbusmenu_node_info == NULL) {
+ GError * error = NULL;
+
+ dbusmenu_node_info = g_dbus_node_info_new_for_xml(dbus_menu_clean_xml, &error);
+ if (error != NULL) {
+ g_error("Unable to parse DBusmenu Interface description: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (dbusmenu_interface_info == NULL) {
+ dbusmenu_interface_info = g_dbus_node_info_lookup_interface(dbusmenu_node_info, DBUSMENU_INTERFACE);
+
+ if (dbusmenu_interface_info == NULL) {
+ g_error("Unable to find interface '" DBUSMENU_INTERFACE "'");
+ }
+ }
+
+ /* Building our Method table :( */
+ dbusmenu_method_table[METHOD_GET_LAYOUT].interned_name = g_intern_static_string("GetLayout");
+ dbusmenu_method_table[METHOD_GET_LAYOUT].func = bus_get_layout;
+
+ dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].interned_name = g_intern_static_string("GetGroupProperties");
+ dbusmenu_method_table[METHOD_GET_GROUP_PROPERTIES].func = bus_get_group_properties;
- dbus_g_object_type_install_info(DBUSMENU_TYPE_SERVER, &dbus_glib__dbusmenu_server_object_info);
+ dbusmenu_method_table[METHOD_GET_CHILDREN].interned_name = g_intern_static_string("GetChildren");
+ dbusmenu_method_table[METHOD_GET_CHILDREN].func = bus_get_children;
+
+ dbusmenu_method_table[METHOD_GET_PROPERTY].interned_name = g_intern_static_string("GetProperty");
+ dbusmenu_method_table[METHOD_GET_PROPERTY].func = bus_get_property;
+
+ dbusmenu_method_table[METHOD_GET_PROPERTIES].interned_name = g_intern_static_string("GetProperties");
+ dbusmenu_method_table[METHOD_GET_PROPERTIES].func = bus_get_properties;
+
+ dbusmenu_method_table[METHOD_EVENT].interned_name = g_intern_static_string("Event");
+ dbusmenu_method_table[METHOD_EVENT].func = bus_event;
+
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].interned_name = g_intern_static_string("AboutToShow");
+ dbusmenu_method_table[METHOD_ABOUT_TO_SHOW].func = bus_about_to_show;
return;
}
@@ -214,12 +356,21 @@ dbusmenu_server_class_init (DbusmenuServerClass *class)
static void
dbusmenu_server_init (DbusmenuServer *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_TYPE_SERVER, DbusmenuServerPrivate);
+
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(self);
priv->root = NULL;
priv->dbusobject = NULL;
priv->layout_revision = 1;
priv->layout_idle = 0;
+ priv->bus = NULL;
+ priv->bus_lookup = NULL;
+ priv->dbus_registration = 0;
+
+ default_text_direction(self);
+ priv->status = DBUSMENU_STATUS_NORMAL;
+ priv->icon_dirs = NULL;
return;
}
@@ -231,6 +382,17 @@ dbusmenu_server_dispose (GObject *object)
if (priv->layout_idle != 0) {
g_source_remove(priv->layout_idle);
+ priv->layout_idle = 0;
+ }
+
+ if (priv->property_idle != 0) {
+ g_source_remove(priv->property_idle);
+ priv->property_idle = 0;
+ }
+
+ if (priv->prop_array != NULL) {
+ prop_array_teardown(priv->prop_array);
+ priv->prop_array = NULL;
}
if (priv->root != NULL) {
@@ -238,6 +400,27 @@ dbusmenu_server_dispose (GObject *object)
g_object_unref(priv->root);
}
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
+ priv->dbus_registration = 0;
+ }
+
+ if (priv->bus != NULL) {
+ g_object_unref(priv->bus);
+ priv->bus = NULL;
+ }
+
+ if (priv->bus_lookup != NULL) {
+ if (!g_cancellable_is_cancelled(priv->bus_lookup)) {
+ /* Note, this may case the async function to run at
+ some point in the future. That's okay, it'll get an
+ error, but just FYI */
+ g_cancellable_cancel(priv->bus_lookup);
+ }
+ g_object_unref(priv->bus_lookup);
+ priv->bus_lookup = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->dispose (object);
return;
}
@@ -245,6 +428,13 @@ dbusmenu_server_dispose (GObject *object)
static void
dbusmenu_server_finalize (GObject *object)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(object);
+
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
G_OBJECT_CLASS (dbusmenu_server_parent_class)->finalize (object);
return;
}
@@ -253,27 +443,37 @@ static void
set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(obj);
- GError * error = NULL;
switch (id) {
case PROP_DBUS_OBJECT:
g_return_if_fail(priv->dbusobject == NULL);
priv->dbusobject = g_value_dup_string(value);
- DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (connection == NULL || error != NULL) {
- g_warning("Unable to get session bus: %s", error == NULL ? "No message" : error->message);
- if (error != NULL) { g_error_free(error); }
+ if (priv->bus == NULL) {
+ if (priv->bus_lookup == NULL) {
+ priv->bus_lookup = g_cancellable_new();
+ g_return_if_fail(priv->bus_lookup != NULL);
+ }
+
+ g_object_ref(obj);
+ g_bus_get(G_BUS_TYPE_SESSION, priv->bus_lookup, bus_got_cb, obj);
} else {
- dbus_g_connection_register_g_object(connection,
- priv->dbusobject,
- obj);
+ register_object(DBUSMENU_SERVER(obj));
}
break;
case PROP_ROOT_NODE:
if (priv->root != NULL) {
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_remove, obj);
dbusmenu_menuitem_set_root(priv->root, FALSE);
+
+ GList * properties = dbusmenu_menuitem_properties_list(priv->root);
+ GList * iter;
+ for (iter = properties; iter != NULL; iter = g_list_next(iter)) {
+ gchar * property = (gchar *)iter->data;
+ menuitem_property_changed(priv->root, property, NULL, DBUSMENU_SERVER(obj));
+ }
+ g_list_free(properties);
+
g_object_unref(G_OBJECT(priv->root));
priv->root = NULL;
}
@@ -282,25 +482,80 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
g_object_ref(G_OBJECT(priv->root));
dbusmenu_menuitem_set_root(priv->root, TRUE);
dbusmenu_menuitem_foreach(priv->root, menuitem_signals_create, obj);
+
+ GList * properties = dbusmenu_menuitem_properties_list(priv->root);
+ GList * iter;
+ for (iter = properties; iter != NULL; iter = g_list_next(iter)) {
+ gchar * property = (gchar *)iter->data;
+ menuitem_property_changed(priv->root, property, dbusmenu_menuitem_property_get_variant(priv->root, property), DBUSMENU_SERVER(obj));
+ }
+ g_list_free(properties);
} else {
g_debug("Setting root node to NULL");
}
layout_update_signal(DBUSMENU_SERVER(obj));
break;
- default:
- g_return_if_reached();
+ case PROP_TEXT_DIRECTION: {
+ DbusmenuTextDirection indir = g_value_get_enum(value);
+ DbusmenuTextDirection olddir = priv->text_direction;
+
+ /* If being set to none we need to go back to default, otherwise
+ we'll set things the way that we've been told */
+ if (indir == DBUSMENU_TEXT_DIRECTION_NONE) {
+ default_text_direction(DBUSMENU_SERVER(obj));
+ } else {
+ priv->text_direction = indir;
+ }
+
+ /* If the value has changed we need to signal that on DBus */
+ if (priv->text_direction != olddir && priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("TextDirection"), g_variant_new_variant(g_variant_new_string(dbusmenu_text_direction_get_nick(priv->text_direction))));
+ g_variant_builder_add_value(&params, g_variant_new_array(NULL, &dict, 1));
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
+
break;
}
+ case PROP_STATUS: {
+ DbusmenuStatus instatus = g_value_get_enum(value);
+
+ /* If the value has changed we need to signal that on DBus */
+ if (priv->status != instatus && priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("Status"), g_variant_new_variant(g_variant_new_string(dbusmenu_status_get_nick(instatus))));
+ g_variant_builder_add_value(&params, g_variant_new_array(NULL, &dict, 1));
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
- return;
-}
-
-static void
-xmlarray_foreach_free (gpointer arrayentry, gpointer userdata)
-{
- if (arrayentry != NULL) {
- /* g_debug("Freeing pointer: %s", (gchar *)arrayentry); */
- g_free(arrayentry);
+ priv->status = instatus;
+ break;
+ }
+ default:
+ g_return_if_reached();
+ break;
}
return;
@@ -321,6 +576,12 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
case PROP_VERSION:
g_value_set_uint(value, DBUSMENU_VERSION_NUMBER);
break;
+ case PROP_TEXT_DIRECTION:
+ g_value_set_enum(value, priv->text_direction);
+ break;
+ case PROP_STATUS:
+ g_value_set_enum(value, priv->status);
+ break;
default:
g_return_if_reached();
break;
@@ -329,6 +590,192 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
return;
}
+/* Determines the default text direction */
+static void
+default_text_direction (DbusmenuServer * server)
+{
+ DbusmenuTextDirection dir = DBUSMENU_TEXT_DIRECTION_NONE;
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ const gchar * env = g_getenv("DBUSMENU_TEXT_DIRECTION");
+ if (env != NULL) {
+ if (g_strcmp0(env, "ltr") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ } else if (g_strcmp0(env, "rtl") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_RTL;
+ } else {
+ g_warning("Value of 'DBUSMENU_TEXT_DIRECTION' is '%s' which is not one of 'rtl' or 'ltr'", env);
+ }
+ }
+
+ if (dir == DBUSMENU_TEXT_DIRECTION_NONE) {
+ /* TRANSLATORS: This is the direction of the text and can
+ either be the value 'ltr' for left-to-right text (English)
+ or 'rtl' for right-to-left (Arabic). */
+ const gchar * default_dir = C_("default text direction", "ltr");
+
+ if (g_strcmp0(default_dir, "ltr") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ } else if (g_strcmp0(default_dir, "rtl") == 0) {
+ dir = DBUSMENU_TEXT_DIRECTION_RTL;
+ } else {
+ g_warning("Translation has an invalid value '%s' for default text direction. Defaulting to left-to-right.", default_dir);
+ dir = DBUSMENU_TEXT_DIRECTION_LTR;
+ }
+ }
+
+ /* Shouldn't happen, but incase future patches make a mistake
+ this'll catch them */
+ g_return_if_fail(dir != DBUSMENU_TEXT_DIRECTION_NONE);
+
+ priv->text_direction = dir;
+
+ return;
+}
+
+/* Register the object on the dbus bus */
+static void
+register_object (DbusmenuServer * server)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ /* Object info */
+ g_return_if_fail(priv->bus != NULL);
+ g_return_if_fail(priv->dbusobject != NULL);
+
+ /* Class info */
+ g_return_if_fail(dbusmenu_node_info != NULL);
+ g_return_if_fail(dbusmenu_interface_info != NULL);
+
+ /* We might block on this in the future, but it'd be nice if
+ we could change the object path. Thinking about it... */
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
+ priv->dbus_registration = 0;
+ }
+
+ GError * error = NULL;
+ priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
+ priv->dbusobject,
+ dbusmenu_interface_info,
+ &dbusmenu_interface_table,
+ server,
+ NULL,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to register object on bus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* If we've got it registered let's tell everyone about it */
+ g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "LayoutUpdated",
+ g_variant_new("(ui)", priv->layout_revision, 0),
+ NULL);
+ }
+
+ return;
+}
+
+/* Callback from asking GIO to get us the session bus */
+static void
+bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
+{
+ GError * error = NULL;
+
+ GDBusConnection * bus = g_bus_get_finish(result, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(user_data));
+ return;
+ }
+
+ /* Note: We're not using the user_data before we check for
+ the error so that in the cancelled case at destruction of
+ the object we don't end up with an invalid object. */
+
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+ priv->bus = bus;
+
+ register_object(DBUSMENU_SERVER(user_data));
+
+ g_object_unref(G_OBJECT(user_data));
+ return;
+}
+
+/* Function for the GDBus vtable to handle all method calls and dish
+ them out the appropriate functions */
+static void
+bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
+{
+ int i;
+ const gchar * interned_method = g_intern_string(method);
+
+ for (i = 0; i < METHOD_COUNT; i++) {
+ if (dbusmenu_method_table[i].interned_name == interned_method) {
+ if (dbusmenu_method_table[i].func != NULL) {
+ return dbusmenu_method_table[i].func(DBUSMENU_SERVER(user_data), params, invocation);
+ } else {
+ /* If we have a null function we're responding but nothing else. */
+ g_warning("Invalid function call for '%s' with parameters: %s", method, g_variant_print(params, TRUE));
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ return;
+ }
+ }
+ }
+
+ /* We're here because there's an error */
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NOT_IMPLEMENTED,
+ "Unable to find method '%s'",
+ method);
+ return;
+}
+
+/* For the GDBus vtable but we only have one property so it's pretty
+ simple. */
+static GVariant *
+bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+
+ /* None of these should happen */
+ g_return_val_if_fail(g_strcmp0(interface, DBUSMENU_INTERFACE) == 0, NULL);
+ g_return_val_if_fail(g_strcmp0(path, priv->dbusobject) == 0, NULL);
+
+ if (g_strcmp0(property, "Version") == 0) {
+ return g_variant_new_uint32(DBUSMENU_VERSION_NUMBER);
+ } else if (g_strcmp0(property, "TextDirection") == 0) {
+ return g_variant_new_string(dbusmenu_text_direction_get_nick(priv->text_direction));
+ } else if (g_strcmp0(property, "IconThemePath") == 0) {
+ GVariant * dirs = NULL;
+
+ if (priv->icon_dirs != NULL) {
+ dirs = g_variant_new_strv((const gchar * const *)priv->icon_dirs, -1);
+ } else {
+ dirs = g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0);
+ }
+
+ return dirs;
+ } else if (g_strcmp0(property, "Status") == 0) {
+ return g_variant_new_string(dbusmenu_status_get_nick(priv->status));
+ } else {
+ g_warning("Unknown property '%s'", property);
+ }
+
+ return NULL;
+}
+
/* Handle actually signalling in the idle loop. This way we collect all
the updates. */
static gboolean
@@ -338,6 +785,15 @@ layout_update_idle (gpointer user_data)
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
g_signal_emit(G_OBJECT(server), signals[LAYOUT_UPDATED], 0, priv->layout_revision, 0, TRUE);
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "LayoutUpdated",
+ g_variant_new("(ui)", priv->layout_revision, 0),
+ NULL);
+ }
priv->layout_idle = 0;
@@ -358,10 +814,280 @@ layout_update_signal (DbusmenuServer * server)
return;
}
+typedef struct _prop_idle_item_t prop_idle_item_t;
+struct _prop_idle_item_t {
+ DbusmenuMenuitem * mi;
+ GArray * array;
+};
+
+typedef struct _prop_idle_prop_t prop_idle_prop_t;
+struct _prop_idle_prop_t {
+ gchar * property;
+ GVariant * variant;
+};
+
+/* Takes appart our data structure so we don't leak any
+ memory or references. */
+static void
+prop_array_teardown (GArray * prop_array)
+{
+ int i, j;
+
+ for (i = 0; i < prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(prop_array, prop_idle_item_t, i);
+
+ for (j = 0; j < iitem->array->len; j++) {
+ prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j);
+
+ g_free(iprop->property);
+
+ if (iprop->variant != NULL) {
+ g_variant_unref(iprop->variant);
+ }
+ }
+
+ g_object_unref(G_OBJECT(iitem->mi));
+ g_array_free(iitem->array, TRUE);
+ }
+
+ g_array_free(prop_array, TRUE);
+
+ return;
+}
+
+/* Works in the idle to send a set of property updates so that they'll
+ all update in a single dbus message. */
+static gboolean
+menuitem_property_idle (gpointer user_data)
+{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(user_data);
+
+ /* Source will get removed as we return */
+ priv->property_idle = 0;
+
+ /* If there are no items, let's just not signal */
+ if (priv->prop_array == NULL) {
+ return FALSE;
+ }
+
+ int i, j;
+ GVariantBuilder itembuilder;
+ gboolean item_init = FALSE;
+
+ GVariantBuilder removeitembuilder;
+ gboolean removeitem_init = FALSE;
+
+ for (i = 0; i < priv->prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i);
+
+ /* if it's not exposed we're going to block it's properties
+ from getting into the dbus message */
+ if (dbusmenu_menuitem_exposed(iitem->mi) == FALSE) {
+ continue;
+ }
+
+ GVariantBuilder dictbuilder;
+ gboolean dictinit = FALSE;
+
+ GVariantBuilder removedictbuilder;
+ gboolean removedictinit = FALSE;
+
+ /* Go throught each item and see if it should go in the removal list
+ or the additive list. */
+ for (j = 0; j < iitem->array->len; j++) {
+ prop_idle_prop_t * iprop = &g_array_index(iitem->array, prop_idle_prop_t, j);
+
+ if (iprop->variant != NULL) {
+ if (!dictinit) {
+ g_variant_builder_init(&dictbuilder, G_VARIANT_TYPE_DICTIONARY);
+ dictinit = TRUE;
+ }
+
+ GVariant * entry = g_variant_new_dict_entry(g_variant_new_string(iprop->property),
+ g_variant_new_variant(iprop->variant));
+
+ g_variant_builder_add_value(&dictbuilder, entry);
+ } else {
+ if (!removedictinit) {
+ g_variant_builder_init(&removedictbuilder, G_VARIANT_TYPE_ARRAY);
+ removedictinit = TRUE;
+ }
+
+ g_variant_builder_add_value(&removedictbuilder, g_variant_new_string(iprop->property));
+ }
+ }
+
+ /* If we've got new values that are real values we need to add that
+ to the list of items to send the value of */
+ if (dictinit) {
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(dbusmenu_menuitem_get_id(iitem->mi)));
+ g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&dictbuilder));
+
+ if (!item_init) {
+ g_variant_builder_init(&itembuilder, G_VARIANT_TYPE_ARRAY);
+ item_init = TRUE;
+ }
+
+ g_variant_builder_add_value(&itembuilder, g_variant_builder_end(&tuplebuilder));
+ }
+
+ /* If we've got properties that have been removed then we need to add
+ them to the list of removed items */
+ if (removedictinit) {
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
+
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_int32(dbusmenu_menuitem_get_id(iitem->mi)));
+ g_variant_builder_add_value(&tuplebuilder, g_variant_builder_end(&removedictbuilder));
+
+ if (!removeitem_init) {
+ g_variant_builder_init(&removeitembuilder, G_VARIANT_TYPE_ARRAY);
+ removeitem_init = TRUE;
+ }
+
+ g_variant_builder_add_value(&removeitembuilder, g_variant_builder_end(&tuplebuilder));
+ }
+ }
+
+ /* these are going to be standard references in all code paths and must be unrefed */
+ GVariant * megadata[2];
+ gboolean gotsomething = FALSE;
+
+ if (item_init) {
+ megadata[0] = g_variant_builder_end(&itembuilder);
+ g_variant_ref_sink(megadata[0]);
+ gotsomething = TRUE;
+ } else {
+ GError * error = NULL;
+ megadata[0] = g_variant_parse(G_VARIANT_TYPE("a(ia{sv})"), "[ ]", NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to parse '[ ]' as a 'a(ia{sv})': %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (removeitem_init) {
+ megadata[1] = g_variant_builder_end(&removeitembuilder);
+ g_variant_ref_sink(megadata[1]);
+ gotsomething = TRUE;
+ } else {
+ GError * error = NULL;
+ megadata[1] = g_variant_parse(G_VARIANT_TYPE("a(ias)"), "[ ]", NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to parse '[ ]' as a 'a(ias)': %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (gotsomething && priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "ItemsPropertiesUpdated",
+ g_variant_new_tuple(megadata, 2),
+ NULL);
+ }
+
+ g_variant_unref(megadata[0]);
+ g_variant_unref(megadata[1]);
+
+ /* Clean everything up */
+ prop_array_teardown(priv->prop_array);
+ priv->prop_array = NULL;
+
+ return FALSE;
+}
+
static void
-menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GValue * value, DbusmenuServer * server)
+menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
{
- g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, value, TRUE);
+ int i;
+ gint item_id;
+
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ item_id = dbusmenu_menuitem_get_id(mi);
+
+ g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, item_id, property, variant, TRUE);
+
+ /* See if we have a property array, if not, we need to
+ build one of these suckers */
+ if (priv->prop_array == NULL) {
+ priv->prop_array = g_array_new(FALSE, FALSE, sizeof(prop_idle_item_t));
+ }
+
+ /* Look to see if we already have this item in the list
+ and use it if so */
+ prop_idle_item_t * item = NULL;
+ for (i = 0; i < priv->prop_array->len; i++) {
+ prop_idle_item_t * iitem = &g_array_index(priv->prop_array, prop_idle_item_t, i);
+ if (iitem->mi == mi) {
+ item = iitem;
+ break;
+ }
+ }
+
+ GArray * properties = NULL;
+ /* If not, we'll need to build ourselves one */
+ if (item == NULL) {
+ prop_idle_item_t myitem;
+ myitem.mi = mi;
+ g_object_ref(G_OBJECT(mi));
+ myitem.array = g_array_new(FALSE, FALSE, sizeof(prop_idle_prop_t));
+
+ g_array_append_val(priv->prop_array, myitem);
+ properties = myitem.array;
+ } else {
+ properties = item->array;
+ }
+
+ /* Check to see if this property is in the list */
+ prop_idle_prop_t * prop = NULL;
+ for (i = 0; i < properties->len; i++) {
+ prop_idle_prop_t * iprop = &g_array_index(properties, prop_idle_prop_t, i);
+ if (g_strcmp0(iprop->property, property) == 0) {
+ prop = iprop;
+ break;
+ }
+ }
+
+ /* If it's the default value we want to treat it like a clearing
+ of the value so that it doesn't get sent over dbus and waste
+ bandwidth */
+ if (dbusmenu_menuitem_property_is_default(mi, property)) {
+ variant = NULL;
+ }
+
+ /* If so, we need to swap the value */
+ if (prop != NULL) {
+ if (prop->variant != NULL) {
+ g_variant_unref(prop->variant);
+ }
+ prop->variant = variant;
+ } else {
+ /* else we need to add it */
+ prop_idle_prop_t myprop;
+ myprop.property = g_strdup(property);
+ myprop.variant = variant;
+
+ g_array_append_val(properties, myprop);
+ }
+ if (variant != NULL) {
+ g_variant_ref_sink(variant);
+ }
+
+ /* Check to see if the idle is already queued, and queue it
+ if not. */
+ if (priv->property_idle == 0) {
+ priv->property_idle = g_idle_add(menuitem_property_idle, server);
+ }
+
return;
}
@@ -412,7 +1138,20 @@ menuitem_child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint
static void
menuitem_shown (DbusmenuMenuitem * mi, guint timestamp, DbusmenuServer * server)
{
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
g_signal_emit(G_OBJECT(server), signals[ITEM_ACTIVATION], 0, dbusmenu_menuitem_get_id(mi), timestamp, TRUE);
+
+ if (priv->dbusobject != NULL && priv->bus != NULL) {
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ DBUSMENU_INTERFACE,
+ "ItemActivationRequested",
+ g_variant_new("(iu)", dbusmenu_menuitem_get_id(mi), timestamp),
+ NULL);
+ }
+
return;
}
@@ -452,215 +1191,321 @@ error_quark (void)
}
/* DBus interface */
-static gboolean
-_dbusmenu_server_get_layout (DbusmenuServer * server, gint parent, guint * revision, gchar ** layout, GError ** error)
+static void
+bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- *revision = priv->layout_revision;
- GPtrArray * xmlarray = g_ptr_array_new();
+ /* Input */
+ gint32 parent;
+ gint32 recurse;
+ const gchar ** props;
- if (parent == 0) {
- if (priv->root == NULL) {
- /* g_debug("Getting layout without root node!"); */
- g_ptr_array_add(xmlarray, g_strdup("<menu id=\"0\"/>"));
- } else {
- dbusmenu_menuitem_buildxml(priv->root, xmlarray);
+ g_variant_get(params, "(ii^a&s)", &parent, &recurse, &props);
+
+ /* Output */
+ guint revision = priv->layout_revision;
+ GVariant * items = NULL;
+
+ if (priv->root != NULL) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, parent);
+
+ if (mi != NULL) {
+ items = dbusmenu_menuitem_build_variant(mi, props, recurse);
}
- } else {
- DbusmenuMenuitem * item = dbusmenu_menuitem_find_id(priv->root, parent);
- if (item == 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",
- parent);
- }
- return FALSE;
+ }
+ g_free(props);
+
+ /* What happens if we don't have anything? */
+ if (items == NULL) {
+ if (parent == 0) {
+ /* We should always have a root, so we'll make up one for
+ right now. */
+ items = g_variant_parse(G_VARIANT_TYPE("(ia{sv}av)"), "(0, [], [])", NULL, NULL, NULL);
+ } else {
+ /* If we were looking for a specific ID that's an error that
+ we should send back, so let's do that. */
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ parent);
+ return;
}
- dbusmenu_menuitem_buildxml(item, xmlarray);
}
- g_ptr_array_add(xmlarray, NULL);
- /* build string */
- *layout = g_strjoinv("", (gchar **)xmlarray->pdata);
+ /* Build the final variant tuple */
+ GVariantBuilder tuplebuilder;
+ g_variant_builder_init(&tuplebuilder, G_VARIANT_TYPE_TUPLE);
- g_ptr_array_foreach(xmlarray, xmlarray_foreach_free, NULL);
- g_ptr_array_free(xmlarray, TRUE);
+ g_variant_builder_add_value(&tuplebuilder, g_variant_new_uint32(revision));
+ g_variant_builder_add_value(&tuplebuilder, items);
- return TRUE;
+ GVariant * retval = g_variant_builder_end(&tuplebuilder);
+ // g_debug("Sending layout type: %s", g_variant_get_type_string(retval));
+ g_dbus_method_invocation_return_value(invocation,
+ retval);
+ return;
}
-static gboolean
-_dbusmenu_server_get_property (DbusmenuServer * server, gint id, gchar * property, gchar ** value, GError ** error)
+/* Get a single property off of a single menuitem */
+static void
+bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ const gchar * property;
+
+ g_variant_get(params, "(i&s)", &id, &property);
+
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
"The ID supplied %d does not refer to a menu item we have",
id);
- }
- return FALSE;
+ return;
}
- const gchar * prop = dbusmenu_menuitem_property_get(mi, property);
- if (prop == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ GVariant * variant = dbusmenu_menuitem_property_get_variant(mi, property);
+ if (variant == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_PROPERTY_NAME,
"The property '%s' does not exist on menuitem with ID of %d",
property,
id);
- }
- return FALSE;
+ return;
}
- 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;
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", variant));
+ return;
}
-static gboolean
-_dbusmenu_server_get_properties (DbusmenuServer * server, gint id, gchar ** properties, GHashTable ** dict, GError ** error)
+/* Get some properties off of a single menuitem */
+static void
+bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
+
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
- if (error != NULL) {
- g_set_error(error,
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
INVALID_MENUITEM_ID,
"The ID supplied %d does not refer to a menu item we have",
id);
- }
- return FALSE;
+ return;
}
- *dict = dbusmenu_menuitem_properties_copy(mi);
+ GVariant * dict = dbusmenu_menuitem_properties_variant(mi, NULL);
+
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
- return TRUE;
+ return;
}
/* Handles getting a bunch of properties from a variety of menu items
to make one mega dbus message */
-static gboolean
-_dbusmenu_server_get_group_properties (DbusmenuServer * server, GArray * ids, gchar ** properties, GPtrArray ** values, GError ** error)
+static void
+bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
- /* Build an initial pointer array */
- *values = g_ptr_array_new();
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- /* Go through each ID to get that ID's properties */
- int idcnt;
- for (idcnt = 0; idcnt < ids->len; idcnt++) {
- GHashTable * idprops = NULL;
- GError * error = NULL;
- gint id = g_array_index(ids, int, idcnt);
+ if (priv->root == NULL) {
+ /* Allow a request for just id 0 when root is null. Return no properties.
+ So that a request always returns a valid structure no matter the
+ state of the structure in the server.
+ */
+ GVariant * idlist = g_variant_get_child_value(params, 0);
+ if (g_variant_n_children(idlist) == 1) {
- /* Get the properties for this ID the old fashioned way. */
- if (!_dbusmenu_server_get_properties(server, id, properties, &idprops, &error)) {
- g_warning("Error getting the properties from ID %d: %s", id, error->message);
- g_error_free(error);
- error = NULL;
- continue;
- }
+ GVariant *id_v = g_variant_get_child_value(idlist, 0);
+ gint32 id = g_variant_get_int32(id_v);
+ g_variant_unref(id_v);
- GValueArray * valarray = g_value_array_new(2);
+ if (id == 0) {
- _gvalue_array_append_int(valarray, id);
- _gvalue_array_append_hashtable(valarray, idprops);
+ GVariant * final = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
+ g_dbus_method_invocation_return_value(invocation, final);
+ g_variant_unref(final);
+ }
+ } else {
- g_ptr_array_add(*values, valarray);
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ }
+ g_variant_unref(idlist);
+ return;
}
- return TRUE;
-}
+ GVariantIter *ids;
+ g_variant_get(params, "(aias)", &ids, NULL);
+ /* TODO: implementation ignores propertyNames declared in XML */
-/* Allocate a value on the stack for the int and append
- it to the array. */
-static void
-_gvalue_array_append_int(GValueArray *array, gint i)
-{
- GValue value = {0};
+ GVariantBuilder builder;
+ gboolean builder_init = FALSE;
- g_value_init(&value, G_TYPE_INT);
- g_value_set_int(&value, i);
- g_value_array_append(array, &value);
- g_value_unset(&value);
-}
+ gint32 id;
+ while (g_variant_iter_loop(ids, "i", &id)) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+ if (mi == NULL) continue;
-/* Allocate a value on the stack for the hashtable and append
- it to the array. */
-static void
-_gvalue_array_append_hashtable(GValueArray *array, GHashTable * dict)
-{
- GValue value = {0};
+ if (!builder_init) {
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+ builder_init = TRUE;
+ }
+
+ GVariantBuilder wbuilder;
+ g_variant_builder_init(&wbuilder, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add(&wbuilder, "i", id);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
+
+ if (props == NULL) {
+ GError * error = NULL;
+ props = g_variant_parse(G_VARIANT_TYPE("a{sv}"), "{}", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to parse '{}' as a 'a{sv}': %s", error->message);
+ g_error_free(error);
+ props = NULL;
+ }
+ }
+
+ g_variant_builder_add_value(&wbuilder, props);
+ GVariant * mi_data = g_variant_builder_end(&wbuilder);
+
+ g_variant_builder_add_value(&builder, mi_data);
+ }
+ g_variant_iter_free(ids);
+
+ /* a standard reference that must be unrefed */
+ GVariant * ret = NULL;
+
+ if (builder_init) {
+ ret = g_variant_builder_end(&builder);
+ g_variant_ref_sink(ret);
+ } else {
+ GError * error = NULL;
+ ret = g_variant_parse(G_VARIANT_TYPE("a(ia{sv})"), "[]", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to parse '[]' as a 'a(ia{sv})': %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ GVariant * final = NULL;
+ if (ret != NULL) {
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&builder, ret);
+ g_variant_unref(ret);
+ final = g_variant_builder_end(&builder);
+ } else {
+ g_warning("Error building property list, final variant is NULL");
+ }
+
+ g_dbus_method_invocation_return_value(invocation, final);
- g_value_init(&value, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
- g_value_set_boxed(&value, dict);
- g_value_array_append(array, &value);
- g_value_unset(&value);
+ return;
}
+/* Turn a menuitem into an variant and attach it to the
+ VariantBuilder we passed in */
static void
serialize_menuitem(gpointer data, gpointer user_data)
{
DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(data);
- GPtrArray * output = (GPtrArray *)(user_data);
+ GVariantBuilder * builder = (GVariantBuilder *)(user_data);
+ GVariantBuilder tuple;
+
+ g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
gint id = dbusmenu_menuitem_get_id(mi);
- GHashTable * dict = dbusmenu_menuitem_properties_copy(mi);
-
- GValueArray * item = g_value_array_new(2);
- _gvalue_array_append_int(item, id);
- _gvalue_array_append_hashtable(item, dict);
+ g_variant_builder_add_value(&tuple, g_variant_new_int32(id));
- g_ptr_array_add(output, item);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
+ g_variant_builder_add_value(&tuple, props);
- g_hash_table_unref(dict);
+ g_variant_builder_add_value(builder, g_variant_builder_end(&tuple));
return;
}
-static gboolean
-_dbusmenu_server_get_children (DbusmenuServer * server, gint id, GPtrArray * properties, GPtrArray ** output, GError ** error)
+/* Gets the children and their properties of the ID that is
+ passed into the function */
+static void
+bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
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;
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ return;
}
- *output = g_ptr_array_new();
GList * children = dbusmenu_menuitem_get_children(mi);
- g_list_foreach(children, serialize_menuitem, *output);
+ GVariant * ret = NULL;
+
+ if (children != NULL) {
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
+ g_list_foreach(children, serialize_menuitem, &builder);
+
+ GVariant * end = g_variant_builder_end(&builder);
+ ret = g_variant_new_tuple(&end, 1);
+ } else {
+ GError * error = NULL;
+ ret = g_variant_parse(G_VARIANT_TYPE("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to parse '([(0, {})],)' as a '(a(ia{sv}))': %s", error->message);
+ g_error_free(error);
+ ret = NULL;
+ }
+ }
- return TRUE;
+ g_dbus_method_invocation_return_value(invocation, ret);
+ return;
}
/* Structure for holding the event data for the idle function
@@ -669,7 +1514,7 @@ typedef struct _idle_event_t idle_event_t;
struct _idle_event_t {
DbusmenuMenuitem * mi;
gchar * eventid;
- GValue data;
+ GVariant * variant;
guint timestamp;
};
@@ -680,66 +1525,96 @@ event_local_handler (gpointer user_data)
{
idle_event_t * data = (idle_event_t *)user_data;
- dbusmenu_menuitem_handle_event(data->mi, data->eventid, &data->data, data->timestamp);
+ dbusmenu_menuitem_handle_event(data->mi, data->eventid, data->variant, data->timestamp);
g_object_unref(data->mi);
g_free(data->eventid);
- g_value_unset(&data->data);
+ g_variant_unref(data->variant);
g_free(data);
return FALSE;
}
-/* Handles the even coming off of DBus */
-static gboolean
-_dbusmenu_server_event (DbusmenuServer * server, gint id, gchar * eventid, GValue * data, guint timestamp, GError ** error)
+/* Handles the events coming off of DBus */
+static void
+bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
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,
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
error_quark(),
- INVALID_MENUITEM_ID,
- "The ID supplied %d does not refer to a menu item we have",
- id);
- }
- return FALSE;
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
}
- idle_event_t * event_data = g_new0(idle_event_t, 1);
- event_data->mi = mi;
- g_object_ref(event_data->mi);
- event_data->eventid = g_strdup(eventid);
- event_data->timestamp = timestamp;
- g_value_init(&(event_data->data), G_VALUE_TYPE(data));
- g_value_copy(data, &(event_data->data));
+ gint32 id;
+ gchar *etype;
+ GVariant *data;
+ guint32 ts;
- g_timeout_add(0, event_local_handler, event_data);
- return TRUE;
+ g_variant_get(params, "(isvu)", &id, &etype, &data, &ts);
+
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
+
+ if (mi == NULL) {
+
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ g_free(etype);
+ g_variant_unref(data);
+
+ } else {
+
+ idle_event_t * event_data = g_new0(idle_event_t, 1);
+ event_data->mi = g_object_ref(mi);
+ event_data->eventid = etype;
+ event_data->timestamp = ts;
+ event_data->variant = data; /* give away our reference */
+
+ g_timeout_add(0, event_local_handler, event_data);
+ }
+
+ g_dbus_method_invocation_return_value(invocation, NULL);
+ return;
}
/* Recieve the About To Show function. Pass it to our menu item. */
-static gboolean
-_dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * need_update, GError ** error)
+static void
+bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->root == NULL) {
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ NO_VALID_LAYOUT,
+ "There currently isn't a layout in this server");
+ return;
+ }
+
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
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;
+ g_dbus_method_invocation_return_error(invocation,
+ error_quark(),
+ INVALID_MENUITEM_ID,
+ "The ID supplied %d does not refer to a menu item we have",
+ id);
+ return;
}
+ dbusmenu_menuitem_send_about_to_show(mi, NULL, NULL);
+
/* GTK+ does not support about-to-show concept for now */
- *need_update = FALSE;
- return TRUE;
+ g_dbus_method_invocation_return_value(invocation,
+ g_variant_new("(b)", FALSE));
+ return;
}
/* Public Interface */
@@ -750,7 +1625,7 @@ _dbusmenu_server_about_to_show (DbusmenuServer * server, gint id, gboolean * nee
Creates a new #DbusmenuServer object with a specific object
path on DBus. If @object is set to NULL the default object
- name of "/org/ayatana/dbusmenu" will be used.
+ name of "/com/canonical/dbusmenu" will be used.
Return value: A brand new #DbusmenuServer
*/
@@ -758,7 +1633,7 @@ DbusmenuServer *
dbusmenu_server_new (const gchar * object)
{
if (object == NULL) {
- object = "/org/ayatana/dbusmenu";
+ object = "/com/canonical/dbusmenu";
}
DbusmenuServer * self = g_object_new(DBUSMENU_TYPE_SERVER,
@@ -792,5 +1667,164 @@ dbusmenu_server_set_root (DbusmenuServer * self, DbusmenuMenuitem * root)
return;
}
+/**
+ dbusmenu_server_get_text_direction:
+ @server: The #DbusmenuServer object to get the text direction from
+
+ Returns the value of the text direction that is being exported
+ over DBus for this server. It should relate to the direction
+ of the labels and other text fields that are being exported by
+ this server.
+
+ Return value: Text direction exported for this server.
+*/
+DbusmenuTextDirection
+dbusmenu_server_get_text_direction (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), DBUSMENU_TEXT_DIRECTION_NONE);
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_TEXT_DIRECTION);
+ g_object_get_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_TEXT_DIRECTION, &val);
+
+ DbusmenuTextDirection retval = g_value_get_enum(&val);
+ g_value_unset(&val);
+
+ return retval;
+}
+
+/**
+ dbusmenu_server_set_text_direction:
+ @server: The #DbusmenuServer object to set the text direction on
+ @dir: Direction of the text
+
+ Sets the text direction that should be exported over DBus for
+ this server. If the value is set to #DBUSMENU_TEXT_DIRECTION_NONE
+ the default detection will be used for setting the value and
+ exported over DBus.
+*/
+void
+dbusmenu_server_set_text_direction (DbusmenuServer * server, DbusmenuTextDirection dir)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+ g_return_if_fail(dir == DBUSMENU_TEXT_DIRECTION_NONE || dir == DBUSMENU_TEXT_DIRECTION_LTR || dir == DBUSMENU_TEXT_DIRECTION_RTL);
+
+ GValue newval = {0};
+ g_value_init(&newval, DBUSMENU_TYPE_TEXT_DIRECTION);
+ g_value_set_enum(&newval, dir);
+ g_object_set_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_TEXT_DIRECTION, &newval);
+ g_value_unset(&newval);
+ return;
+}
+
+/**
+ dbusmenu_server_get_status:
+ @server: The #DbusmenuServer to get the status from
+
+ Gets the current statust hat the server is sending out over
+ DBus.
+
+ Return value: The current status the server is sending
+*/
+DbusmenuStatus
+dbusmenu_server_get_status (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), DBUSMENU_STATUS_NORMAL);
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_STATUS);
+ g_object_get_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_STATUS, &val);
+
+ DbusmenuStatus retval = g_value_get_enum(&val);
+ g_value_unset(&val);
+
+ return retval;
+}
+
+/**
+ dbusmenu_server_set_status:
+ @server: The #DbusmenuServer to set the status on
+ @status: Status value to set on the server
+
+ Changes the status of the server.
+*/
+void
+dbusmenu_server_set_status (DbusmenuServer * server, DbusmenuStatus status)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+
+ GValue val = {0};
+ g_value_init(&val, DBUSMENU_TYPE_STATUS);
+ g_value_set_enum(&val, status);
+ g_object_set_property(G_OBJECT(server), DBUSMENU_SERVER_PROP_STATUS, &val);
+ g_value_unset(&val);
+
+ return;
+}
+
+/**
+ * dbusmenu_server_get_icon_paths:
+ * @server: The #DbusmenuServer to get the icon paths from
+ *
+ * Gets the stored and exported icon paths from the server.
+ *
+ * Return value: (transfer none): A NULL-terminated list of icon paths with
+ * memory managed by the server. Duplicate if you want
+ * to keep them.
+ */
+const GStrv
+dbusmenu_server_get_icon_paths (DbusmenuServer * server)
+{
+ g_return_val_if_fail(DBUSMENU_IS_SERVER(server), NULL);
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ return priv->icon_dirs;
+}
+
+/**
+ dbusmenu_server_set_icon_paths:
+ @server: The #DbusmenuServer to set the icon paths on
+
+ Sets the icon paths for the server. This will replace previously
+ set icon theme paths.
+*/
+void
+dbusmenu_server_set_icon_paths (DbusmenuServer * server, GStrv icon_paths)
+{
+ g_return_if_fail(DBUSMENU_IS_SERVER(server));
+ DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+
+ if (priv->icon_dirs != NULL) {
+ g_strfreev(priv->icon_dirs);
+ priv->icon_dirs = NULL;
+ }
+
+ if (icon_paths != NULL) {
+ priv->icon_dirs = g_strdupv(icon_paths);
+ }
+ if (priv->bus != NULL && priv->dbusobject != NULL) {
+ GVariantBuilder params;
+ g_variant_builder_init(&params, G_VARIANT_TYPE_TUPLE);
+ g_variant_builder_add_value(&params, g_variant_new_string(DBUSMENU_INTERFACE));
+ GVariant * items = NULL;
+ if (priv->icon_dirs != NULL) {
+ GVariant * dict = g_variant_new_dict_entry(g_variant_new_string("IconThemePath"), g_variant_new_variant(g_variant_new_strv((const gchar * const *)priv->icon_dirs, -1)));
+ items = g_variant_new_array(NULL, &dict, 1);
+ } else {
+ items = g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0);
+ }
+ g_variant_builder_add_value(&params, items);
+ g_variant_builder_add_value(&params, g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0));
+ GVariant * vparams = g_variant_builder_end(&params);
+
+ g_dbus_connection_emit_signal(priv->bus,
+ NULL,
+ priv->dbusobject,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ vparams,
+ NULL);
+ }
+ return;
+}
diff --git a/libdbusmenu-glib/server.h b/libdbusmenu-glib/server.h
index a9bf213..5feb09b 100644
--- a/libdbusmenu-glib/server.h
+++ b/libdbusmenu-glib/server.h
@@ -33,6 +33,7 @@ License version 3 and version 2.1 along with this program. If not, see
#include <glib-object.h>
#include "menuitem.h"
+#include "types.h"
G_BEGIN_DECLS
@@ -43,15 +44,69 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_SERVER))
#define DBUSMENU_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_SERVER, DbusmenuServerClass))
+/**
+ * DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE:
+ *
+ * String to attach to signal #DbusmenuServer::item-property-updated
+ */
#define DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE "item-property-updated"
+/**
+ * DBUSMENU_SERVER_SIGNAL_ID_UPDATE:
+ *
+ * String to attach to signal #DbusmenuServer::item-updated
+ */
#define DBUSMENU_SERVER_SIGNAL_ID_UPDATE "item-updated"
+/**
+ * DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED:
+ *
+ * String to attach to signal #DbusmenuServer::layout-updated
+ */
#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED "layout-updated"
+/**
+ * DBUSMENU_SERVER_SIGNAL_ITEM_ACTIVATION:
+ *
+ * String to attach to signal #DbusmenuServer::item-activation-requested
+ */
#define DBUSMENU_SERVER_SIGNAL_ITEM_ACTIVATION "item-activation-requested"
+/**
+ * DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE:
+ *
+ * String to attach to signal #DbusmenuServer::layout-updated
+ */
#define DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED
+/**
+ * DBUSMENU_SERVER_PROP_DBUS_OBJECT:
+ *
+ * String to access property #DbusmenuServer:dbus-object
+ */
#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
+/**
+ * DBUSMENU_SERVER_PROP_ROOT_NODE:
+ *
+ * String to access property #DbusmenuServer:root-node
+ */
#define DBUSMENU_SERVER_PROP_ROOT_NODE "root-node"
+/**
+ * DBUSMENU_SERVER_PROP_VERSION:
+ *
+ * String to access property #DbusmenuServer:version
+ */
#define DBUSMENU_SERVER_PROP_VERSION "version"
+/**
+ * DBUSMENU_SERVER_PROP_TEXT_DIRECTION:
+ *
+ * String to access property #DbusmenuServer:text-direction
+ */
+#define DBUSMENU_SERVER_PROP_TEXT_DIRECTION "text-direction"
+/**
+ * DBUSMENU_SERVER_PROP_STATUS:
+ *
+ * String to access property #DbusmenuServer:status
+ */
+#define DBUSMENU_SERVER_PROP_STATUS "status"
+
+typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
/**
DbusmenuServerClass:
@@ -59,10 +114,13 @@ G_BEGIN_DECLS
@id_prop_update: Slot for #DbusmenuServer::id-prop-update.
@id_update: Slot for #DbusmenuServer::id-update.
@layout_updated: Slot for #DbusmenuServer::layout-update.
- @item_activation_requested: Slot for #DbusmenuServer::item-activation-requested.
- @dbusmenu_server_reserved1: Reserved for future use.
- @dbusmenu_server_reserved2: Reserved for future use.
- @dbusmenu_server_reserved3: Reserved for future use.
+ @item_activation: Slot for #DbusmenuServer::item-activation-requested.
+ @reserved1: Reserved for future use.
+ @reserved2: Reserved for future use.
+ @reserved3: Reserved for future use.
+ @reserved4: Reserved for future use.
+ @reserved5: Reserved for future use.
+ @reserved6: Reserved for future use.
The class implementing the virtual functions for #DbusmenuServer.
*/
@@ -76,15 +134,17 @@ struct _DbusmenuServerClass {
void (*layout_updated)(gint revision);
void (*item_activation)(gint id, guint timestamp);
- /* Reserved */
- void (*dbusmenu_server_reserved1)(void);
- void (*dbusmenu_server_reserved2)(void);
- void (*dbusmenu_server_reserved3)(void);
+ /*< Private >*/
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+ void (*reserved5) (void);
+ void (*reserved6) (void);
};
/**
DbusmenuServer:
- @parent: #GObject
A server which represents a sharing of a set of
#DbusmenuMenuitems across DBus to a #DbusmenuClient.
@@ -92,14 +152,27 @@ struct _DbusmenuServerClass {
typedef struct _DbusmenuServer DbusmenuServer;
struct _DbusmenuServer {
GObject parent;
+
+ /*< Private >*/
+ DbusmenuServerPrivate * priv;
};
-GType dbusmenu_server_get_type (void);
-DbusmenuServer * dbusmenu_server_new (const gchar * object);
-void dbusmenu_server_set_root (DbusmenuServer * server, DbusmenuMenuitem * root);
+GType dbusmenu_server_get_type (void);
+DbusmenuServer * dbusmenu_server_new (const gchar * object);
+void dbusmenu_server_set_root (DbusmenuServer * self,
+ DbusmenuMenuitem * root);
+DbusmenuTextDirection dbusmenu_server_get_text_direction (DbusmenuServer * server);
+void dbusmenu_server_set_text_direction (DbusmenuServer * server,
+ DbusmenuTextDirection dir);
+DbusmenuStatus dbusmenu_server_get_status (DbusmenuServer * server);
+void dbusmenu_server_set_status (DbusmenuServer * server,
+ DbusmenuStatus status);
+const GStrv dbusmenu_server_get_icon_paths (DbusmenuServer * server);
+void dbusmenu_server_set_icon_paths (DbusmenuServer * server,
+ GStrv icon_paths);
/**
- SECIONT:server
+ SECTION:server
@short_description: The server signals changed and
updates on a tree of #DbusmenuMenuitem objecs.
@stability: Unstable
diff --git a/libdbusmenu-glib/types.h b/libdbusmenu-glib/types.h
new file mode 100644
index 0000000..03ae801
--- /dev/null
+++ b/libdbusmenu-glib/types.h
@@ -0,0 +1,76 @@
+/*
+Types that are used in several objects.
+
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+*/
+
+#ifndef __DBUSMENU_TYPES_H__
+#define __DBUSMENU_TYPES_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ DbusmenuTextDirection:
+ @DBUSMENU_TEXT_DIRECTION_NONE: Unspecified text direction
+ @DBUSMENU_TEXT_DIRECTION_LTR: Left-to-right text direction
+ @DBUSMENU_TEXT_DIRECTION_RTL: Right-to-left text direction
+
+ The direction of text that the strings that this server
+ will be sending strings as.
+*/
+typedef enum { /*< prefix=DBUSMENU_TEXT_DIRECTION >*/
+ DBUSMENU_TEXT_DIRECTION_NONE, /*< nick=none >*/
+ DBUSMENU_TEXT_DIRECTION_LTR, /*< nick=ltr >*/
+ DBUSMENU_TEXT_DIRECTION_RTL /*< nick=rtl >*/
+} DbusmenuTextDirection;
+
+/**
+ DbusmenuStatus:
+ @DBUSMENU_STATUS_NORMAL: Everything is normal
+ @DBUSMENU_STATUS_NOTICE: The menus should be shown at a higher priority
+
+ Tracks how the menus should be presented to the user.
+*/
+typedef enum { /*< prefix=DBUSMENU_STATUS >*/
+ DBUSMENU_STATUS_NORMAL, /*< nick=normal >*/
+ DBUSMENU_STATUS_NOTICE /*< nick=notice >*/
+} DbusmenuStatus;
+
+/**
+ SECTION:types
+ @short_description: Types that are used by both client and
+ server.
+ @stability: Unstable
+ @include: libdbusmenu-glib/types.h
+
+ Enums that are used to describe states of the server across the
+ bus. They are sent over dbus using their nicks but then turned
+ back into enums by the client.
+*/
+G_END_DECLS
+
+#endif /* __DBUSMENU_TYPES_H__ */
+