aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore30
-rw-r--r--Makefile.am14
-rw-r--r--Makefile.am.enum45
-rw-r--r--configure.ac67
-rw-r--r--docs/Makefile.am8
-rw-r--r--docs/libdbusmenu-glib/reference/Makefile.am18
-rw-r--r--docs/libdbusmenu-glib/reference/libdbusmenu-glib-docs.sgml5
-rw-r--r--docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt96
-rw-r--r--docs/libdbusmenu-gtk/reference/Makefile.am13
-rw-r--r--docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-docs.sgml9
-rw-r--r--docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt58
-rw-r--r--libdbusmenu-glib/Makefile.am66
-rw-r--r--libdbusmenu-glib/client-marshal.list2
-rw-r--r--libdbusmenu-glib/client-menuitem.c7
-rw-r--r--libdbusmenu-glib/client-menuitem.h12
-rw-r--r--libdbusmenu-glib/client-private.h49
-rw-r--r--libdbusmenu-glib/client.c788
-rw-r--r--libdbusmenu-glib/client.h108
-rw-r--r--libdbusmenu-glib/dbus-menu.xml150
-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.list1
-rw-r--r--libdbusmenu-glib/menuitem-private.h6
-rw-r--r--libdbusmenu-glib/menuitem-proxy.c34
-rw-r--r--libdbusmenu-glib/menuitem-proxy.h14
-rw-r--r--libdbusmenu-glib/menuitem.c1102
-rw-r--r--libdbusmenu-glib/menuitem.h315
-rw-r--r--libdbusmenu-glib/server.c811
-rw-r--r--libdbusmenu-glib/server.h75
-rw-r--r--libdbusmenu-glib/types.h76
-rw-r--r--libdbusmenu-gtk/Makefile.am41
-rw-r--r--libdbusmenu-gtk/client.c521
-rw-r--r--libdbusmenu-gtk/client.h42
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk.h1
-rw-r--r--libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in2
-rw-r--r--libdbusmenu-gtk/genericmenuitem-enum-types.c.in116
-rw-r--r--libdbusmenu-gtk/genericmenuitem-enum-types.h.in65
-rw-r--r--libdbusmenu-gtk/genericmenuitem.c173
-rw-r--r--libdbusmenu-gtk/genericmenuitem.h71
-rw-r--r--libdbusmenu-gtk/menu.c89
-rw-r--r--libdbusmenu-gtk/menu.h33
-rw-r--r--libdbusmenu-gtk/menuitem.c163
-rw-r--r--libdbusmenu-gtk/menuitem.h15
-rw-r--r--libdbusmenu-gtk/parser.c854
-rw-r--r--libdbusmenu-gtk/parser.h15
-rw-r--r--libdbusmenu-gtk/serializablemenuitem.c288
-rw-r--r--libdbusmenu-gtk/serializablemenuitem.h115
-rw-r--r--po/POTFILES.in2
-rw-r--r--tests/Makefile.am85
-rw-r--r--tests/json-loader.c1
-rw-r--r--tests/test-glib-layout-client.c5
-rwxr-xr-xtests/test-glib-simple-items.py35
-rw-r--r--tests/test-gtk-label-client.c2
-rw-r--r--tests/test-gtk-label.json79
-rw-r--r--tests/test-gtk-objects.c4
-rw-r--r--tests/test-gtk-parser.c14
-rw-r--r--tests/test-gtk-shortcut-client.c2
-rwxr-xr-xtests/test-gtk-shortcut-client.py52
-rw-r--r--tests/test-gtk-shortcut-server.c4
-rw-r--r--tests/test-gtk-submenu-client.c2
-rw-r--r--tests/test-json-01.json2111
-rw-r--r--tools/dbusmenu-dumper.c6
-rw-r--r--tools/testapp/Makefile.am6
66 files changed, 6169 insertions, 3385 deletions
diff --git a/.bzrignore b/.bzrignore
index 39be2d5..0073e93 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -227,3 +227,33 @@ libdbusmenu-gtk/libdbusmenu_gtk_la-parser.lo
test-gtk-parser
test-gtk-parser-test
test-gtk-parser.xml
+libdbusmenu-glib/libdbusmenu_glib_la-defaults.lo
+enum-types.c
+enum-types.h
+libdbusmenu_glib_la-enum-types.lo
+stamp-enum-types
+libdbusmenu-0.3.100
+docs/libdbusmenu-glib/reference/gtkdoc-in-srcdir
+docs/libdbusmenu-glib/reference/html/api-index-deprecated.html
+docs/libdbusmenu-glib/reference/html/libdbusmenu-glib-Types.html
+docs/libdbusmenu-glib/reference/tmpl/types.sgml
+docs/libdbusmenu-gtk/reference/gtkdoc-in-srcdir
+docs/libdbusmenu-gtk/reference/html/annotation-glossary.html
+docs/libdbusmenu-gtk/reference/html/api-index-deprecated.html
+docs/libdbusmenu-gtk/reference/html/libdbusmenu-gtk-DbusmenuGtkSerializableMenuItem.html
+docs/libdbusmenu-gtk/reference/html/libdbusmenu-gtk-parser.html
+docs/libdbusmenu-gtk/reference/tmpl/parser.sgml
+docs/libdbusmenu-gtk/reference/tmpl/parser.sgml.bak
+docs/libdbusmenu-gtk/reference/tmpl/serializablemenuitem.sgml.bak
+libdbusmenu-glib/Dbusmenu-0.4.gir
+libdbusmenu-glib/Dbusmenu-0.4.typelib
+libdbusmenu-glib/Dbusmenu-0.4.vapi
+po/libdbusmenu.pot
+tests/test-gtk-shortcut-python
+tests/test-gtk-submenu
+tests/test-gtk-submenu-client
+tests/test-gtk-submenu-server
+tests/test-json-01.output.json
+genericmenuitem-enum-types.c
+genericmenuitem-enum-types.h
+libdbusmenu_gtk3_la-genericmenuitem-enum-types.lo
diff --git a/Makefile.am b/Makefile.am
index c2c0980..6b11b31 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,13 +1,23 @@
+ACLOCAL_AMFLAGS = -I m4
+
EXTRA_DIST = \
COPYING.2.1 \
COPYING-GPL3 \
m4/introspection.m4
+if WANT_TESTS
+TESTS_SUBDIR = tests
+endif
+
+if WANT_LIBDBUSMENUGTK
+LIBDBUSMENUGTK_SUBDIR = libdbusmenu-gtk
+endif
+
SUBDIRS = \
libdbusmenu-glib \
- libdbusmenu-gtk \
+ $(LIBDBUSMENUGTK_SUBDIR) \
tools \
- tests \
+ $(TESTS_SUBDIR) \
docs \
po
diff --git a/Makefile.am.enum b/Makefile.am.enum
new file mode 100644
index 0000000..0503de0
--- /dev/null
+++ b/Makefile.am.enum
@@ -0,0 +1,45 @@
+# Rules for generating enumeration types using glib-mkenums
+#
+# Define:
+# glib_enum_h = header template file
+# glib_enum_c = source template file
+# glib_enum_headers = list of headers to parse
+#
+# before including Makefile.am.enums. You will also need to have
+# the following targets already defined:
+#
+# CLEANFILES
+# DISTCLEANFILES
+# BUILT_SOURCES
+# EXTRA_DIST
+#
+# Author: Emmanuele Bassi <ebassi@linux.intel.com>
+
+enum_tmpl_h=$(glib_enum_h:.h=.h.in)
+enum_tmpl_c=$(glib_enum_c:.c=.c.in)
+
+CLEANFILES += stamp-enum-types
+DISTCLEANFILES += $(glib_enum_h) $(glib_enum_c)
+BUILT_SOURCES += $(glib_enum_h) $(glib_enum_c)
+EXTRA_DIST += $(enum_tmpl_h) $(enum_tmpl_c)
+
+stamp-enum-types: $(glib_enum_headers)
+ $(QUIET_GEN)mkdir -p `dirname $(builddir)/$(glib_enum_h)`
+ $(QUIET_GEN)$(GLIB_MKENUMS) \
+ --template $(srcdir)/$(enum_tmpl_h) \
+ $(glib_enum_headers) > xgen-eh \
+ && (cmp -s xgen-eh $(builddir)/$(glib_enum_h) || cp -f xgen-eh $(builddir)/$(glib_enum_h)) \
+ && rm -f xgen-eh \
+ && echo timestamp > $(@F)
+
+$(glib_enum_h): stamp-enum-types
+ @true
+
+$(glib_enum_c): $(glib_enum_h)
+ $(QUIET_GEN)mkdir -p `dirname $(builddir)/$(glib_enum_c)`
+ $(QUIET_GEN)$(GLIB_MKENUMS) \
+ --template $(srcdir)/$(enum_tmpl_c) \
+ $(glib_enum_headers) > xgen-ec \
+ && cp -f xgen-ec $(builddir)/$(glib_enum_c) \
+ && rm -f xgen-ec
+
diff --git a/configure.ac b/configure.ac
index 619cb17..895f9f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT(libdbusmenu, 0.3.94, ted@canonical.com)
+AC_INIT(libdbusmenu, 0.4.93, ted@canonical.com)
AC_COPYRIGHT([Copyright 2009,2010 Canonical])
AC_PREREQ(2.62)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(libdbusmenu, 0.3.94, [-Wno-portability])
+AM_INIT_AUTOMAKE(libdbusmenu, 0.4.93, [-Wno-portability])
AM_MAINTAINER_MODE
@@ -21,6 +21,8 @@ AC_CONFIG_MACRO_DIR([m4])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])
+
AC_ARG_ENABLE([deprecations],
[AS_HELP_STRING([--enable-deprecations],
[allow deprecated API usage @<:@default=yes@:>@])],
@@ -42,11 +44,9 @@ GNOME_DOC_INIT
###########################
GLIB_REQUIRED_VERSION=2.26
-XML_REQUIRED_VERSION=2.6
PKG_CHECK_MODULES(DBUSMENUGLIB, glib-2.0 >= $GLIB_REQUIRED_VERSION
- gio-2.0 >= $GLIB_REQUIRED_VERSION
- libxml-2.0 >= $XML_REQUIRED_VERSION)
+ gio-2.0 >= $GLIB_REQUIRED_VERSION)
AC_SUBST(DBUSMENUGLIB_CFLAGS)
AC_SUBST(DBUSMENUGLIB_LIBS)
@@ -58,29 +58,37 @@ AC_SUBST(DBUSMENUGLIB_LIBS)
GTK_REQUIRED_VERSION=2.16
GTK3_REQUIRED_VERSION=2.91
+AC_ARG_ENABLE([gtk],
+ AC_HELP_STRING([--disable-gtk], [Disable libdbusmenu-gtk library]),
+ [enable_gtk=$enableval], [enable_gtk=auto])
+AM_CONDITIONAL([WANT_LIBDBUSMENUGTK], [test "x$enable_gtk" != "xno"])
+
AC_ARG_WITH([gtk],
[AS_HELP_STRING([--with-gtk],
- [Which version of gtk to use @<:@default=2@:>@])],
+ [Which version of gtk to use @<:@default=3@:>@])],
[],
- [with_gtk=2])
+ [with_gtk=3])
+AM_CONDITIONAL([USE_GTK3], [test "x$with_gtk" = "x3"])
+
+AS_IF([test "x$enable_gtk" != "xno"],[
AS_IF([test "x$with_gtk" = x3],
[PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-3.0 >= $GTK3_REQUIRED_VERSION
- glib-2.0 >= $GLIB_REQUIRED_VERSION
- libxml-2.0 >= $XML_REQUIRED_VERSION)
- AC_SUBST(DBUSMENUGTK_CFLAGS)
- AC_SUBST(DBUSMENUGTK_LIBS)
+ glib-2.0 >= $GLIB_REQUIRED_VERSION,
+ [have_gtk=yes]
+)
AC_DEFINE(HAVE_GTK3, 1, [whether gtk3 is available])
],
[test "x$with_gtk" = x2],
[PKG_CHECK_MODULES(DBUSMENUGTK, gtk+-2.0 >= $GTK_REQUIRED_VERSION
- glib-2.0 >= $GLIB_REQUIRED_VERSION
- libxml-2.0 >= $XML_REQUIRED_VERSION)
- AC_SUBST(DBUSMENUGTK_CFLAGS)
- AC_SUBST(DBUSMENUGTK_LIBS)
+ glib-2.0 >= $GLIB_REQUIRED_VERSION,
+ [have_gtk=yes]
+)
],
[AC_MSG_FAILURE([Value for --with-gtk was neither 2 nor 3])]
)
-AM_CONDITIONAL(USE_GTK3, [test "x$with_gtk" = x3])
+])
+AC_SUBST(DBUSMENUGTK_CFLAGS)
+AC_SUBST(DBUSMENUGTK_LIBS)
###########################
# Dependencies - dumper
@@ -98,11 +106,20 @@ AC_SUBST(DBUSMENUDUMPER_LIBS)
# Dependencies - Testing
###########################
-JSON_GLIB_REQUIRED_VERSION=0.6.0
+JSON_GLIB_REQUIRED_VERSION=0.13.4
GIO_UNIX_REQUIRED_VERSION=2.24
+AC_ARG_ENABLE([tests],
+ AC_HELP_STRING([--disable-tests], [Disable tests]),
+ [enable_tests=$enableval], [enable_tests=auto])
+AM_CONDITIONAL([WANT_TESTS], [test "x$enable_tests" != "xno"])
+
+AS_IF([test "x$enable_tests" != "xno"],[
PKG_CHECK_MODULES(DBUSMENUTESTS, json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION
- gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION)
+ gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION,
+ [have_tests=yes]
+)
+])
AC_SUBST(DBUSMENUTESTS_CFLAGS)
AC_SUBST(DBUSMENUTESTS_LIBS)
@@ -135,8 +152,8 @@ AC_PATH_PROG([XSLT_PROC], [xsltproc])
# Lib versioning
###########################
-LIBDBUSMENU_CURRENT=3
-LIBDBUSMENU_REVISION=2
+LIBDBUSMENU_CURRENT=4
+LIBDBUSMENU_REVISION=3
LIBDBUSMENU_AGE=0
AC_SUBST(LIBDBUSMENU_CURRENT)
@@ -201,3 +218,13 @@ libdbusmenu Configuration:
GTK+ Version: $with_gtk
])
+AS_IF([test "x$have_tests" = "xyes"],
+ AC_MSG_NOTICE([ Tests: yes]),
+ AC_MSG_NOTICE([ Tests: no])
+)
+
+AS_IF([test "x$enable_gtk" = "xno"],
+ AC_MSG_NOTICE([ Gtk: no]),
+ AC_MSG_NOTICE([ Gtk: yes (gtk$with_gtk)])
+)
+
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 87ffe5f..23a4c86 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -1 +1,7 @@
-SUBDIRS = libdbusmenu-glib libdbusmenu-gtk
+if WANT_LIBDBUSMENUGTK
+LIBDBUSMENUGTK_SUBDIR = libdbusmenu-gtk
+endif
+
+SUBDIRS = \
+ libdbusmenu-glib \
+ $(LIBDBUSMENUGTK_SUBDIR)
diff --git a/docs/libdbusmenu-glib/reference/Makefile.am b/docs/libdbusmenu-glib/reference/Makefile.am
index e8a3610..2af5ec3 100644
--- a/docs/libdbusmenu-glib/reference/Makefile.am
+++ b/docs/libdbusmenu-glib/reference/Makefile.am
@@ -48,11 +48,13 @@ CFILE_GLOB=$(top_srcdir)/libdbusmenu-glib/*.c
# Header files to ignore when scanning.
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
IGNORE_HFILES= \
+ dbus-menu-clean.xml.h \
+ client-menuitem.h \
+ client-private.h \
+ defaults.h \
menuitem-marshal.h \
server-marshal.h \
- menuitem-private.h \
- dbusmenu-client.h \
- dbusmenu-server.h
+ menuitem-private.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
@@ -88,5 +90,13 @@ EXTRA_DIST += version.xml.in
#DISTCLEANFILES +=
# Comment this out if you want your docs-status tested during 'make check'
-#TESTS = $(GTKDOC_CHECK)
+TESTS = gtkdoc-in-srcdir
+
+gtkdoc-in-srcdir: Makefile.am
+ @echo "#!/bin/sh" > $@
+ @echo "cd \"$(srcdir)\"" >> $@
+ @echo "$(GTKDOC_CHECK)" >> $@
+ @chmod +x $@
+
+DISTCLEANFILES = gtkdoc-in-srcdir
diff --git a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-docs.sgml b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-docs.sgml
index ecc25a3..53a0ceb 100644
--- a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-docs.sgml
+++ b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-docs.sgml
@@ -18,6 +18,7 @@
<xi:include href="xml/client-menuitem.xml"/>
<xi:include href="xml/dbusmenu-client.xml"/>
<xi:include href="xml/dbusmenu-server.xml"/>
+ <xi:include href="xml/types.xml"/>
</chapter>
<chapter id="object-tree">
@@ -28,6 +29,10 @@
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-deprecated">
+ <title>Deprecated API Index</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
</book>
diff --git a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
index 0186049..d95ec4c 100644
--- a/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
+++ b/docs/libdbusmenu-glib/reference/libdbusmenu-glib-sections.txt
@@ -4,8 +4,13 @@
DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED
DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED
DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM
+DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT
+DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE
+DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED
DBUSMENU_CLIENT_PROP_DBUS_NAME
DBUSMENU_CLIENT_PROP_DBUS_OBJECT
+DBUSMENU_CLIENT_PROP_STATUS
+DBUSMENU_CLIENT_PROP_TEXT_DIRECTION
DBUSMENU_CLIENT_TYPES_DEFAULT
DBUSMENU_CLIENT_TYPES_SEPARATOR
DBUSMENU_CLIENT_TYPES_IMAGE
@@ -13,18 +18,22 @@ DbusmenuClient
DbusmenuClientClass
DbusmenuClientTypeHandler
dbusmenu_client_new
+dbusmenu_client_get_icon_paths
dbusmenu_client_get_root
+dbusmenu_client_get_status
+dbusmenu_client_get_text_direction
dbusmenu_client_add_type_handler
-dbusmenu_client_send_event
-dbusmenu_client_send_about_to_show
+dbusmenu_client_add_type_handler_full
<SUBSECTION Standard>
DBUSMENU_CLIENT
DBUSMENU_IS_CLIENT
DBUSMENU_TYPE_CLIENT
-dbusmenu_client_get_type
DBUSMENU_CLIENT_CLASS
DBUSMENU_IS_CLIENT_CLASS
DBUSMENU_CLIENT_GET_CLASS
+<SUBSECTION Private>
+DbusmenuClientPrivate
+dbusmenu_client_get_type
</SECTION>
<SECTION>
@@ -35,8 +44,11 @@ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED
DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED
DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED
DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED
+DBUSMENU_MENUITEM_SIGNAL_EVENT
DBUSMENU_MENUITEM_SIGNAL_REALIZED
DBUSMENU_MENUITEM_SIGNAL_REALIZED_ID
+DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW
+DBUSMENU_MENUITEM_SIGNAL_SHOW_TO_USER
DBUSMENU_MENUITEM_PROP_TYPE
DBUSMENU_MENUITEM_PROP_VISIBLE
DBUSMENU_MENUITEM_PROP_ENABLED
@@ -46,6 +58,8 @@ DBUSMENU_MENUITEM_PROP_ICON_DATA
DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE
DBUSMENU_MENUITEM_PROP_TOGGLE_STATE
DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY
+DBUSMENU_MENUITEM_PROP_SHORTCUT
+DBUSMENU_MENUITEM_PROP_DISPOSITION
DBUSMENU_MENUITEM_TOGGLE_CHECK
DBUSMENU_MENUITEM_TOGGLE_RADIO
DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED
@@ -53,9 +67,20 @@ DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED
DBUSMENU_MENUITEM_TOGGLE_STATE_UNKNOWN
DBUSMENU_MENUITEM_ICON_NAME_BLANK
DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU
+DBUSMENU_MENUITEM_SHORTCUT_ALT
+DBUSMENU_MENUITEM_SHORTCUT_CONTROL
+DBUSMENU_MENUITEM_SHORTCUT_SHIFT
+DBUSMENU_MENUITEM_SHORTCUT_SUPER
+DBUSMENU_MENUITEM_DISPOSITION_NORMAL
+DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE
+DBUSMENU_MENUITEM_DISPOSITION_WARNING
+DBUSMENU_MENUITEM_DISPOSITION_ALERT
+DBUSMENU_MENUITEM_EVENT_ACTIVATED
+DBUSMENU_MENUITEM_EVENT_CLOSED
+DBUSMENU_MENUITEM_EVENT_OPENED
DbusmenuMenuitem
dbusmenu_menuitem_about_to_show_cb
-dbusmenu_menuitem_buildxml_slot_t
+dbusmenu_menuitem_buildvariant_slot_t
DbusmenuMenuitemClass
dbusmenu_menuitem_new
dbusmenu_menuitem_new_with_id
@@ -72,13 +97,13 @@ dbusmenu_menuitem_child_reorder
dbusmenu_menuitem_child_find
dbusmenu_menuitem_find_id
dbusmenu_menuitem_property_set
-dbusmenu_menuitem_property_set_value
dbusmenu_menuitem_property_set_bool
dbusmenu_menuitem_property_set_int
+dbusmenu_menuitem_property_set_variant
dbusmenu_menuitem_property_get
-dbusmenu_menuitem_property_get_value
dbusmenu_menuitem_property_get_bool
dbusmenu_menuitem_property_get_int
+dbusmenu_menuitem_property_get_variant
dbusmenu_menuitem_property_exist
dbusmenu_menuitem_properties_list
dbusmenu_menuitem_properties_copy
@@ -88,14 +113,20 @@ dbusmenu_menuitem_get_root
dbusmenu_menuitem_foreach
dbusmenu_menuitem_handle_event
dbusmenu_menuitem_send_about_to_show
+dbusmenu_menuitem_show_to_user
+dbusmenu_menuitem_get_parent
+dbusmenu_menuitem_set_parent
+dbusmenu_menuitem_unparent
<SUBSECTION Standard>
DBUSMENU_MENUITEM
DBUSMENU_IS_MENUITEM
DBUSMENU_TYPE_MENUITEM
-dbusmenu_menuitem_get_type
DBUSMENU_MENUITEM_CLASS
DBUSMENU_IS_MENUITEM_CLASS
DBUSMENU_MENUITEM_GET_CLASS
+<SUBSECTION Private>
+DbusmenuMenuitemPrivate
+dbusmenu_menuitem_get_type
</SECTION>
<SECTION>
@@ -105,37 +136,32 @@ DBUSMENU_SERVER_SIGNAL_ID_PROP_UPDATE
DBUSMENU_SERVER_SIGNAL_ID_UPDATE
DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATED
DBUSMENU_SERVER_SIGNAL_LAYOUT_UPDATE
+DBUSMENU_SERVER_SIGNAL_ITEM_ACTIVATION
DBUSMENU_SERVER_PROP_DBUS_OBJECT
DBUSMENU_SERVER_PROP_ROOT_NODE
+DBUSMENU_SERVER_PROP_STATUS
+DBUSMENU_SERVER_PROP_TEXT_DIRECTION
DBUSMENU_SERVER_PROP_VERSION
DbusmenuServer
DbusmenuServerClass
dbusmenu_server_new
+dbusmenu_server_get_status
+dbusmenu_server_get_text_direction
dbusmenu_server_set_root
+dbusmenu_server_set_status
+dbusmenu_server_set_text_direction
<SUBSECTION Standard>
DBUSMENU_SERVER
DBUSMENU_IS_SERVER
DBUSMENU_TYPE_SERVER
-dbusmenu_server_get_type
DBUSMENU_SERVER_CLASS
DBUSMENU_IS_SERVER_CLASS
DBUSMENU_SERVER_GET_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>client-menuitem</FILE>
-<TITLE>DbusmenuClientMenuitem</TITLE>
-DBUSMENU_CLIENT_MENUITEM_TYPE
-DbusmenuClientMenuitem
-DbusmenuClientMenuitemClass
-dbusmenu_client_menuitem_new
-<SUBSECTION Standard>
-DBUSMENU_CLIENT_MENUITEM
-DBUSMENU_IS_CLIENT_MENUITEM
-dbusmenu_client_menuitem_get_type
-DBUSMENU_CLIENT_MENUITEM_CLASS
-DBUSMENU_IS_CLIENT_MENUITEM_CLASS
-DBUSMENU_CLIENT_MENUITEM_GET_CLASS
+<SUBSECTION Private>
+DbusmenuServerPrivate
+dbusmenu_server_get_type
+dbusmenu_server_get_icon_paths
+dbusmenu_server_set_icon_paths
</SECTION>
<SECTION>
@@ -149,9 +175,29 @@ dbusmenu_menuitem_proxy_get_wrapped
DBUSMENU_MENUITEM_PROXY
DBUSMENU_IS_MENUITEM_PROXY
DBUSMENU_TYPE_MENUITEM_PROXY
-dbusmenu_menuitem_proxy_get_type
DBUSMENU_MENUITEM_PROXY_CLASS
DBUSMENU_IS_MENUITEM_PROXY_CLASS
DBUSMENU_MENUITEM_PROXY_GET_CLASS
+<SUBSECTION Private>
+DbusmenuMenuitemProxyPrivate
+dbusmenu_menuitem_proxy_get_type
+</SECTION>
+
+<SECTION>
+<FILE>types</FILE>
+<TITLE>Types</TITLE>
+DBUSMENU_TYPE_STATUS
+DBUSMENU_TYPE_TEXT_DIRECTION
+DbusmenuStatus
+DbusmenuTextDirection
+dbusmenu_status_get_nick
+dbusmenu_status_get_type
+dbusmenu_status_get_value_from_nick
+dbusmenu_text_direction_get_nick
+dbusmenu_text_direction_get_type
+dbusmenu_text_direction_get_value_from_nick
+<SUBSECTION Private>
+DbusmenuServerPrivate
+dbusmenu_server_get_type
</SECTION>
diff --git a/docs/libdbusmenu-gtk/reference/Makefile.am b/docs/libdbusmenu-gtk/reference/Makefile.am
index 191d68e..62f80ba 100644
--- a/docs/libdbusmenu-gtk/reference/Makefile.am
+++ b/docs/libdbusmenu-gtk/reference/Makefile.am
@@ -16,7 +16,7 @@ AUTOMAKE_OPTIONS = 1.6
# of using the various options.
# The name of the module, e.g. 'glib'.
-DOC_MODULE=libdbusmenu-gtk$(VER)
+DOC_MODULE=libdbusmenu-gtk
# The top-level SGML file. You can change this if you want to.
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
@@ -54,7 +54,7 @@ CFILE_GLOB=$(top_srcdir)/libdbusmenu-gtk/*.c
# Header files to ignore when scanning.
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
-IGNORE_HFILES=genericmenuitem.h
+IGNORE_HFILES=genericmenuitem.h genericmenuitem-enum-types.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
@@ -90,5 +90,12 @@ EXTRA_DIST += version.xml.in
#DISTCLEANFILES +=
# Comment this out if you want your docs-status tested during 'make check'
-#TESTS = $(GTKDOC_CHECK)
+TESTS = gtkdoc-in-srcdir
+gtkdoc-in-srcdir: Makefile.am
+ @echo "#!/bin/sh" > $@
+ @echo "cd \"$(srcdir)\"" >> $@
+ @echo "$(GTKDOC_CHECK)" >> $@
+ @chmod +x $@
+
+DISTCLEANFILES = gtkdoc-in-srcdir
diff --git a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-docs.sgml b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-docs.sgml
index ec6b82f..e9020de 100644
--- a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-docs.sgml
+++ b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-docs.sgml
@@ -11,9 +11,10 @@
<chapter>
<title>API</title>
- <xi:include href="xml/menu.xml"/>
- <xi:include href="xml/client.xml"/>
+ <xi:include href="xml/menu.xml"/>
<xi:include href="xml/menuitem.xml"/>
+ <xi:include href="xml/client.xml"/>
+ <xi:include href="xml/parser.xml"/>
<xi:include href="xml/serializablemenuitem.xml"/>
</chapter>
@@ -25,6 +26,10 @@
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-deprecated">
+ <title>Deprecated API Index</title>
+ <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+ </index>
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
</book>
diff --git a/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt
new file mode 100644
index 0000000..bc1f0ba
--- /dev/null
+++ b/docs/libdbusmenu-gtk/reference/libdbusmenu-gtk-sections.txt
@@ -0,0 +1,58 @@
+<SECTION>
+<FILE>client</FILE>
+<TITLE>DbusmenuGtkClient</TITLE>
+DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED
+DbusmenuGtkClient
+DbusmenuGtkClientClass
+dbusmenu_gtkclient_new
+dbusmenu_gtkclient_menuitem_get
+dbusmenu_gtkclient_menuitem_get_submenu
+dbusmenu_gtkclient_set_accel_group
+dbusmenu_gtkclient_get_accel_group
+dbusmenu_gtkclient_newitem_base
+<SUBSECTION Standard>
+DBUSMENU_GTKCLIENT
+DBUSMENU_IS_GTKCLIENT
+DBUSMENU_GTKCLIENT_CLASS
+DBUSMENU_IS_GTKCLIENT_CLASS
+DBUSMENU_GTKCLIENT_GET_CLASS
+<SUBSECTION Private>
+DBUSMENU_GTKCLIENT_TYPE
+dbusmenu_gtkclient_get_type
+DbusmenuGtkClientPrivate
+</SECTION>
+
+<SECTION>
+<FILE>menu</FILE>
+<TITLE>DbusmenuGtkMenu</TITLE>
+DbusmenuGtkMenuClass
+dbusmenu_gtkmenu_new
+dbusmenu_gtkmenu_get_client
+<SUBSECTION Standard>
+DBUSMENU_GTKMENU
+DBUSMENU_IS_GTKMENU
+DBUSMENU_GTKMENU_CLASS
+DBUSMENU_IS_GTKMENU_CLASS
+DBUSMENU_GTKMENU_GET_CLASS
+<SUBSECTION Private>
+dbusmenu_gtkmenu_get_type
+DBUSMENU_GTKMENU_TYPE
+DbusmenuGtkMenuPrivate
+</SECTION>
+
+<SECTION>
+<FILE>menuitem</FILE>
+dbusmenu_menuitem_property_set_image
+dbusmenu_menuitem_property_get_image
+dbusmenu_menuitem_property_set_shortcut
+dbusmenu_menuitem_property_set_shortcut_string
+dbusmenu_menuitem_property_set_shortcut_menuitem
+dbusmenu_menuitem_property_get_shortcut
+</SECTION>
+
+<SECTION>
+<FILE>parser</FILE>
+dbusmenu_gtk_parse_menu_structure
+dbusmenu_gtk_parse_get_cached_item
+</SECTION>
+
diff --git a/libdbusmenu-glib/Makefile.am b/libdbusmenu-glib/Makefile.am
index a139f7c..5b04415 100644
--- a/libdbusmenu-glib/Makefile.am
+++ b/libdbusmenu-glib/Makefile.am
@@ -1,6 +1,7 @@
-CLEANFILES =
-
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
EXTRA_DIST = \
clean-namespaces.xslt \
dbusmenu-glib-0.4.pc.in \
@@ -9,21 +10,33 @@ EXTRA_DIST = \
menuitem-marshal.list \
server-marshal.list
+include $(top_srcdir)/Makefile.am.enum
+
lib_LTLIBRARIES = \
libdbusmenu-glib.la
libdbusmenu_glibincludedir=$(includedir)/libdbusmenu-0.4/libdbusmenu-glib/
-libdbusmenu_glibinclude_HEADERS = \
- dbusmenu-glib.h \
+
+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 = \
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 \
@@ -39,6 +52,7 @@ libdbusmenu_glib_la_SOURCES = \
client-marshal.c \
client-menuitem.h \
client-menuitem.c \
+ client-private.h \
client.h \
client.c
@@ -56,6 +70,14 @@ libdbusmenu_glib_la_LIBADD = \
pkgconfig_DATA = dbusmenu-glib-0.4.pc
pkgconfigdir = $(libdir)/pkgconfig
+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 $@))));" > $@
@@ -69,7 +91,7 @@ dbus-menu-clean.xml: dbus-menu.xml
CLEANFILES += dbus-menu-clean.xml
-BUILT_SOURCES = \
+BUILT_SOURCES += \
dbus-menu-clean.xml.c \
dbus-menu-clean.xml.h \
client-marshal.h \
@@ -122,34 +144,36 @@ if INTROSPECTION_TEN
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
--warn-all \
--add-include-path=$(srcdir) \
- $(addprefix --c-include=libdbusmenu-glib/, $(introspection_sources)) \
+ $(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/, $(introspection_sources))
+ $(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.4.gir: libdbusmenu-glib.la
-Dbusmenu_Glib_0_4_gir_INCLUDES = \
+Dbusmenu-0.4.gir: libdbusmenu-glib.la
+Dbusmenu_0_4_gir_INCLUDES = \
GObject-2.0
-Dbusmenu_Glib_0_4_gir_CFLAGS = $(DBUSMENUGLIB_CFLAGS) -I$(top_srcdir)
-Dbusmenu_Glib_0_4_gir_LIBS = libdbusmenu-glib.la
-Dbusmenu_Glib_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources))
-Dbusmenu_Glib_0_4_gir_NAMESPACE = Dbusmenu
-Dbusmenu_Glib_0_4_gir_VERSION = Glib-0.4
-Dbusmenu_Glib_0_4_gir_EXPORT_PACKAGES = dbusmenu-glib-0.4
-Dbusmenu_Glib_0_4_gir_SCANNER_FLAGS = $(INTROSPECTION_SCANNER_ARGS)
+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.4.gir
+INTROSPECTION_GIRS += Dbusmenu-0.4.gir
girdir = $(datadir)/gir-1.0
gir_DATA = $(INTROSPECTION_GIRS)
@@ -168,10 +192,10 @@ endif
if HAVE_INTROSPECTION
vapidir = $(datadir)/vala/vapi
-vapi_DATA = Dbusmenu-Glib-0.4.vapi
+vapi_DATA = Dbusmenu-0.4.vapi
-Dbusmenu-Glib-0.4.vapi: Dbusmenu-Glib-0.4.gir
- $(VALA_API_GEN) --library=Dbusmenu-Glib-0.4 $<
+Dbusmenu-0.4.vapi: Dbusmenu-0.4.gir
+ $(VALA_API_GEN) --library=Dbusmenu-0.4 $<
CLEANFILES += $(vapi_DATA)
diff --git a/libdbusmenu-glib/client-marshal.list b/libdbusmenu-glib/client-marshal.list
index 866dfa8..980c5c3 100644
--- a/libdbusmenu-glib/client-marshal.list
+++ b/libdbusmenu-glib/client-marshal.list
@@ -1,2 +1,4 @@
VOID: OBJECT, UINT
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 0f14b85..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,7 +40,7 @@ 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);
@@ -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;
@@ -105,7 +106,7 @@ static void
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, variant, timestamp);
+ dbusmenu_client_send_event(priv->client, dbusmenu_menuitem_get_id(mi), name, variant, timestamp, mi);
return;
}
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 b196c9f..588c940 100644
--- a/libdbusmenu-glib/client.c
+++ b/libdbusmenu-glib/client.c
@@ -32,16 +32,15 @@ License version 3 and version 2.1 along with this program. If not, see
#include <gio/gio.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
#include "client.h"
+#include "client-private.h"
#include "menuitem.h"
#include "menuitem-private.h"
#include "client-menuitem.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 */
@@ -51,7 +50,9 @@ License version 3 and version 2.1 along with this program. If not, see
enum {
PROP_0,
PROP_DBUSOBJECT,
- PROP_DBUSNAME
+ PROP_DBUSNAME,
+ PROP_STATUS,
+ PROP_TEXT_DIRECTION
};
/* Signals */
@@ -61,6 +62,7 @@ enum {
NEW_MENUITEM,
ITEM_ACTIVATE,
EVENT_RESULT,
+ ICON_THEME_DIRS,
LAST_SIGNAL
};
@@ -82,6 +84,7 @@ struct _DbusmenuClientPrivate
GCancellable * menuproxy_cancel;
GCancellable * layoutcall;
+ GVariant * layout_props;
gint current_revision;
gint my_revision;
@@ -93,6 +96,10 @@ struct _DbusmenuClientPrivate
GArray * delayed_property_list;
GArray * delayed_property_listeners;
gint delayed_idle;
+
+ DbusmenuTextDirection text_direction;
+ DbusmenuStatus status;
+ GStrv icon_dirs;
};
typedef struct _newItemPropData newItemPropData;
@@ -124,11 +131,17 @@ typedef struct _type_handler_t type_handler_t;
struct _type_handler_t {
DbusmenuClient * client;
DbusmenuClientTypeHandler cb;
- DbusmenuClientTypeDestroyHandler destroy_cb;
+ GDestroyNotify destroy_cb;
gpointer user_data;
gchar * type;
};
+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"
@@ -145,9 +158,8 @@ static void layout_update (GDBusProxy * proxy, guint revision, gint parent, Dbus
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, GDBusProxy * proxy);
-static gint parse_layout (DbusmenuClient * client, const gchar * layout);
+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 (GVariant * properties, GError * error, gpointer data);
@@ -155,6 +167,7 @@ static void get_properties_globber (DbusmenuClient * client, gint id, const gcha
static GQuark error_domain (void);
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);
@@ -261,6 +274,20 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
NULL, NULL,
_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",
@@ -272,6 +299,16 @@ 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;
@@ -294,6 +331,8 @@ dbusmenu_client_class_init (DbusmenuClientClass *klass)
return;
}
+#define LAYOUT_PROPS_COUNT 5
+
static void
dbusmenu_client_init (DbusmenuClient *self)
{
@@ -314,6 +353,16 @@ dbusmenu_client_init (DbusmenuClient *self)
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;
@@ -326,6 +375,10 @@ dbusmenu_client_init (DbusmenuClient *self)
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;
}
@@ -378,6 +431,11 @@ dbusmenu_client_dispose (GObject *object)
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) {
@@ -386,6 +444,9 @@ dbusmenu_client_dispose (GObject *object)
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;
}
@@ -428,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;
}
@@ -470,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;
@@ -512,7 +584,8 @@ find_listener (GArray * listeners, guint index, gint id)
static void
get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
{
- GArray * listeners = (GArray *)user_data;
+ properties_callback_t * cbdata = (properties_callback_t *)user_data;
+ GArray * listeners = cbdata->listeners;
int i;
GError * error = NULL;
GVariant * params = NULL;
@@ -526,26 +599,33 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
listener->callback(NULL, error, listener->user_data);
}
- g_array_free(listeners, TRUE);
g_error_free(error);
- return;
+ goto out;
}
/* Callback all the folks we can find */
- GVariantIter * iter = g_variant_iter_new(g_variant_get_child_value(params, 0));
+ 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) {
+ 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;
}
- gint id = g_variant_get_int32(g_variant_get_child_value(child, 0));
+ GVariant * idv = g_variant_get_child_value(child, 0);
+ gint id = g_variant_get_int32(idv);
+ g_variant_unref(idv);
+
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;
}
@@ -555,8 +635,10 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
} 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_iter_free(iter);
+ g_variant_unref(parent);
g_variant_unref(params);
/* Provide errors for those who we can't */
@@ -575,8 +657,11 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
g_error_free(localerror);
}
+out:
/* Clean up */
g_array_free(listeners, TRUE);
+ g_object_unref(cbdata->client);
+ g_free(user_data);
return;
}
@@ -586,6 +671,7 @@ get_properties_callback (GObject *obj, GAsyncResult * res, gpointer user_data)
static gboolean
get_properties_idle (gpointer user_data)
{
+ properties_callback_t * cbdata = NULL;
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
g_return_val_if_fail(priv->menuproxy != NULL, TRUE);
@@ -606,7 +692,9 @@ get_properties_idle (gpointer user_data)
GVariant * variant_ids = g_variant_builder_end(&builder);
/* Build up a prop list to pass */
- g_variant_builder_init(&builder, g_variant_type_new("as"));
+ 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);
@@ -616,6 +704,11 @@ get_properties_idle (gpointer user_data)
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,
@@ -623,7 +716,7 @@ get_properties_idle (gpointer user_data)
-1, /* timeout */
NULL, /* cancellable */
get_properties_callback,
- priv->delayed_property_listeners);
+ cbdata);
/* Free properties */
gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
@@ -972,6 +1065,50 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
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);
+ }
+
+ /* 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);
+
+ g_variant_unref(icon_dirs);
+ }
+
/* If we get here, we don't need the DBus proxy */
if (priv->dbusproxy != 0) {
g_bus_unwatch_name(priv->dbusproxy);
@@ -980,6 +1117,7 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
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);
gchar * name_owner = g_dbus_proxy_get_name_owner(priv->menuproxy);
if (name_owner != NULL) {
@@ -990,6 +1128,83 @@ menuproxy_build_cb (GObject * object, GAsyncResult * res, gpointer user_data)
return;
}
+/* Handle the properites changing */
+static void
+menuproxy_prop_changed_cb (GDBusProxy * proxy, GVariant * properties, GStrv invalidated, gpointer user_data)
+{
+ 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;
+ }
+ }
+ }
+
+ /* 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;
+ }
+
+ priv->icon_dirs = g_variant_dup_strv(value, NULL);
+ dirs_changed = TRUE;
+ }
+ }
+
+ 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;
+}
+
/* Handle the case where we change owners */
static void
menuproxy_name_changed_cb (GObject * object, GParamSpec * pspec, gpointer user_data)
@@ -1015,15 +1230,88 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
{
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);
@@ -1039,40 +1327,6 @@ menuproxy_signal_cb (GDBusProxy * proxy, gchar * sender, gchar * signal, GVarian
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)
-{
- 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;
- }
-
- 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;
- }
- break;
- }
- }
-
- g_warning("Unable to find an ID on the node");
- return -1;
-}
-
/* This is the callback for the properties on a menu item. There
should be all of them in the Hash, and they we use foreach to
copy them into the menuitem.
@@ -1086,23 +1340,29 @@ menuitem_get_properties_cb (GVariant * properties, GError * error, gpointer data
if (error != NULL) {
g_warning("Error getting properties on a menuitem: %s", error->message);
- g_object_unref(data);
- return;
+ goto out;
+ }
+
+ if (properties == NULL) {
+ goto out;
}
- GVariantIter * iter = g_variant_iter_new(properties);
+ 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;
- while (g_variant_iter_next(iter, "{sv}", &key, &value)) {
- dbusmenu_menuitem_property_set_variant(item, key, value);
+ g_variant_iter_init(&iter, properties);
- g_variant_unref(value);
- g_free(key);
+ while (g_variant_iter_loop(&iter, "{sv}", &key, &value)) {
+ dbusmenu_menuitem_property_set_variant(item, key, value);
}
- g_variant_iter_free(iter);
-
+out:
g_object_unref(data);
return;
@@ -1121,13 +1381,37 @@ menuitem_get_properties_replace_cb (GVariant * properties, GError * error, gpoin
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 && have_error == FALSE;
- current_props = g_list_next(current_props)) {
- 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);
@@ -1150,8 +1434,12 @@ menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer
if (error != NULL) {
g_warning("Error getting properties on a new menuitem: %s", error->message);
- g_object_unref(propdata->item);
- 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);
@@ -1185,6 +1473,7 @@ menuitem_get_properties_new_cb (GVariant * properties, GError * error, gpointer
g_signal_emit(G_OBJECT(propdata->client), signals[NEW_MENUITEM], 0, propdata->item, TRUE);
}
+out:
g_object_unref(propdata->item);
g_free(propdata);
@@ -1211,6 +1500,7 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
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)) {
@@ -1226,14 +1516,13 @@ menuitem_call_cb (GObject * proxy, GAsyncResult * res, gpointer userdata)
/* 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, GVariant * variant, 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;
@@ -1245,12 +1534,13 @@ dbusmenu_client_send_event (DbusmenuClient * client, gint id, const gchar * name
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);
edata->timestamp = timestamp;
edata->variant = variant;
- g_variant_ref(variant);
+ g_variant_ref_sink(variant);
g_dbus_proxy_call(priv->menuproxy,
"Event",
@@ -1375,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, GDBusProxy * 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;
}
@@ -1390,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;
@@ -1436,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
@@ -1458,14 +1797,25 @@ parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * it
}
/* 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;
}
@@ -1474,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.");
@@ -1492,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");
@@ -1500,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) {
@@ -1519,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) {
@@ -1553,34 +1895,29 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
DbusmenuClient * client = DBUSMENU_CLIENT(data);
DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
- if (priv->layoutcall != NULL) {
- g_object_unref(priv->layoutcall);
- priv->layoutcall = NULL;
- }
-
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: %s", error->message);
g_error_free(error);
- return;
+ goto out;
}
- guint rev;
- gchar * xml;
+ GVariant * revv = g_variant_get_child_value(params, 0);
+ guint rev = g_variant_get_uint32(revv);
+ g_variant_unref(revv);
- g_variant_get(params, "(us)", &rev, &xml);
- g_variant_unref(params);
+ layout = g_variant_get_child_value(params, 1);
- guint parseable = parse_layout(client, xml);
- g_free(xml);
+ guint parseable = parse_layout(client, layout);
if (parseable == 0) {
g_warning("Unable to parse layout!");
- return;
+ goto out;
}
priv->my_revision = rev;
@@ -1596,6 +1933,21 @@ update_layout_cb (GObject * proxy, GAsyncResult * res, gpointer data)
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;
}
@@ -1605,6 +1957,7 @@ 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;
@@ -1622,9 +1975,20 @@ update_layout (DbusmenuClient * 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",
- g_variant_new("(i)", 0), /* root */
+ args,
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
priv->layoutcall, /* cancellable */
@@ -1636,17 +2000,17 @@ update_layout (DbusmenuClient * client)
/* 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)
@@ -1660,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)
{
@@ -1695,7 +2059,7 @@ type_handler_destroy (gpointer user_data)
{
type_handler_t * th = (type_handler_t *)user_data;
if (th->destroy_cb != NULL) {
- th->destroy_cb(th->client, th->type, th->user_data);
+ th->destroy_cb(th->user_data);
}
g_free(th->type);
g_free(th);
@@ -1703,25 +2067,25 @@ type_handler_destroy (gpointer user_data)
}
/**
- 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)
@@ -1730,32 +2094,32 @@ dbusmenu_client_add_type_handler (DbusmenuClient * client, const gchar * type, D
}
/**
- 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: 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.
+ * 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, DbusmenuClientTypeDestroyHandler destroy_func)
+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);
@@ -1788,3 +2152,59 @@ dbusmenu_client_add_type_handler_full (DbusmenuClient * client, const gchar * ty
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 6d78edf..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,17 +44,89 @@ 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;
@@ -62,15 +135,16 @@ 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.
- @reserved6: Reserved for future use.
A simple class that takes all of the information from a
#DbusmenuServer over DBus and makes the same set of
@@ -85,6 +159,7 @@ struct _DbusmenuClientClass {
void (*new_menuitem) (DbusmenuMenuitem * newitem);
void (*item_activate) (DbusmenuMenuitem * item, guint timestamp);
void (*event_result) (DbusmenuMenuitem * item, gchar * event, GVariant * data, guint timestamp, GError * error);
+ void (*icon_theme_dirs) (DbusmenuMenuitem * item, gpointer theme_dirs, GError * error);
/*< Private >*/
void (*reserved1) (void);
@@ -92,12 +167,10 @@ struct _DbusmenuClientClass {
void (*reserved3) (void);
void (*reserved4) (void);
void (*reserved5) (void);
- void (*reserved6) (void);
};
/**
DbusmenuClient:
- @parent: #GObject.
The client for a #DbusmenuServer creating a shared
object set of #DbusmenuMenuitem objects.
@@ -119,20 +192,11 @@ struct _DbusmenuClient {
The type handler is called when a dbusmenu item is created
with a matching type as setup in #dbusmenu_client_add_type_handler
-*/
-typedef gboolean (*DbusmenuClientTypeHandler) (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
-/**
- DbusmenuClientTypeDestroyHandler:
- @client: A pointer to the #DbusmenuClient
- @type: The type that this handler was registered with
- @user_data: The data you gave us
-
- This handler is called when the type becomes unregistered by the
- client. This is usally caused by the #DbusmenuClient being destroyed
- and should free memory or unref objects in @user_data.
+ Return value: #TRUE if the type has been handled. #FALSE if this
+ function was somehow unable to handle it.
*/
-typedef void (*DbusmenuClientTypeDestroyHandler) (DbusmenuClient * client, const gchar * type, gpointer user_data);
+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,
@@ -145,16 +209,10 @@ gboolean dbusmenu_client_add_type_handler_full (DbusmenuClient * cli
const gchar * type,
DbusmenuClientTypeHandler newfunc,
gpointer user_data,
- DbusmenuClientTypeDestroyHandler destory_func);
-void dbusmenu_client_send_event (DbusmenuClient * client,
- gint id,
- const gchar * name,
- GVariant * variant,
- guint timestamp);
-void dbusmenu_client_send_about_to_show(DbusmenuClient * client,
- gint id,
- void (*cb) (gpointer user_data),
- gpointer cb_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 042a24c..4b5a5d8 100644
--- a/libdbusmenu-glib/dbus-menu.xml
+++ b/libdbusmenu-glib/dbus-menu.xml
@@ -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/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 e3cb272..4382ed7 100644
--- a/libdbusmenu-glib/menuitem-marshal.list
+++ b/libdbusmenu-glib/menuitem-marshal.list
@@ -4,3 +4,4 @@ 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 2028464..336769d 100644
--- a/libdbusmenu-glib/menuitem-private.h
+++ b/libdbusmenu-glib/menuitem-private.h
@@ -33,10 +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);
+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 1d97c4c..ae6a334 100644
--- a/libdbusmenu-glib/menuitem-proxy.c
+++ b/libdbusmenu-glib/menuitem-proxy.c
@@ -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 2a22efe..ee988d8 100644
--- a/libdbusmenu-glib/menuitem-proxy.h
+++ b/libdbusmenu-glib/menuitem-proxy.h
@@ -67,8 +67,7 @@ struct _DbusmenuMenuitemProxyClass {
};
/**
- DbusmeneMenuitemProxy:
- @parent: The instance of #DbusmenuMenuitem
+ DbusmenuMenuitemProxy:
Public instance data for a #DbusmenuMenuitemProxy.
*/
@@ -83,6 +82,17 @@ 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 b40195c..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)
@@ -59,6 +60,9 @@ struct _DbusmenuMenuitemPrivate
GHashTable * properties;
gboolean root;
gboolean realized;
+ DbusmenuDefaults * defaults;
+ gboolean exposed;
+ DbusmenuMenuitem * parent;
};
/* Signals */
@@ -71,6 +75,7 @@ enum {
REALIZED,
SHOW_TO_USER,
ABOUT_TO_SHOW,
+ EVENT,
LAST_SIGNAL
};
@@ -95,7 +100,7 @@ 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, GVariant * 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 */
@@ -244,6 +249,23 @@ dbusmenu_menuitem_class_init (DbusmenuMenuitemClass *klass)
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",
@@ -312,6 +334,9 @@ dbusmenu_menuitem_init (DbusmenuMenuitem *self)
priv->root = FALSE;
priv->realized = FALSE;
+
+ priv->defaults = dbusmenu_defaults_ref_default();
+ priv->exposed = FALSE;
return;
}
@@ -328,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;
}
@@ -395,7 +430,7 @@ get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
static void
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);
}
@@ -411,7 +446,7 @@ send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gp
{
g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
- if (dbusmenu_menuitem_get_children(mi) == NULL) {
+ 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;
@@ -425,15 +460,28 @@ send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (DbusmenuMenuitem * mi, gp
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.
-*/
+ * dbusmenu_menuitem_new:
+ *
+ * Create a new #DbusmenuMenuitem with all default values.
+ *
+ * Return value: A newly allocated #DbusmenuMenuitem.
+ */
DbusmenuMenuitem *
dbusmenu_menuitem_new (void)
{
@@ -441,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)
{
@@ -457,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)
{
@@ -480,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)
{
@@ -500,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)
{
@@ -520,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)
{
@@ -541,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)
{
@@ -571,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);
@@ -579,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)
{
@@ -618,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)
{
@@ -662,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)
{
@@ -680,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);
}
@@ -694,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)
{
@@ -712,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);
}
@@ -726,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
@@ -758,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)
{
@@ -778,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);
}
@@ -792,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)
{
@@ -832,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)
{
@@ -885,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)
{
@@ -913,20 +980,98 @@ 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)
{
@@ -938,20 +1083,20 @@ dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, c
}
/**
- 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)
{
@@ -960,20 +1105,20 @@ dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * proper
}
/**
- 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)
{
@@ -982,43 +1127,99 @@ dbusmenu_menuitem_property_set_int (DbusmenuMenuitem * mi, const gchar * propert
}
/**
- 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.
-*/
+ * 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_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_utf8_validate(property, -1, NULL), FALSE);
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
+ GVariant * default_value = NULL;
- gboolean replaced = FALSE;
- gpointer currentval = g_hash_table_lookup(priv->properties, property);
+ const gchar * type = menuitem_get_type(mi);
if (value != NULL) {
- gchar * lprop = g_strdup(property);
- g_variant_ref(value);
+ /* 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));
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
- if (currentval == NULL || !g_variant_equal((GVariant*)currentval, value)) {
- g_hash_table_replace(priv->properties, lprop, value);
+ 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);
+
+ 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);
+
+ /* 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 (currentval != NULL) {
- g_hash_table_remove(priv->properties, property);
+ 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);
}
}
@@ -1026,26 +1227,39 @@ dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * pro
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 (currentval == NULL || replaced) {
- g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, value, TRUE);
+ 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)
{
@@ -1056,16 +1270,16 @@ dbusmenu_menuitem_property_get (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: A GVariant for the 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)
{
@@ -1074,19 +1288,25 @@ dbusmenu_menuitem_property_get_variant (DbusmenuMenuitem * mi, const gchar * pro
DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
- return (GVariant *)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)
{
@@ -1112,15 +1332,15 @@ dbusmenu_menuitem_property_get_bool (DbusmenuMenuitem * mi, const gchar * proper
}
/**
- 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)
{
@@ -1142,15 +1362,15 @@ dbusmenu_menuitem_property_get_int (DbusmenuMenuitem * mi, const gchar * propert
/**
- dbusmenu_menuitem_property_exit:
- @mi: The #DbusmenuMenuitem to look for the property on.
- @property: The property to look for.
-
- Checkes to see if a particular property exists on @mi and
- returns #TRUE if so.
-
- Return value: A boolean checking to see if the property is available
-*/
+ * 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)
{
@@ -1165,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)
@@ -1213,24 +1432,24 @@ copy_helper (gpointer in_key, gpointer in_value, gpointer in_data)
GHashTable * table = (GHashTable *)in_data;
gchar * key = (gchar *)in_key;
GVariant * value = (GVariant *)in_value;
- g_variant_ref(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)
@@ -1250,21 +1469,23 @@ dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
static void
variant_helper (gpointer in_key, gpointer in_value, gpointer user_data)
{
- g_variant_builder_add((GVariantBuilder *)user_data, "{sv}", in_key, in_value);
+ 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_properties_variant:
- @mi: #DbusmenuMenuitem to get properties from
-
- Grabs the properties of the menuitem as a GVariant with the
- type "a{sv}".
-
- Return Value: A GVariant of type "a{sv}" or NULL on error.
-*/
+ * 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)
+dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi, const gchar ** properties)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
@@ -1272,28 +1493,55 @@ dbusmenu_menuitem_properties_variant (DbusmenuMenuitem * mi)
GVariant * final_variant = NULL;
- if (g_hash_table_size(priv->properties) > 0) {
+ if ((properties == NULL || properties[0] == NULL) && g_hash_table_size(priv->properties) > 0) {
GVariantBuilder builder;
- g_variant_builder_init(&builder, g_variant_type_new("a{sv}"));
+ 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
-*/
+ * 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)
{
@@ -1304,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)
{
@@ -1322,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 {
@@ -1369,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)
@@ -1393,23 +1667,23 @@ 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
- @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.
+ * 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, GVariant * variant, guint timestamp)
@@ -1420,23 +1694,33 @@ dbusmenu_menuitem_handle_event (DbusmenuMenuitem * mi, const gchar * name, GVari
#endif
DbusmenuMenuitemClass * class = DBUSMENU_MENUITEM_GET_CLASS(mi);
- if (class->handle_event != NULL) {
- return class->handle_event(mi, name, variant, 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, void (*cb) (DbusmenuMenuitem * mi, gpointer user_data), gpointer cb_data)
{
@@ -1456,14 +1740,14 @@ dbusmenu_menuitem_send_about_to_show (DbusmenuMenuitem * mi, void (*cb) (Dbusmen
}
/**
- 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)
{
@@ -1473,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 a4c7611..985e1a3 100644
--- a/libdbusmenu-glib/menuitem.h
+++ b/libdbusmenu-glib/menuitem.h
@@ -41,48 +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
@@ -111,17 +392,20 @@ 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.
@@ -129,17 +413,19 @@ typedef void (*dbusmenu_menuitem_buildxml_slot_t) (DbusmenuMenuitem * mi, GPtrAr
* @child_moved: Slot for #DbusmenuMenuitem::child-moved.
* @realized: Slot for #DbusmenuMenuitem::realized.
* @about_to_show: Slot for #DbusmenuMenuitem::about-to-show.
- * @buildxml: Virtual function that appends the strings required to represent this menu item in the menu XML file.
+ * @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.
- * @reserved6: Reserved for future use.
+ *
+ * Functions and signals that every menuitem should know something
+ * about.
*/
typedef struct _DbusmenuMenuitemClass DbusmenuMenuitemClass;
struct _DbusmenuMenuitemClass
@@ -155,20 +441,21 @@ struct _DbusmenuMenuitemClass
void (*realized) (void);
/* Virtual functions */
- dbusmenu_menuitem_buildxml_slot_t buildxml;
- void (*handle_event) (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp);
+ 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);
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);
- void (*reserved6) (void);
};
GType dbusmenu_menuitem_get_type (void);
@@ -190,6 +477,10 @@ 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_variant (DbusmenuMenuitem * mi, const gchar * property, GVariant * value);
gboolean dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value);
@@ -207,7 +498,7 @@ 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, GVariant * value, guint timestamp);
+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.c b/libdbusmenu-glib/server.c
index adb9f91..c7057df 100644
--- a/libdbusmenu-glib/server.c
+++ b/libdbusmenu-glib/server.c
@@ -30,11 +30,13 @@ 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"
#include "dbus-menu-clean.xml.h"
@@ -54,6 +56,13 @@ struct _DbusmenuServerPrivate
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) (DBUSMENU_SERVER(o)->priv)
@@ -74,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 */
@@ -121,6 +133,7 @@ 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,
@@ -156,6 +169,7 @@ static void menuitem_signals_create (DbusmenuMenuitem * mi,
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);
@@ -285,6 +299,16 @@ 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;
@@ -344,6 +368,10 @@ dbusmenu_server_init (DbusmenuServer *self)
priv->bus_lookup = NULL;
priv->dbus_registration = 0;
+ default_text_direction(self);
+ priv->status = DBUSMENU_STATUS_NORMAL;
+ priv->icon_dirs = NULL;
+
return;
}
@@ -354,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) {
@@ -389,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;
}
@@ -409,6 +455,7 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
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 {
register_object(DBUSMENU_SERVER(obj));
@@ -418,6 +465,15 @@ set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
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;
}
@@ -426,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;
@@ -465,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;
@@ -473,6 +590,49 @@ 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)
@@ -535,6 +695,7 @@ bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
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;
}
@@ -547,6 +708,7 @@ bus_got_cb (GObject * obj, GAsyncResult * result, gpointer user_data)
register_object(DBUSMENU_SERVER(user_data));
+ g_object_unref(G_OBJECT(user_data));
return;
}
@@ -590,9 +752,28 @@ bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar *
/* 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);
- g_return_val_if_fail(g_strcmp0(property, "version") == 0, NULL);
- return g_variant_new_uint32(DBUSMENU_VERSION_NUMBER);
+ 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
@@ -633,22 +814,280 @@ layout_update_signal (DbusmenuServer * server)
return;
}
-static void
-menuitem_property_changed (DbusmenuMenuitem * mi, gchar * property, GVariant * variant, DbusmenuServer * server)
+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)
{
- DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
+ int i, j;
- g_signal_emit(G_OBJECT(server), signals[ID_PROP_UPDATE], 0, dbusmenu_menuitem_get_id(mi), property, variant, TRUE);
+ 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);
- if (priv->dbusobject != NULL && priv->bus != NULL) {
+ 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,
- "ItemPropertyUpdated",
- g_variant_new("(isv)", dbusmenu_menuitem_get_id(mi), property, variant),
+ "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, GVariant * variant, DbusmenuServer * server)
+{
+ 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;
}
@@ -757,26 +1196,35 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- gint parent = 0;
- g_variant_get(params, "(i)", &parent);
+ /* Input */
+ gint32 parent;
+ gint32 recurse;
+ const gchar ** props;
+
+ g_variant_get(params, "(ii^a&s)", &parent, &recurse, &props);
+ /* Output */
guint revision = priv->layout_revision;
- GPtrArray * xmlarray = g_ptr_array_new();
+ GVariant * items = NULL;
- 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);
- }
- } else {
- DbusmenuMenuitem * item = NULL;
- if (priv->root != NULL) {
- item = dbusmenu_menuitem_find_id(priv->root, parent);
- }
+ if (priv->root != NULL) {
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, parent);
- if (item == NULL) {
+ if (mi != NULL) {
+ items = dbusmenu_menuitem_build_variant(mi, props, recurse);
+ }
+ }
+ 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,
@@ -784,23 +1232,19 @@ bus_get_layout (DbusmenuServer * server, GVariant * params, GDBusMethodInvocatio
parent);
return;
}
- dbusmenu_menuitem_buildxml(item, xmlarray);
}
- g_ptr_array_add(xmlarray, NULL);
- /* build string */
- gchar * 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);
+ 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,
- g_variant_new("(us)",
- revision,
- layout));
-
- g_free(layout);
-
+ retval);
return;
}
@@ -817,9 +1261,11 @@ bus_get_property (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat
"There currently isn't a layout in this server");
return;
}
-
- gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
- const gchar * property = g_variant_get_string(g_variant_get_child_value(params, 1), NULL);
+
+ gint32 id;
+ const gchar * property;
+
+ g_variant_get(params, "(i&s)", &id, &property);
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
@@ -861,7 +1307,8 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc
return;
}
- gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
@@ -874,7 +1321,7 @@ bus_get_properties (DbusmenuServer * server, GVariant * params, GDBusMethodInvoc
return;
}
- GVariant * dict = dbusmenu_menuitem_properties_variant(mi);
+ GVariant * dict = dbusmenu_menuitem_properties_variant(mi, NULL);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", dict));
@@ -889,28 +1336,43 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
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 && g_variant_get_int32(g_variant_get_child_value(idlist, 0)) == 0) {
- GVariant * final = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, NULL);
- g_dbus_method_invocation_return_value(invocation, final);
- return;
- }
+ if (g_variant_n_children(idlist) == 1) {
- g_dbus_method_invocation_return_error(invocation,
- error_quark(),
- NO_VALID_LAYOUT,
- "There currently isn't a layout in this server");
+ GVariant *id_v = g_variant_get_child_value(idlist, 0);
+ gint32 id = g_variant_get_int32(id_v);
+ g_variant_unref(id_v);
+
+ if (id == 0) {
+
+ 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_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;
}
- GVariantIter ids;
- g_variant_iter_init(&ids, g_variant_get_child_value(params, 0));
+ GVariantIter *ids;
+ g_variant_get(params, "(aias)", &ids, NULL);
+ /* TODO: implementation ignores propertyNames declared in XML */
GVariantBuilder builder;
gboolean builder_init = FALSE;
- gint id;
- while (g_variant_iter_next(&ids, "i", &id)) {
+ gint32 id;
+ while (g_variant_iter_loop(ids, "i", &id)) {
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) continue;
@@ -922,11 +1384,11 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
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);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
if (props == NULL) {
GError * error = NULL;
- props = g_variant_parse(g_variant_type_new("a{sv}"), "{}", NULL, NULL, &error);
+ 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);
@@ -939,18 +1401,20 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
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_new("a(ia(sv))"), "[]", NULL, NULL, 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_warning("Unable to parse '[]' as a 'a(ia{sv})': %s", error->message);
g_error_free(error);
- ret = NULL;
}
}
@@ -958,6 +1422,7 @@ bus_get_group_properties (DbusmenuServer * server, GVariant * params, GDBusMetho
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");
@@ -982,7 +1447,7 @@ serialize_menuitem(gpointer data, gpointer user_data)
gint id = dbusmenu_menuitem_get_id(mi);
g_variant_builder_add_value(&tuple, g_variant_new_int32(id));
- GVariant * props = dbusmenu_menuitem_properties_variant(mi);
+ GVariant * props = dbusmenu_menuitem_properties_variant(mi, NULL);
g_variant_builder_add_value(&tuple, props);
g_variant_builder_add_value(builder, g_variant_builder_end(&tuple));
@@ -996,7 +1461,8 @@ static void
bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * invocation)
{
DbusmenuServerPrivate * priv = DBUSMENU_SERVER_GET_PRIVATE(server);
- gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
if (priv->root == NULL) {
g_dbus_method_invocation_return_error(invocation,
@@ -1030,7 +1496,7 @@ bus_get_children (DbusmenuServer * server, GVariant * params, GDBusMethodInvocat
ret = g_variant_new_tuple(&end, 1);
} else {
GError * error = NULL;
- ret = g_variant_parse(g_variant_type_new("(a(ia{sv}))"), "([(0, {})],)", NULL, NULL, &error);
+ 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);
@@ -1082,32 +1548,35 @@ bus_event (DbusmenuServer * server, GVariant * params, GDBusMethodInvocation * i
return;
}
- gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
+ gint32 id;
+ gchar *etype;
+ GVariant *data;
+ guint32 ts;
+
+ 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);
- 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(g_variant_get_string(g_variant_get_child_value(params, 1), NULL));
- event_data->timestamp = g_variant_get_uint32(g_variant_get_child_value(params, 3));
- event_data->variant = g_variant_get_child_value(params, 2);
+ g_free(etype);
+ g_variant_unref(data);
- if (g_variant_is_of_type(event_data->variant, G_VARIANT_TYPE_VARIANT)) {
- event_data->variant = g_variant_get_variant(event_data->variant);
- }
+ } else {
- g_variant_ref(event_data->variant);
+ 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_timeout_add(0, event_local_handler, event_data);
+ }
g_dbus_method_invocation_return_value(invocation, NULL);
return;
@@ -1127,7 +1596,8 @@ bus_about_to_show (DbusmenuServer * server, GVariant * params, GDBusMethodInvoca
return;
}
- gint id = g_variant_get_int32(g_variant_get_child_value(params, 0));
+ gint32 id;
+ g_variant_get(params, "(i)", &id);
DbusmenuMenuitem * mi = dbusmenu_menuitem_find_id(priv->root, id);
if (mi == NULL) {
@@ -1197,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 5668258..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,67 @@ 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;
@@ -61,8 +114,7 @@ typedef struct _DbusmenuServerPrivate DbusmenuServerPrivate;
@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.
-
+ @item_activation: Slot for #DbusmenuServer::item-activation-requested.
@reserved1: Reserved for future use.
@reserved2: Reserved for future use.
@reserved3: Reserved for future use.
@@ -93,7 +145,6 @@ struct _DbusmenuServerClass {
/**
DbusmenuServer:
- @parent: #GObject
A server which represents a sharing of a set of
#DbusmenuMenuitems across DBus to a #DbusmenuClient.
@@ -106,12 +157,22 @@ struct _DbusmenuServer {
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__ */
+
diff --git a/libdbusmenu-gtk/Makefile.am b/libdbusmenu-gtk/Makefile.am
index f3556e9..4ec464b 100644
--- a/libdbusmenu-gtk/Makefile.am
+++ b/libdbusmenu-gtk/Makefile.am
@@ -1,6 +1,4 @@
-CLEANFILES =
-
if USE_GTK3
VER=3
GTKGIR=Gtk-3.0
@@ -13,10 +11,28 @@ GTKVALA=gtk+-2.0
lib_LTLIBRARIES = libdbusmenu-gtk.la
endif
+BUILT_SOURCES =
+CLEANFILES =
+DISTCLEANFILES =
EXTRA_DIST = \
dbusmenu-gtk-0.4.pc.in \
dbusmenu-gtk3-0.4.pc.in
+##############
+# Enum Stuff
+##############
+
+include $(top_srcdir)/Makefile.am.enum
+
+glib_enum_h = genericmenuitem-enum-types.h
+glib_enum_c = genericmenuitem-enum-types.c
+glib_enum_headers = $(srcdir)/genericmenuitem.h
+
+
+#####################
+# Include Directory
+#####################
+
libdbusmenu_gtkincludedir=$(includedir)/libdbusmenu-0.4/libdbusmenu-gtk$(VER)/
libdbusmenu_gtkinclude_HEADERS = \
@@ -24,22 +40,21 @@ libdbusmenu_gtkinclude_HEADERS = \
client.h \
menu.h \
menuitem.h \
- parser.h \
- serializablemenuitem.h
+ parser.h
libdbusmenu_gtk_la_SOURCES = \
client.h \
client.c \
genericmenuitem.h \
genericmenuitem.c \
+ genericmenuitem-enum-types.h \
+ genericmenuitem-enum-types.c \
menu.h \
menu.c \
menuitem.h \
menuitem.c \
parser.h \
- parser.c \
- serializablemenuitem.h \
- serializablemenuitem.c
+ parser.c
libdbusmenu_gtk_la_LDFLAGS = \
-version-info $(LIBDBUSMENU_CURRENT):$(LIBDBUSMENU_REVISION):$(LIBDBUSMENU_AGE) \
@@ -78,32 +93,31 @@ if INTROSPECTION_TEN
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
--warn-all \
--add-include-path=$(top_builddir)/libdbusmenu-glib \
- $(addprefix --c-include=libdbusmenu-gtk/, $(introspection_sources)) \
+ $(addprefix --c-include=libdbusmenu-gtk/, $(libdbusmenu_gtkinclude_HEADERS)) \
--symbol-prefix=dbusmenu \
--identifier-prefix=DbusmenuGtk
else
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) \
--warn-all \
--add-include-path=$(top_builddir)/libdbusmenu-glib \
- $(addprefix --c-include=libdbusmenu-gtk/, $(introspection_sources))
+ $(addprefix --c-include=libdbusmenu-gtk/, $(libdbusmenu_gtkinclude_HEADERS))
endif
INTROSPECTION_COMPILER_ARGS = --includedir=$(builddir) --includedir=$(top_builddir)/libdbusmenu-glib
if HAVE_INTROSPECTION
-introspection_sources = $(libdbusmenu_gtkinclude_HEADERS)
+introspection_sources = $(filter-out genericmenuitem%, $(libdbusmenu_gtkinclude_HEADERS) $(libdbusmenu_gtk_la_SOURCES))
DbusmenuGtk$(VER)-0.4.gir: libdbusmenu-gtk$(VER).la
DbusmenuGtk_0_4_gir_INCLUDES = \
GObject-2.0 \
$(GTKGIR) \
- Dbusmenu-Glib-0.4
+ Dbusmenu-0.4
DbusmenuGtk_0_4_gir_CFLAGS = $(DBUSMENUGTK_CFLAGS) -I$(top_srcdir)
DbusmenuGtk_0_4_gir_LIBS = libdbusmenu-gtk$(VER).la
DbusmenuGtk_0_4_gir_FILES = $(addprefix $(srcdir)/, $(introspection_sources))
DbusmenuGtk_0_4_gir_NAMESPACE = DbusmenuGtk$(VER)
-DbusmenuGtk_0_4_gir_SCANNERFLAGS = $(INTROSPECTION_SCANNER_ARGS)
DbusmenuGtk_0_4_gir_EXPORT_PACKAGES = dbusmenu-gtk$(VER)-0.4
# We duplicate these for the same reason as libdbusmenu_gtk3includedir above
@@ -112,7 +126,6 @@ DbusmenuGtk3_0_4_gir_CFLAGS = $(DbusmenuGtk_0_4_gir_CFLAGS)
DbusmenuGtk3_0_4_gir_LIBS = $(DbusmenuGtk_0_4_gir_LIBS)
DbusmenuGtk3_0_4_gir_FILES = $(DbusmenuGtk_0_4_gir_FILES)
DbusmenuGtk3_0_4_gir_NAMESPACE = $(DbusmenuGtk_0_4_gir_NAMESPACE)
-DbusmenuGtk3_0_4_gir_SCANNERFLAGS = $(DbusmenuGtk_0_4_gir_SCANNERFLAGS)
DbusmenuGtk3_0_4_gir_EXPORT_PACKAGES = $(DbusmenuGtk_0_4_gir_EXPORT_PACKAGES)
INTROSPECTION_GIRS += DbusmenuGtk$(VER)-0.4.gir
@@ -141,7 +154,7 @@ DbusmenuGtk$(VER)-0.4.vapi: DbusmenuGtk$(VER)-0.4.tmp.gir Makefile.am
--pkg gdk-pixbuf-2.0 \
--pkg $(GTKVALA) \
--pkg atk \
- --pkg Dbusmenu-Glib-0.4 \
+ --pkg Dbusmenu-0.4 \
--vapidir=$(top_builddir)/libdbusmenu-glib \
$<
diff --git a/libdbusmenu-gtk/client.c b/libdbusmenu-gtk/client.c
index 7ab2fe9..1051f20 100644
--- a/libdbusmenu-gtk/client.c
+++ b/libdbusmenu-gtk/client.c
@@ -31,16 +31,21 @@ License version 3 and version 2.1 along with this program. If not, see
#endif
#include <gtk/gtk.h>
+#include <glib.h>
#include "client.h"
#include "menuitem.h"
#include "genericmenuitem.h"
+#include "genericmenuitem-enum-types.h"
/* Private */
struct _DbusmenuGtkClientPrivate {
+ GStrv old_themedirs;
GtkAccelGroup * agroup;
};
+GHashTable * theme_dir_db = NULL;
+
#define DBUSMENU_GTKCLIENT_GET_PRIVATE(o) (DBUSMENU_GTKCLIENT(o)->priv)
#define USE_FALLBACK_PROP "use-fallback"
@@ -54,6 +59,9 @@ static void new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint po
static void delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient * gtkclient);
static void move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint old, DbusmenuGtkClient * gtkclient);
static void item_activate (DbusmenuClient * client, DbusmenuMenuitem * mi, guint timestamp, gpointer userdata);
+static void theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata);
+static void remove_theme_dirs (GtkIconTheme * theme, GStrv dirs);
+static void event_result (DbusmenuClient * client, DbusmenuMenuitem * mi, const gchar * event, GVariant * variant, guint timestamp, GError * error);
static gboolean new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
static gboolean new_item_seperator (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
@@ -89,6 +97,23 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(self);
priv->agroup = NULL;
+ priv->old_themedirs = NULL;
+
+ /* We either build the theme db or we get a reference
+ to it. This way when all clients die the hashtable
+ will be free'd as well. */
+ if (theme_dir_db == NULL) {
+ theme_dir_db = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ /* NOTE: We're adding an extra ref here because there
+ is no way to clear the pointer when the hash table
+ dies, so it's safer to keep the hash table around
+ forever than not know if it's free'd or not. Patch
+ submitted to GLib. */
+ g_hash_table_ref(theme_dir_db);
+ } else {
+ g_hash_table_ref(theme_dir_db);
+ }
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_DEFAULT, new_item_normal);
dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(self), DBUSMENU_CLIENT_TYPES_SEPARATOR, new_item_seperator);
@@ -96,6 +121,10 @@ dbusmenu_gtkclient_init (DbusmenuGtkClient *self)
/* TODO: I think these can be handled in the class... */
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_NEW_MENUITEM, G_CALLBACK(new_menuitem), NULL);
g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ITEM_ACTIVATE, G_CALLBACK(item_activate), NULL);
+ g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_ICON_THEME_DIRS_CHANGED, G_CALLBACK(theme_dir_changed), NULL);
+ g_signal_connect(G_OBJECT(self), DBUSMENU_CLIENT_SIGNAL_EVENT_RESULT, G_CALLBACK(event_result), NULL);
+
+ theme_dir_changed(DBUSMENU_CLIENT(self), dbusmenu_client_get_icon_paths(DBUSMENU_CLIENT(self)), NULL);
return;
}
@@ -111,6 +140,18 @@ dbusmenu_gtkclient_dispose (GObject *object)
priv->agroup = NULL;
}
+ if (priv->old_themedirs) {
+ remove_theme_dirs(gtk_icon_theme_get_default(), priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ if (theme_dir_db != NULL) {
+ g_hash_table_unref(theme_dir_db);
+ } else {
+ g_assert_not_reached();
+ }
+
G_OBJECT_CLASS (dbusmenu_gtkclient_parent_class)->dispose (object);
return;
}
@@ -124,6 +165,141 @@ dbusmenu_gtkclient_finalize (GObject *object)
return;
}
+/* Add a theme directory to the table and the theme's list of available
+ themes to use. */
+static void
+theme_dir_ref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ int count = 0;
+ if ((count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir))) != 0) {
+ /* It exists so what we need to do is increase the ref
+ count of this dir. */
+ count++;
+ } else {
+ /* It doesn't exist, so we need to add it to the table
+ and to the search path. */
+ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir);
+ g_debug("\tAppending search path: %s", dir);
+ count = 1;
+ }
+
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+
+ return;
+}
+
+/* Unreference the theme directory, and if it's count goes to zero then
+ we need to remove it from the search path. */
+static void
+theme_dir_unref (GtkIconTheme * theme, GHashTable * db, const gchar * dir)
+{
+ g_return_if_fail(db != NULL);
+ g_return_if_fail(theme != NULL);
+ g_return_if_fail(dir != NULL);
+
+ /* Grab the count for this dir */
+ int count = GPOINTER_TO_INT(g_hash_table_lookup(db, dir));
+
+ /* Is this a simple deprecation, if so, we can just lower the
+ number and move on. */
+ if (count > 1) {
+ count--;
+ g_hash_table_insert(db, g_strdup(dir), GINT_TO_POINTER(count));
+ return;
+ }
+
+ /* Try to remove it from the hash table, this makes sure
+ that it existed */
+ if (!g_hash_table_remove(db, dir)) {
+ g_warning("Unref'd a directory that wasn't in the theme dir hash table.");
+ return;
+ }
+
+ gchar ** paths;
+ gint path_count;
+
+ gtk_icon_theme_get_search_path(theme, &paths, &path_count);
+
+ gint i;
+ gboolean found = FALSE;
+ for (i = 0; i < path_count; i++) {
+ if (found) {
+ /* If we've already found the right entry */
+ paths[i - 1] = paths[i];
+ } else {
+ /* We're still looking, is this the one? */
+ if (!g_strcmp0(paths[i], dir)) {
+ found = TRUE;
+ /* We're freeing this here as it won't be captured by the
+ g_strfreev() below as it's out of the array. */
+ g_free(paths[i]);
+ }
+ }
+ }
+
+ /* If we found one we need to reset the path to
+ accomidate the changes */
+ if (found) {
+ paths[path_count - 1] = NULL; /* Clear the last one */
+ gtk_icon_theme_set_search_path(theme, (const gchar **)paths, path_count - 1);
+ }
+
+ g_strfreev(paths);
+
+ return;
+}
+
+/* Unregister this list of theme directories */
+static void
+remove_theme_dirs (GtkIconTheme * theme, GStrv dirs)
+{
+ g_return_if_fail(GTK_ICON_THEME(theme));
+ g_return_if_fail(dirs != NULL);
+
+ int dir;
+
+ for (dir = 0; dirs[dir] != NULL; dir++) {
+ theme_dir_unref(theme, theme_dir_db, dirs[dir]);
+ }
+
+ return;
+}
+
+/* Called when the theme directories are changed by the
+ server part of things. */
+static void
+theme_dir_changed (DbusmenuClient * client, GStrv theme_dirs, gpointer userdata)
+{
+ DbusmenuGtkClientPrivate * priv = DBUSMENU_GTKCLIENT_GET_PRIVATE(client);
+ GtkIconTheme * theme = gtk_icon_theme_get_default();
+
+ /* Ref the new directories */
+ if (theme_dirs != NULL) {
+ int dir;
+ for (dir = 0; theme_dirs[dir] != NULL; dir++) {
+ theme_dir_ref(theme, theme_dir_db, theme_dirs[dir]);
+ }
+ }
+
+ /* Unref the old ones */
+ if (priv->old_themedirs) {
+ remove_theme_dirs(theme, priv->old_themedirs);
+ g_strfreev(priv->old_themedirs);
+ priv->old_themedirs = NULL;
+ }
+
+ /* Copy the new to the old */
+ if (theme_dirs != NULL) {
+ priv->old_themedirs = g_strdupv(theme_dirs);
+ }
+
+ return;
+}
+
/* Structure for passing data to swap_agroup */
typedef struct _swap_agroup_t swap_agroup_t;
struct _swap_agroup_t {
@@ -219,13 +395,13 @@ refresh_shortcut (DbusmenuGtkClient * client, DbusmenuMenuitem * mi)
/**
- dbusmenu_gtkclient_set_accel_group:
- @client: To set the group on
- @agroup: The new acceleration group
-
- Sets the acceleration group for the menu items with accelerators
- on this client.
-*/
+ * dbusmenu_gtkclient_set_accel_group:
+ * @client: To set the group on
+ * @agroup: The new acceleration group
+ *
+ * Sets the acceleration group for the menu items with accelerators
+ * on this client.
+ */
void
dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup * agroup)
{
@@ -256,14 +432,14 @@ dbusmenu_gtkclient_set_accel_group (DbusmenuGtkClient * client, GtkAccelGroup *
}
/**
- dbusmenu_gtkclient_get_accel_group:
- @client: Client to query for an accelerator group
-
- Gets the accel group for this client.
-
- Return value: Either a valid group or #NULL on error or
- none set.
-*/
+ * dbusmenu_gtkclient_get_accel_group:
+ * @client: Client to query for an accelerator group
+ *
+ * Gets the accel group for this client.
+ *
+ * Return value: (transfer none): Either a valid group or #NULL on error or
+ * none set.
+ */
GtkAccelGroup *
dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client)
{
@@ -276,8 +452,97 @@ dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client)
/* Internal Functions */
-static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem";
-static const gchar * data_menu = "dbusmenugtk-data-gtkmenu";
+static const gchar * data_menuitem = "dbusmenugtk-data-gtkmenuitem";
+static const gchar * data_menu = "dbusmenugtk-data-gtkmenu";
+static const gchar * data_activating = "dbusmenugtk-data-activating";
+static const gchar * data_idle_close_id = "dbusmenugtk-data-idle-close-id";
+static const gchar * data_delayed_close = "dbusmenugtk-data-delayed-close";
+
+static void
+menu_item_start_activating(DbusmenuMenuitem * mi)
+{
+ /* Mark this item and all it's parents as activating */
+ DbusmenuMenuitem * parent = mi;
+ do {
+ g_object_set_data(G_OBJECT(parent), data_activating,
+ GINT_TO_POINTER(TRUE));
+ } while ((parent = dbusmenu_menuitem_get_parent (parent)) != NULL);
+
+ GVariant * variant = g_variant_new("i", 0);
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_ACTIVATED, variant, gtk_get_current_event_time());
+}
+
+static gboolean
+menu_item_is_activating(DbusmenuMenuitem * mi)
+{
+ return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi), data_activating));
+}
+
+static void
+menu_item_stop_activating(DbusmenuMenuitem * mi)
+{
+ if (!menu_item_is_activating(mi))
+ return;
+
+ /* Mark this item and all it's parents as not activating and finally
+ send their queued close event. */
+ g_object_set_data(G_OBJECT(mi), data_activating, GINT_TO_POINTER(FALSE));
+
+ /* There is one master root parent that we don't care about, so stop
+ right before it */
+ DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi);
+ while (dbusmenu_menuitem_get_parent (parent) != NULL &&
+ menu_item_is_activating(parent)) {
+ /* Now clean up the activating flag */
+ g_object_set_data(G_OBJECT(parent), data_activating,
+ GINT_TO_POINTER(FALSE));
+
+ gboolean should_close = FALSE;
+
+ /* Note that dbus might be fast enough to have already
+ processed the app's reply before close_in_idle() is called.
+ So to avoid that, we shut down any pending close_in_idle call */
+ guint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent),
+ data_idle_close_id));
+ if (id > 0) {
+ g_source_remove(id);
+ g_object_set_data(G_OBJECT(parent), data_idle_close_id,
+ GINT_TO_POINTER(0));
+ should_close = TRUE;
+ }
+
+ gboolean delayed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi),
+ data_delayed_close));
+ if (delayed) {
+ g_object_set_data(G_OBJECT(mi), data_delayed_close,
+ GINT_TO_POINTER(FALSE));
+ should_close = TRUE;
+ }
+
+ /* And finally send a delayed closed event if one would have
+ happened */
+ if (should_close) {
+ dbusmenu_menuitem_handle_event(parent,
+ DBUSMENU_MENUITEM_EVENT_CLOSED,
+ NULL,
+ gtk_get_current_event_time());
+ }
+
+ parent = dbusmenu_menuitem_get_parent (parent);
+ }
+}
+
+static void
+event_result (DbusmenuClient * client, DbusmenuMenuitem * mi,
+ const gchar * event, GVariant * variant, guint timestamp,
+ GError * error)
+{
+ if (g_strcmp0(event, DBUSMENU_MENUITEM_EVENT_ACTIVATED) == 0) {
+ menu_item_stop_activating(mi);
+ }
+
+ return;
+}
/* This is the call back for the GTK widget for when it gets
clicked on by the user to send it back across the bus. */
@@ -285,8 +550,7 @@ static gboolean
menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
{
if (gtk_menu_item_get_submenu(gmi) == NULL) {
- GVariant * variant = g_variant_new("i", 0);
- dbusmenu_menuitem_handle_event(mi, "clicked", variant, gtk_get_current_event_time());
+ menu_item_start_activating(mi);
} else {
/* TODO: We need to stop the display of the submenu
until this callback returns. */
@@ -295,6 +559,44 @@ menu_pressed_cb (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
return TRUE;
}
+static gboolean
+close_in_idle (DbusmenuMenuitem * mi)
+{
+ /* Don't send closed signal if we also sent activating signal.
+ We'd just be asking for race conditions. We'll send closed
+ when done with activation. */
+ if (!menu_item_is_activating(mi))
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_CLOSED, NULL, gtk_get_current_event_time());
+ else
+ g_object_set_data(G_OBJECT(mi), data_delayed_close, GINT_TO_POINTER(TRUE));
+
+ g_object_set_data(G_OBJECT(mi), data_idle_close_id, GINT_TO_POINTER(0));
+ return FALSE;
+}
+
+static void
+submenu_notify_visible_cb (GtkWidget * menu, GParamSpec * pspec, DbusmenuMenuitem * mi)
+{
+ if (gtk_widget_get_visible (menu)) {
+ menu_item_stop_activating(mi); /* just in case */
+ dbusmenu_menuitem_handle_event(mi, DBUSMENU_MENUITEM_EVENT_OPENED, NULL, gtk_get_current_event_time());
+ } else {
+ /* Try to close in the idle loop because we actually get a menu
+ close notification before we get notified that a menu item
+ was clicked. We want to give that clicked signal some
+ time, so we wait until all queued signals are handled before
+ continuing. (our handling of the closed signal depends on
+ whether the user clicked an item or not) */
+ guint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(mi),
+ data_idle_close_id));
+ if (id == 0) {
+ id = g_idle_add((GSourceFunc)close_in_idle, mi);
+ g_object_set_data(G_OBJECT(mi), data_idle_close_id,
+ GINT_TO_POINTER(id));
+ }
+ }
+}
+
/* Process the visible property */
static void
process_visible (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * value)
@@ -370,11 +672,58 @@ process_toggle_state (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * varia
return;
}
+/* Submenu processing */
+static void
+process_submenu (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient)
+{
+ const gchar * submenu = NULL;
+ if (variant != NULL) {
+ submenu = g_variant_get_string(variant, NULL);
+ }
+
+ if (g_strcmp0(submenu, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU) != 0) {
+ /* This is the only case we're really supporting right now,
+ so if it's not this, we want to clean up. */
+ /* We're just going to warn for now. */
+ gpointer pmenu = g_object_get_data(G_OBJECT(mi), data_menu);
+ if (pmenu != NULL) {
+ g_warning("The child-display variable is set to '%s' but there's a menu, odd?", submenu);
+ }
+ } else {
+ /* We need to build a menu for these guys to live in. */
+ GtkMenu * menu = GTK_MENU(gtk_menu_new());
+ g_object_ref_sink(menu);
+ g_object_set_data_full(G_OBJECT(mi), data_menu, menu, g_object_unref);
+
+ gtk_menu_item_set_submenu(gmi, GTK_WIDGET(menu));
+
+ g_signal_connect(menu, "notify::visible", G_CALLBACK(submenu_notify_visible_cb), mi);
+ }
+
+ return;
+}
+
+/* Process the disposition changing */
+static void
+process_disposition (DbusmenuMenuitem * mi, GtkMenuItem * gmi, GVariant * variant, DbusmenuGtkClient * gtkclient)
+{
+ /* We can only handle generic menu items here. Perhaps someone else
+ will find the value useful. Not us. */
+ if (!IS_GENERICMENUITEM(gmi)) {
+ return;
+ }
+
+ genericmenuitem_set_disposition(GENERICMENUITEM(gmi), genericmenuitem_disposition_get_value_from_nick(g_variant_get_string(variant, NULL)));
+ return;
+}
+
/* Whenever we have a property change on a DbusmenuMenuitem
we need to be responsive to that. */
static void
-menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, GtkMenuItem * gmi)
+menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, DbusmenuGtkClient * gtkclient)
{
+ GtkMenuItem * gmi = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
+
if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_LABEL)) {
gtk_menu_item_set_label(gmi, variant == NULL ? NULL : g_variant_get_string(variant, NULL));
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
@@ -385,6 +734,10 @@ menu_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * variant, Gt
process_toggle_type(mi, gmi, variant);
} else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE)) {
process_toggle_state(mi, gmi, variant);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) {
+ process_submenu(mi, gmi, variant, gtkclient);
+ } else if (!g_strcmp0(prop, DBUSMENU_MENUITEM_PROP_DISPOSITION)) {
+ process_disposition(mi, gmi, variant, gtkclient);
}
return;
@@ -401,19 +754,6 @@ menu_shortcut_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value,
return;
}
-/* Call back that happens when the DbusmenuMenuitem
- is destroyed. We're making sure to clean up everything
- else down the pipe. */
-static void
-destoryed_dbusmenuitem_cb (gpointer udata, GObject * dbusmenuitem)
-{
- #ifdef MASSIVEDEBUGGING
- g_debug("DbusmenuMenuitem was destroyed");
- #endif
- gtk_widget_destroy(GTK_WIDGET(udata));
- return;
-}
-
/* The new menuitem signal only happens if we don't have a type handler
for the type of the item. This should be an error condition and we're
printing out a message. */
@@ -493,21 +833,21 @@ destroy_gmi (GtkMenuItem * gmi, DbusmenuMenuitem * mi)
#endif
/**
- dbusmenu_gtkclient_newitem_base:
- @client: The client handling everything on this connection
- @item: The #DbusmenuMenuitem to attach the GTK-isms to
- @gmi: A #GtkMenuItem representing the GTK world's view of this menuitem
- @parent: The parent #DbusmenuMenuitem
-
- This function provides some of the basic connectivity for being in
- the GTK world. Things like visibility and sensitivity of the item are
- handled here so that the subclasses don't have to. If you're building
- your on GTK menu item you can use this function to apply those basic
- attributes so that you don't have to deal with them either.
-
- This also handles passing the "activate" signal back to the
- #DbusmenuMenuitem side of thing.
-*/
+ * dbusmenu_gtkclient_newitem_base:
+ * @client: The client handling everything on this connection
+ * @item: The #DbusmenuMenuitem to attach the GTK-isms to
+ * @gmi: A #GtkMenuItem representing the GTK world's view of this menuitem
+ * @parent: The parent #DbusmenuMenuitem
+ *
+ * This function provides some of the basic connectivity for being in
+ * the GTK world. Things like visibility and sensitivity of the item are
+ * handled here so that the subclasses don't have to. If you're building
+ * your on GTK menu item you can use this function to apply those basic
+ * attributes so that you don't have to deal with them either.
+ *
+ * This also handles passing the "activate" signal back to the
+ * #DbusmenuMenuitem side of thing.
+ */
void
dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * item, GtkMenuItem * gmi, DbusmenuMenuitem * parent)
{
@@ -516,14 +856,11 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
#endif
/* Attach these two */
- g_object_set_data(G_OBJECT(item), data_menuitem, gmi);
- g_object_ref(G_OBJECT(gmi));
- #ifdef MASSIVEDEBUGGING
- g_signal_connect(G_OBJECT(gmi), "destroy", G_CALLBACK(destroy_gmi), item);
- #endif
+ g_object_ref_sink(G_OBJECT(gmi));
+ g_object_set_data_full(G_OBJECT(item), data_menuitem, gmi, (GDestroyNotify)gtk_widget_destroy);
/* DbusmenuMenuitem signals */
- g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), gmi);
+ g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_prop_change_cb), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(menu_shortcut_change_cb), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(delete_child), client);
g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(move_child), client);
@@ -531,14 +868,13 @@ dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem *
/* GtkMenuitem signals */
g_signal_connect(G_OBJECT(gmi), "activate", G_CALLBACK(menu_pressed_cb), item);
- /* Life insurance */
- g_object_weak_ref(G_OBJECT(item), destoryed_dbusmenuitem_cb, gmi);
-
/* Check our set of props to see if any are set already */
process_visible(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_VISIBLE));
process_sensitive(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_ENABLED));
process_toggle_type(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE));
process_toggle_state(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE));
+ process_submenu(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY), client);
+ process_disposition(item, gmi, dbusmenu_menuitem_property_get_variant(item, DBUSMENU_MENUITEM_PROP_DISPOSITION), client);
refresh_shortcut(client, item);
/* Oh, we're a child, let's deal with that */
@@ -560,19 +896,15 @@ new_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position, Dbus
if (g_strcmp0(dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_TYPE), DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) { return; }
gpointer ann_menu = g_object_get_data(G_OBJECT(mi), data_menu);
- GtkMenu * menu = GTK_MENU(ann_menu);
- if (menu == NULL) {
- /* Oh, we don't have a submenu, build one! */
- menu = GTK_MENU(gtk_menu_new());
- g_object_set_data(G_OBJECT(mi), data_menu, menu);
+ if (ann_menu == NULL) {
+ g_warning("Children but no menu, someone's been naughty with their '" DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "' property: '%s'", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY));
+ return;
+ }
- GtkMenuItem * parent = dbusmenu_gtkclient_menuitem_get(gtkclient, mi);
- gtk_menu_item_set_submenu(parent, GTK_WIDGET(menu));
- }
+ GtkMenu * menu = GTK_MENU(ann_menu);
GtkMenuItem * childmi = dbusmenu_gtkclient_menuitem_get(gtkclient, child);
gtk_menu_shell_insert(GTK_MENU_SHELL(menu), GTK_WIDGET(childmi), position);
- gtk_widget_show(GTK_WIDGET(menu));
return;
}
@@ -589,7 +921,7 @@ delete_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, DbusmenuGtkClient
if (menu != NULL) {
gtk_widget_destroy(GTK_WIDGET(menu));
- g_object_set_data(G_OBJECT(mi), data_menu, NULL);
+ g_object_steal_data(G_OBJECT(mi), data_menu);
}
}
@@ -617,15 +949,15 @@ move_child (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint new, guint ol
/* Public API */
/**
- dbusmenu_gtkclient_new:
- @dbus_name: Name of the #DbusmenuServer on DBus
- @dbus_name: Name of the object on the #DbusmenuServer
-
- Creates a new #DbusmenuGtkClient object and creates a #DbusmenuClient
- that connects across DBus to a #DbusmenuServer.
-
- Return value: A new #DbusmenuGtkClient sync'd with a server
-*/
+ * dbusmenu_gtkclient_new:
+ * @dbus_name: Name of the #DbusmenuServer on DBus
+ * @dbus_object: Name of the object on the #DbusmenuServer
+ *
+ * Creates a new #DbusmenuGtkClient object and creates a #DbusmenuClient
+ * that connects across DBus to a #DbusmenuServer.
+ *
+ * Return value: A new #DbusmenuGtkClient sync'd with a server
+ */
DbusmenuGtkClient *
dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object)
{
@@ -636,15 +968,15 @@ dbusmenu_gtkclient_new (gchar * dbus_name, gchar * dbus_object)
}
/**
- dbusmenu_gtkclient_menuitem_get:
- @client: A #DbusmenuGtkClient with the item in it.
- @item: #DbusmenuMenuitem to get associated #GtkMenuItem on.
-
- This grabs the #GtkMenuItem that is associated with the
- #DbusmenuMenuitem.
-
- Return value: The #GtkMenuItem that can be played with.
-*/
+ * dbusmenu_gtkclient_menuitem_get:
+ * @client: A #DbusmenuGtkClient with the item in it.
+ * @item: #DbusmenuMenuitem to get associated #GtkMenuItem on.
+ *
+ * This grabs the #GtkMenuItem that is associated with the
+ * #DbusmenuMenuitem.
+ *
+ * Return value: (transfer none): The #GtkMenuItem that can be played with.
+ */
GtkMenuItem *
dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem * item)
{
@@ -660,13 +992,13 @@ dbusmenu_gtkclient_menuitem_get (DbusmenuGtkClient * client, DbusmenuMenuitem *
}
/**
- dbusmenu_gtkclient_menuitem_get_submenu:
- @client: A #DbusmenuGtkClient with the item in it.
- @item: #DbusmenuMenuitem to get associated #GtkMenu on.
-
- This grabs the submenu associated with the menuitem.
-
- Return value: The #GtkMenu if there is one.
+ * dbusmenu_gtkclient_menuitem_get_submenu:
+ * @client: A #DbusmenuGtkClient with the item in it.
+ * @item: #DbusmenuMenuitem to get associated #GtkMenu on.
+ *
+ * This grabs the submenu associated with the menuitem.
+ *
+ * Return value: (transfer none): The #GtkMenu if there is one.
*/
GtkMenu *
dbusmenu_gtkclient_menuitem_get_submenu (DbusmenuGtkClient * client, DbusmenuMenuitem * item)
@@ -693,9 +1025,9 @@ new_item_normal (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusmenu
GtkMenuItem * gmi;
gmi = GTK_MENU_ITEM(g_object_new(GENERICMENUITEM_TYPE, NULL));
- gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL));
if (gmi != NULL) {
+ gtk_menu_item_set_label(gmi, dbusmenu_menuitem_property_get(newitem, DBUSMENU_MENUITEM_PROP_LABEL));
dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
} else {
return FALSE;
@@ -870,6 +1202,9 @@ image_property_handle (DbusmenuMenuitem * item, const gchar * property, GVariant
} else {
gtk_image_set_from_pixbuf(GTK_IMAGE(gtkimage), image);
}
+ if (image) {
+ g_object_unref(image);
+ }
}
}
diff --git a/libdbusmenu-gtk/client.h b/libdbusmenu-gtk/client.h
index c986a5d..75b59a0 100644
--- a/libdbusmenu-gtk/client.h
+++ b/libdbusmenu-gtk/client.h
@@ -41,20 +41,28 @@ G_BEGIN_DECLS
#define DBUSMENU_IS_GTKCLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_GTKCLIENT_TYPE))
#define DBUSMENU_GTKCLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_GTKCLIENT_TYPE, DbusmenuGtkClientClass))
+/**
+ * DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED:
+ *
+ * String to attach to signal #DbusmenuClient::root-changed
+ */
#define DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED
typedef struct _DbusmenuGtkClientPrivate DbusmenuGtkClientPrivate;
/**
- DbusmenuGtkClientClass:
- @parent_class: #GtkMenuClass
- @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.
-*/
+ * DbusmenuGtkClientClass:
+ * @parent_class: #GtkMenuClass
+ * @root_changed: Slot for signal #DbusmenuGtkClient::root-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.
+ * @reserved6: Reserved for future use.
+ *
+ * Functions and signal slots for using a #DbusmenuGtkClient
+ */
typedef struct _DbusmenuGtkClientClass DbusmenuGtkClientClass;
struct _DbusmenuGtkClientClass {
DbusmenuClientClass parent_class;
@@ -72,9 +80,11 @@ struct _DbusmenuGtkClientClass {
};
/**
- DbusmenuGtkClient:
- @parent: #GtkMenu
-*/
+ * DbusmenuGtkClient:
+ *
+ * A subclass of #DbusmenuClient to add functionality with regarding
+ * building GTK items out of the abstract tree.
+ */
typedef struct _DbusmenuGtkClient DbusmenuGtkClient;
struct _DbusmenuGtkClient {
DbusmenuClient parent;
@@ -94,10 +104,10 @@ GtkAccelGroup * dbusmenu_gtkclient_get_accel_group (DbusmenuGtkClient * client);
void dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuitem * item, GtkMenuItem * gmi, DbusmenuMenuitem * parent);
/**
- SECTION:gtkmenu
- @short_description: A GTK Menu Object that syncronizes over DBus
+ SECTION:client
+ @short_description: A subclass of #DbusmenuClient adding GTK level features
@stability: Unstable
- @include: libdbusmenu-gtk/menu.h
+ @include: libdbusmenu-gtk/client.h
In general, this is just a #GtkMenu, why else would you care? Oh,
because this menu is created by someone else on a server that exists
@@ -115,8 +125,6 @@ void dbusmenu_gtkclient_newitem_base (DbusmenuGtkClient * client, DbusmenuMenuit
number of entries change, the menus change, if they change thier
properties change, they update in the items. All of this should
be handled transparently to the user of this object.
-
- TODO: Document properties.
*/
G_END_DECLS
diff --git a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
index 8784556..9a1b460 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-2.0
Libs: -L${libdir} -ldbusmenu-gtk
Name: libdbusmenu-gtk
diff --git a/libdbusmenu-gtk/dbusmenu-gtk.h b/libdbusmenu-gtk/dbusmenu-gtk.h
index f2fe5be..de63c61 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk.h
+++ b/libdbusmenu-gtk/dbusmenu-gtk.h
@@ -36,6 +36,5 @@ License version 3 and version 2.1 along with this program. If not, see
#include <libdbusmenu-gtk/client.h>
#include <libdbusmenu-gtk/menu.h>
#include <libdbusmenu-gtk/menuitem.h>
-#include <libdbusmenu-gtk/serializablemenuitem.h>
#endif /* __DBUSMENU_GLIB_H__ */
diff --git a/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
index 804b13e..c297db3 100644
--- a/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
+++ b/libdbusmenu-gtk/dbusmenu-gtk3-0.4.pc.in
@@ -5,7 +5,7 @@ bindir=@bindir@
includedir=@includedir@
Cflags: -I${includedir}/libdbusmenu-0.4
-Requires: dbusmenu-glib-0.4
+Requires: dbusmenu-glib-0.4 gdk-pixbuf-2.0 gtk+-3.0
Libs: -L${libdir} -ldbusmenu-gtk3
Name: libdbusmenu-gtk3
diff --git a/libdbusmenu-gtk/genericmenuitem-enum-types.c.in b/libdbusmenu-gtk/genericmenuitem-enum-types.c.in
new file mode 100644
index 0000000..8b2c046
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem-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 "genericmenuitem-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-gtk/genericmenuitem-enum-types.h.in b/libdbusmenu-gtk/genericmenuitem-enum-types.h.in
new file mode 100644
index 0000000..5758438
--- /dev/null
+++ b/libdbusmenu-gtk/genericmenuitem-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-gtk/genericmenuitem.c b/libdbusmenu-gtk/genericmenuitem.c
index 2af70f3..5488f93 100644
--- a/libdbusmenu-gtk/genericmenuitem.c
+++ b/libdbusmenu-gtk/genericmenuitem.c
@@ -30,9 +30,11 @@ License version 3 and version 2.1 along with this program. If not, see
#include "config.h"
#endif
+#include <gdk/gdk.h>
+
#include "genericmenuitem.h"
-/**
+/*
GenericmenuitemPrivate:
@check_type: What type of check we have, or none at all.
@state: What the state of our check is.
@@ -40,6 +42,7 @@ License version 3 and version 2.1 along with this program. If not, see
struct _GenericmenuitemPrivate {
GenericmenuitemCheckType check_type;
GenericmenuitemState state;
+ GenericmenuitemDisposition disposition;
};
/* Private macro */
@@ -102,6 +105,7 @@ genericmenuitem_init (Genericmenuitem *self)
self->priv->check_type = GENERICMENUITEM_CHECK_TYPE_NONE;
self->priv->state = GENERICMENUITEM_STATE_UNCHECKED;
+ self->priv->disposition = GENERICMENUITEM_DISPOSITION_NORMAL;
return;
}
@@ -165,18 +169,61 @@ set_label_helper (GtkWidget * widget, gpointer data)
style. It should be considered for caching when
optimizing. */
static gint
-get_hpadding (GtkWidget * widget)
+get_toggle_space (GtkWidget * widget)
{
gint padding = 0;
- gtk_widget_style_get(widget, "horizontal-padding", &padding, NULL);
+ gtk_widget_style_get(widget, "toggle-spacing", &padding, NULL);
return padding;
}
+/* Get the value to put in the span for the disposition */
+static gchar *
+get_text_color (GenericmenuitemDisposition disposition, GtkWidget * widget)
+{
+ struct {const gchar * color_name; const gchar * default_color;} values[] = {
+ /* NORMAL */ { NULL, NULL},
+ /* INFO */ { "informational-color", "blue"},
+ /* WARN */ { "warning-color", "orange"},
+ /* ALERT */ { "error-color", "red"}
+ };
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+ GtkStyleContext * context = gtk_widget_get_style_context(widget);
+ GdkRGBA color;
+
+ if (gtk_style_context_lookup_color(context, values[disposition].color_name, &color)) {
+ return g_strdup_printf("rgb(%d, %d, %d)", (gint)(color.red * 255), (gint)(color.green * 255), (gint)(color.blue * 255));
+ }
+#endif
+
+ return g_strdup(values[disposition].default_color);
+}
+
/* Set the label on the item */
static void
-set_label (GtkMenuItem * menu_item, const gchar * label)
+set_label (GtkMenuItem * menu_item, const gchar * in_label)
{
- if (label == NULL) return;
+ if (in_label == NULL) return;
+
+ /* Build a label that might include the colors of the disposition
+ so that it gets rendered in the menuitem. */
+ gchar * local_label = NULL;
+ switch (GENERICMENUITEM(menu_item)->priv->disposition) {
+ case GENERICMENUITEM_DISPOSITION_NORMAL:
+ local_label = g_strdup(in_label);
+ break;
+ case GENERICMENUITEM_DISPOSITION_INFORMATIONAL:
+ case GENERICMENUITEM_DISPOSITION_WARNING:
+ case GENERICMENUITEM_DISPOSITION_ALERT: {
+ gchar * color = get_text_color(GENERICMENUITEM(menu_item)->priv->disposition, GTK_WIDGET(menu_item));
+ local_label = g_markup_printf_escaped("<span fgcolor=\"%s\">%s</span>", color, in_label);
+ g_free(color);
+ break;
+ }
+ default:
+ g_warn_if_reached();
+ break;
+ }
GtkWidget * child = gtk_bin_get_child(GTK_BIN(menu_item));
GtkLabel * labelw = NULL;
@@ -194,10 +241,10 @@ set_label (GtkMenuItem * menu_item, const gchar * label)
/* We need to put the child into a new box and
make the box the child of the menu item. Basically
we're inserting a box in the middle. */
- GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+ GtkWidget * hbox = gtk_hbox_new(FALSE, get_toggle_space(GTK_WIDGET(menu_item)));
g_object_ref(child);
gtk_container_remove(GTK_CONTAINER(menu_item), child);
- gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, get_hpadding(GTK_WIDGET(menu_item)));
+ gtk_box_pack_start(GTK_BOX(hbox), child, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(menu_item), hbox);
gtk_widget_show(hbox);
g_object_unref(child);
@@ -211,10 +258,12 @@ set_label (GtkMenuItem * menu_item, const gchar * label)
update the one that we already have. */
if (labelw == NULL) {
/* Build it */
- labelw = GTK_LABEL(gtk_accel_label_new(label));
+ labelw = GTK_LABEL(gtk_accel_label_new(local_label));
gtk_label_set_use_underline(GTK_LABEL(labelw), TRUE);
+ gtk_label_set_use_markup(GTK_LABEL(labelw), TRUE);
gtk_misc_set_alignment(GTK_MISC(labelw), 0.0, 0.5);
gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(labelw), GTK_WIDGET(menu_item));
+ gtk_label_set_markup_with_mnemonic(labelw, local_label);
gtk_widget_show(GTK_WIDGET(labelw));
/* Check to see if it needs to be in the bin for this
@@ -222,17 +271,17 @@ set_label (GtkMenuItem * menu_item, const gchar * label)
if (child == NULL) {
gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(labelw));
} else {
- gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, get_hpadding(GTK_WIDGET(menu_item)));
+ gtk_box_pack_end(GTK_BOX(child), GTK_WIDGET(labelw), TRUE, TRUE, 0);
}
} else {
/* Oh, just an update. No biggie. */
- if (!g_strcmp0(label, gtk_label_get_label(labelw))) {
+ if (!g_strcmp0(local_label, gtk_label_get_label(labelw))) {
/* The only reason to suppress the update is if we had
a label and the value was the same as the one we're
getting in. */
suppress_update = TRUE;
} else {
- gtk_label_set_label(labelw, label);
+ gtk_label_set_markup_with_mnemonic(labelw, local_label);
}
}
@@ -241,6 +290,12 @@ set_label (GtkMenuItem * menu_item, const gchar * label)
g_object_notify(G_OBJECT(menu_item), "label");
}
+ /* Clean up this */
+ if (local_label != NULL) {
+ g_free(local_label);
+ local_label = NULL;
+ }
+
return;
}
@@ -278,12 +333,12 @@ activate (GtkMenuItem * menu_item)
}
/**
- genericmenuitem_set_check_type:
- @item: #Genericmenuitem to set the type on
- @check_type: Which type of check should be displayed
-
- This function changes the type of the checkmark that
- appears in the left hand gutter for the menuitem.
+ * genericmenuitem_set_check_type:
+ * @item: #Genericmenuitem to set the type on
+ * @check_type: Which type of check should be displayed
+ *
+ * This function changes the type of the checkmark that
+ * appears in the left hand gutter for the menuitem.
*/
void
genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType check_type)
@@ -317,14 +372,14 @@ genericmenuitem_set_check_type (Genericmenuitem * item, GenericmenuitemCheckType
}
/**
- genericmenuitem_set_state:
- @item: #Genericmenuitem to set the type on
- @check_type: What is the state of the check
-
- Sets the state of the check in the menu item. It does
- not require, but isn't really useful if the type of
- check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
-*/
+ * genericmenuitem_set_state:
+ * @item: #Genericmenuitem to set the type on
+ * @check_type: What is the state of the check
+ *
+ * Sets the state of the check in the menu item. It does
+ * not require, but isn't really useful if the type of
+ * check that the menuitem is set to #GENERICMENUITEM_CHECK_TYPE_NONE.
+ */
void
genericmenuitem_set_state (Genericmenuitem * item, GenericmenuitemState state)
{
@@ -377,11 +432,11 @@ set_image_helper (GtkWidget * widget, gpointer data)
}
/**
- genericmenuitem_set_image:
- @item: A #Genericmenuitem
- @image: The image to set as the image of @item
-
- Sets the image of the menu item.
+ * genericmenuitem_set_image:
+ * @item: A #Genericmenuitem
+ * @image: The image to set as the image of @item
+ *
+ * Sets the image of the menu item.
*/
void
genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
@@ -401,10 +456,10 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
/* We need to put the child into a new box and
make the box the child of the menu item. Basically
we're inserting a box in the middle. */
- GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
+ GtkWidget * hbox = gtk_hbox_new(FALSE, get_toggle_space(GTK_WIDGET(menu_item)));
g_object_ref(child);
gtk_container_remove(GTK_CONTAINER(menu_item), child);
- gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, get_hpadding(GTK_WIDGET(menu_item)));
+ gtk_box_pack_end(GTK_BOX(hbox), child, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(menu_item), hbox);
gtk_widget_show(hbox);
g_object_unref(child);
@@ -429,7 +484,7 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
if (child == NULL) {
gtk_container_add(GTK_CONTAINER(menu_item), GTK_WIDGET(image));
} else {
- gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, get_hpadding(GTK_WIDGET(menu_item)));
+ gtk_box_pack_start(GTK_BOX(child), GTK_WIDGET(image), FALSE, FALSE, 0);
}
gtk_widget_show(image);
@@ -439,13 +494,13 @@ genericmenuitem_set_image (Genericmenuitem * menu_item, GtkWidget * image)
}
/**
- genericmenuitem_get_image:
- @item: A #Genericmenuitem
-
- Returns the image if there is one.
-
- Return value: A pointer to the image of the item or #NULL
- if there isn't one.
+ * genericmenuitem_get_image:
+ * @item: A #Genericmenuitem
+ *
+ * Returns the image if there is one.
+ *
+ * Return value: (transfer none): A pointer to the image of the item or #NULL
+ * if there isn't one.
*/
GtkWidget *
genericmenuitem_get_image (Genericmenuitem * menu_item)
@@ -466,3 +521,41 @@ genericmenuitem_get_image (Genericmenuitem * menu_item)
return imagew;
}
+
+/**
+ * genericmenuitem_set_disposition:
+ * @item: A #Genericmenuitem
+ * @disposition: The disposition of the item
+ *
+ * Sets the disposition of the menuitem.
+ */
+void
+genericmenuitem_set_disposition (Genericmenuitem * item, GenericmenuitemDisposition disposition)
+{
+ g_return_if_fail(IS_GENERICMENUITEM(item));
+
+ if (item->priv->disposition == disposition)
+ return;
+
+ item->priv->disposition = disposition;
+
+ set_label(GTK_MENU_ITEM(item), get_label(GTK_MENU_ITEM(item)));
+
+ return;
+}
+
+/**
+ * genericmenuitem_get_disposition:
+ * @item: A #Genericmenuitem
+ *
+ * Gets the disposition of the menuitem.
+ *
+ * Return value: The disposition of the menuitem.
+ */
+GenericmenuitemDisposition
+genericmenuitem_get_disposition (Genericmenuitem * item)
+{
+ g_return_val_if_fail(IS_GENERICMENUITEM(item), GENERICMENUITEM_DISPOSITION_NORMAL);
+
+ return item->priv->disposition;
+}
diff --git a/libdbusmenu-gtk/genericmenuitem.h b/libdbusmenu-gtk/genericmenuitem.h
index 3c4af0c..0b7df55 100644
--- a/libdbusmenu-gtk/genericmenuitem.h
+++ b/libdbusmenu-gtk/genericmenuitem.h
@@ -42,13 +42,11 @@ G_BEGIN_DECLS
#define IS_GENERICMENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GENERICMENUITEM_TYPE))
#define GENERICMENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GENERICMENUITEM_TYPE, GenericmenuitemClass))
-typedef struct _Genericmenuitem Genericmenuitem;
-typedef struct _GenericmenuitemClass GenericmenuitemClass;
-typedef struct _GenericmenuitemPrivate GenericmenuitemPrivate;
-typedef enum _GenericmenuitemCheckType GenericmenuitemCheckType;
-typedef enum _GenericmenuitemState GenericmenuitemState;
+typedef struct _Genericmenuitem Genericmenuitem;
+typedef struct _GenericmenuitemClass GenericmenuitemClass;
+typedef struct _GenericmenuitemPrivate GenericmenuitemPrivate;
-/**
+/*
GenericmenuitemClass:
@parent_class: Our parent #GtkCheckMenuItemClass
*/
@@ -56,7 +54,7 @@ struct _GenericmenuitemClass {
GtkCheckMenuItemClass parent_class;
};
-/**
+/*
Genericmenuitem:
@parent: Our parent #GtkCheckMenuItem
*/
@@ -65,26 +63,61 @@ struct _Genericmenuitem {
GenericmenuitemPrivate * priv;
};
-enum _GenericmenuitemCheckType {
+/**
+ * GenericmenuitemCheckType:
+ * @GENERICMENUITEM_CHECK_TYPE_NONE: No check
+ * @GENERICMENUITEM_CHECK_TYPE_CHECKBOX: Nice little check
+ * @GENERICMENUITEM_CHECK_TYPE_RADIO: Radio button
+ *
+ * Tracks what type of checkmark should be shown on the item
+ */
+typedef enum { /*< prefix=GENERICMENUITEM_CHECK_TYPE >*/
GENERICMENUITEM_CHECK_TYPE_NONE,
GENERICMENUITEM_CHECK_TYPE_CHECKBOX,
GENERICMENUITEM_CHECK_TYPE_RADIO
-};
+} GenericmenuitemCheckType;
-enum _GenericmenuitemState {
+/**
+ * GenericmenuitemState:
+ * @GENERICMENUITEM_STATE_UNCHECKED: No check visisble
+ * @GENERICMENUITEM_STATE_CHECKED: Check visible
+ * @GENERICMENUITEM_STATE_INDETERMINATE: We have no clue
+ *
+ * What the state of the check mark on the item is
+ */
+typedef enum { /*< prefix=GENERICMENUITEM_STATE >*/
GENERICMENUITEM_STATE_UNCHECKED,
GENERICMENUITEM_STATE_CHECKED,
GENERICMENUITEM_STATE_INDETERMINATE
-};
+} GenericmenuitemState;
-GType genericmenuitem_get_type (void);
-void genericmenuitem_set_check_type (Genericmenuitem * item,
- GenericmenuitemCheckType check_type);
-void genericmenuitem_set_state (Genericmenuitem * item,
- GenericmenuitemState state);
-void genericmenuitem_set_image (Genericmenuitem * item,
- GtkWidget * image);
-GtkWidget * genericmenuitem_get_image (Genericmenuitem * item);
+/**
+ * GenericmenuitemDisposition:
+ * @GENERICMENUITEM_DISPOSITION_NORMAL: Normal state
+ * @GENERICMENUITEM_DISPOSITION_INFORMATIONAL: Item is informational
+ * @GENERICMENUITEM_DISPOSITION_WARNING: Oh, you should watch out for this one
+ * @GENERICMENUITEM_DISPOSITION_ALERT: Boom!
+ *
+ * What the disposition of the menu item is
+ */
+typedef enum { /*< prefix=GENERICMENUITEM_DISPOSITION >*/
+ GENERICMENUITEM_DISPOSITION_NORMAL,
+ GENERICMENUITEM_DISPOSITION_INFORMATIONAL,
+ GENERICMENUITEM_DISPOSITION_WARNING,
+ GENERICMENUITEM_DISPOSITION_ALERT
+} GenericmenuitemDisposition;
+
+GType genericmenuitem_get_type (void);
+void genericmenuitem_set_check_type (Genericmenuitem * item,
+ GenericmenuitemCheckType check_type);
+void genericmenuitem_set_state (Genericmenuitem * item,
+ GenericmenuitemState state);
+void genericmenuitem_set_image (Genericmenuitem * item,
+ GtkWidget * image);
+GtkWidget * genericmenuitem_get_image (Genericmenuitem * item);
+void genericmenuitem_set_disposition (Genericmenuitem * item,
+ GenericmenuitemDisposition disposition);
+GenericmenuitemDisposition genericmenuitem_get_disposition (Genericmenuitem * item);
G_END_DECLS
diff --git a/libdbusmenu-gtk/menu.c b/libdbusmenu-gtk/menu.c
index 9c4f7f8..236a596 100644
--- a/libdbusmenu-gtk/menu.c
+++ b/libdbusmenu-gtk/menu.c
@@ -46,6 +46,7 @@ enum {
/* Private */
struct _DbusmenuGtkMenuPrivate {
DbusmenuGtkClient * client;
+ DbusmenuMenuitem * root;
gchar * dbus_object;
gchar * dbus_name;
@@ -63,6 +64,8 @@ static void get_property (GObject * obj, guint id, GValue * value, GParamSpec *
/* Internal */
static void build_client (DbusmenuGtkMenu * self);
static void child_realized (DbusmenuMenuitem * child, gpointer userdata);
+static void remove_child_signals (gpointer data, gpointer user_data);
+static void root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu);
/* GObject Stuff */
G_DEFINE_TYPE (DbusmenuGtkMenu, dbusmenu_gtkmenu, GTK_TYPE_MENU);
@@ -100,7 +103,7 @@ menu_focus_cb(DbusmenuGtkMenu * menu, gpointer userdata)
if (priv->client != NULL) {
/* TODO: We should stop the display of the menu
until the about to show call returns. */
- dbusmenu_client_send_about_to_show(DBUSMENU_CLIENT(priv->client), 0, NULL, NULL);
+ dbusmenu_menuitem_send_about_to_show(priv->root, NULL, NULL);
}
return;
}
@@ -127,6 +130,12 @@ dbusmenu_gtkmenu_dispose (GObject *object)
{
DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(object);
+ /* Remove signals from the root */
+ if (priv->root != NULL) {
+ /* This will clear the root */
+ root_changed(priv->client, NULL, DBUSMENU_GTKMENU(object));
+ }
+
if (priv->client != NULL) {
g_object_unref(G_OBJECT(priv->client));
priv->client = NULL;
@@ -271,6 +280,10 @@ root_child_delete (DbusmenuMenuitem * root, DbusmenuMenuitem * child, DbusmenuGt
#ifdef MASSIVEDEBUGGING
g_debug("Root child deleted");
#endif
+
+ /* Remove signal for realized */
+ remove_child_signals(child, menu);
+
DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
GtkWidget * item = GTK_WIDGET(dbusmenu_gtkclient_menuitem_get(priv->client, child));
if (item != NULL) {
@@ -308,15 +321,55 @@ child_realized (DbusmenuMenuitem * child, gpointer userdata)
return;
}
+/* Remove any signals we attached to children -- just realized right now */
+static void
+remove_child_signals (gpointer data, gpointer user_data)
+{
+ g_signal_handlers_disconnect_by_func(G_OBJECT(data), child_realized, user_data);
+ return;
+}
+
+/* Handler for all of the menu items on a root change to ensure that
+ the menus are hidden before we start going and deleting things. */
+static void
+popdown_all (DbusmenuMenuitem * mi, gpointer user_data)
+{
+ GtkMenu * menu = dbusmenu_gtkclient_menuitem_get_submenu(DBUSMENU_GTKCLIENT(user_data), mi);
+ if (menu != NULL) {
+ gtk_menu_popdown(menu);
+ }
+ return;
+}
+
/* When the root menuitem changes we need to resetup things so that
we're back in the game. */
static void
root_changed (DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, DbusmenuGtkMenu * menu) {
+ DbusmenuGtkMenuPrivate * priv = DBUSMENU_GTKMENU_GET_PRIVATE(menu);
+
+ /* Clear out our interest in the old root */
+ if (priv->root != NULL) {
+ GList * children = dbusmenu_menuitem_get_children(priv->root);
+ g_list_foreach(children, remove_child_signals, menu);
+
+ g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_added, menu);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_moved, menu);
+ g_signal_handlers_disconnect_by_func(G_OBJECT(priv->root), root_child_delete, menu);
+
+ dbusmenu_menuitem_foreach(priv->root, popdown_all, client);
+
+ g_object_unref(priv->root);
+ priv->root = NULL;
+ }
+
if (newroot == NULL) {
gtk_widget_hide(GTK_WIDGET(menu));
return;
}
+ priv->root = newroot;
+ g_object_ref(priv->root);
+
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(root_child_added), menu);
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(root_child_moved), menu);
g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(root_child_delete), menu);
@@ -359,15 +412,15 @@ build_client (DbusmenuGtkMenu * self)
/* Public API */
/**
- dbusmenu_gtkmenu_new:
- @dbus_name: Name of the #DbusmenuServer on DBus
- @dbus_name: Name of the object on the #DbusmenuServer
-
- Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
- that connects across DBus to a #DbusmenuServer.
-
- Return value: A new #DbusmenuGtkMenu sync'd with a server
-*/
+ * dbusmenu_gtkmenu_new:
+ * @dbus_name: Name of the #DbusmenuServer on DBus
+ * @dbus_object: Name of the object on the #DbusmenuServer
+ *
+ * Creates a new #DbusmenuGtkMenu object and creates a #DbusmenuClient
+ * that connects across DBus to a #DbusmenuServer.
+ *
+ * Return value: A new #DbusmenuGtkMenu sync'd with a server
+ */
DbusmenuGtkMenu *
dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
{
@@ -378,14 +431,14 @@ dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object)
}
/**
- dbusmenu_gtkmenu_get_client:
- @menu: The #DbusmenuGtkMenu to get the client from
-
- An accessor for the client that this menu is using to
- communicate with the server.
-
- Return value: A valid #DbusmenuGtkClient or NULL on error.
-*/
+ * dbusmenu_gtkmenu_get_client:
+ * @menu: The #DbusmenuGtkMenu to get the client from
+ *
+ * An accessor for the client that this menu is using to
+ * communicate with the server.
+ *
+ * Return value: (transfer none): A valid #DbusmenuGtkClient or NULL on error.
+ */
DbusmenuGtkClient *
dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu)
{
diff --git a/libdbusmenu-gtk/menu.h b/libdbusmenu-gtk/menu.h
index 896e466..c413ab8 100644
--- a/libdbusmenu-gtk/menu.h
+++ b/libdbusmenu-gtk/menu.h
@@ -45,15 +45,18 @@ G_BEGIN_DECLS
typedef struct _DbusmenuGtkMenuPrivate DbusmenuGtkMenuPrivate;
/**
- DbusmenuGtkMenuClass:
- @parent_class: #GtkMenuClass
- @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.
-*/
+ * DbusmenuGtkMenuClass:
+ * @parent_class: #GtkMenuClass
+ * @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.
+ *
+ * All of the subclassable functions and signal slots for a
+ * #DbusmenuGtkMenu.
+ */
typedef struct _DbusmenuGtkMenuClass DbusmenuGtkMenuClass;
struct _DbusmenuGtkMenuClass {
GtkMenuClass parent_class;
@@ -68,9 +71,11 @@ struct _DbusmenuGtkMenuClass {
};
/**
- DbusmenuGtkMenu:
- @parent: #GtkMenu
-*/
+ * DbusmenuGtkMenu:
+ *
+ * A #GtkMenu that is built using an abstract tree built from
+ * a #DbusmenuGtkClient.
+ */
typedef struct _DbusmenuGtkMenu DbusmenuGtkMenu;
struct _DbusmenuGtkMenu {
GtkMenu parent;
@@ -84,7 +89,7 @@ DbusmenuGtkMenu * dbusmenu_gtkmenu_new (gchar * dbus_name, gchar * dbus_object);
DbusmenuGtkClient * dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu);
/**
- SECTION:gtkmenu
+ SECTION:menu
@short_description: A GTK Menu Object that syncronizes over DBus
@stability: Unstable
@include: libdbusmenu-gtk/menu.h
@@ -105,8 +110,6 @@ DbusmenuGtkClient * dbusmenu_gtkmenu_get_client (DbusmenuGtkMenu * menu);
number of entries change, the menus change, if they change thier
properties change, they update in the items. All of this should
be handled transparently to the user of this object.
-
- TODO: Document properties.
*/
G_END_DECLS
diff --git a/libdbusmenu-gtk/menuitem.c b/libdbusmenu-gtk/menuitem.c
index fa5eb89..0f511bc 100644
--- a/libdbusmenu-gtk/menuitem.c
+++ b/libdbusmenu-gtk/menuitem.c
@@ -31,17 +31,17 @@ License version 3 and version 2.1 along with this program. If not, see
#include <gtk/gtk.h>
/**
- dbusmenu_menuitem_property_set_image:
- @menuitem: The #DbusmenuMenuitem to set the property on.
- @property: Name of the property to set.
- @data: The image to place on the property.
-
- This function takes the pixbuf that is stored in @data and
- turns it into a base64 encoded PNG so that it can be placed
- onto a standard #DbusmenuMenuitem property.
-
- Return value: Whether the function was able to set the property
- or not.
+ * dbusmenu_menuitem_property_set_image:
+ * @menuitem: The #DbusmenuMenuitem to set the property on.
+ * @property: Name of the property to set.
+ * @data: The image to place on the property.
+ *
+ * This function takes the pixbuf that is stored in @data and
+ * turns it into a base64 encoded PNG so that it can be placed
+ * onto a standard #DbusmenuMenuitem property.
+ *
+ * Return value: Whether the function was able to set the property
+ * or not.
*/
gboolean
dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar * property, const GdkPixbuf * data)
@@ -77,17 +77,17 @@ dbusmenu_menuitem_property_set_image (DbusmenuMenuitem * menuitem, const gchar *
}
/**
- dbusmenu_menuitem_property_get_image:
- @menuitem: The #DbusmenuMenuite to look for the property on
- @property: The name of the property to look for.
-
- This function looks on the menu item for a property by the
- name of @property. If one exists it tries to turn it into
- a #GdkPixbuf. It assumes that the property is a base64 encoded
- PNG file like the one created by #dbusmenu_menuite_property_set_image.
-
- Return value: A pixbuf or #NULL to signal error.
-*/
+ * dbusmenu_menuitem_property_get_image:
+ * @menuitem: The #DbusmenuMenuitem to look for the property on
+ * @property: The name of the property to look for.
+ *
+ * This function looks on the menu item for a property by the
+ * name of @property. If one exists it tries to turn it into
+ * a #GdkPixbuf. It assumes that the property is a base64 encoded
+ * PNG file like the one created by #dbusmenu_menuite_property_set_image.
+ *
+ * Return value: (transfer full): A pixbuf or #NULL to signal error.
+ */
GdkPixbuf *
dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar * property)
{
@@ -119,28 +119,23 @@ dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, const gchar *
g_error_free(error);
}
- error = NULL;
- g_input_stream_close(input, NULL, &error);
- if (error != NULL) {
- g_warning("Unable to close input stream: %s", error->message);
- g_error_free(error);
- }
+ g_object_unref(input);
g_free(icondata);
return icon;
}
/**
- dbusmenu_menuitem_property_set_shortcut_string:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @shortcut: String describing the shortcut
-
- This function takes a GTK shortcut string as defined in
- #gtk_accelerator_parse and turns that into the information
- required to send it over DBusmenu.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut_string:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @shortcut: String describing the shortcut
+ *
+ * This function takes a GTK shortcut string as defined in
+ * #gtk_accelerator_parse and turns that into the information
+ * required to send it over DBusmenu.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut)
{
@@ -161,22 +156,25 @@ dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, con
}
/**
- dbusmenu_menuitem_property_set_shortcut:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @key: The keycode of the key to send
- @modifier: A bitmask of modifiers used to activate the item
-
- Takes the modifer described by @key and @modifier and places that into
- the format sending across Dbus for shortcuts.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @key: The keycode of the key to send
+ * @modifier: A bitmask of modifiers used to activate the item
+ *
+ * Takes the modifer described by @key and @modifier and places that into
+ * the format sending across Dbus for shortcuts.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier)
{
g_return_val_if_fail(DBUSMENU_IS_MENUITEM(menuitem), FALSE);
g_return_val_if_fail(gtk_accelerator_valid(key, modifier), FALSE);
+ const gchar * keyname = gdk_keyval_name(key);
+ g_return_val_if_fail(keyname != NULL, FALSE);
+
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
@@ -193,7 +191,6 @@ dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key,
g_variant_builder_add(&builder, "s", DBUSMENU_MENUITEM_SHORTCUT_SUPER);
}
- const gchar * keyname = gdk_keyval_name(key);
g_variant_builder_add(&builder, "s", keyname);
GVariant * inside = g_variant_builder_end(&builder);
@@ -213,16 +210,16 @@ find_closure (GtkAccelKey * key, GClosure * closure, gpointer user_data)
}
/**
- dbusmenu_menuitem_property_set_shortcut_menuitem:
- @menuitem: The #DbusmenuMenuitem to set the shortcut on
- @gmi: A menu item to steal the shortcut off of
-
- Takes the shortcut that is installed on a menu item and calls
- #dbusmenu_menuitem_property_set_shortcut with it. It also sets
- up listeners to watch it change.
-
- Return value: Whether it was successful at setting the property.
-*/
+ * dbusmenu_menuitem_property_set_shortcut_menuitem:
+ * @menuitem: The #DbusmenuMenuitem to set the shortcut on
+ * @gmi: A menu item to steal the shortcut off of
+ *
+ * Takes the shortcut that is installed on a menu item and calls
+ * #dbusmenu_menuitem_property_set_shortcut with it. It also sets
+ * up listeners to watch it change.
+ *
+ * Return value: Whether it was successful at setting the property.
+ */
gboolean
dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi)
{
@@ -239,8 +236,15 @@ dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, c
NULL);
}
- if (closure == NULL)
- return FALSE;
+ if (closure == NULL) {
+ /* As a fallback, check for a closure in the related menu item. This
+ actually happens with SWT menu items. */
+ GList * closures = gtk_widget_list_accel_closures (GTK_WIDGET (gmi));
+ if (closures == NULL)
+ return FALSE;
+ closure = closures->data;
+ g_list_free (closures);
+ }
GtkAccelGroup * group = gtk_accel_group_from_accel_closure(closure);
@@ -260,17 +264,28 @@ dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, c
}
/**
- dbusmenu_menuitem_property_get_shortcut:
- @menuitem: The #DbusmenuMenuitem to get the shortcut off
- @key: Location to put the key value
- @modifier: Location to put the modifier mask
-
- This function gets a GTK shortcut as a key and a mask
- for use to set the accelerators.
-*/
+ * dbusmenu_menuitem_property_get_shortcut:
+ * @menuitem: The #DbusmenuMenuitem to get the shortcut off
+ * @key: (out): Location to put the key value
+ * @modifier: (out): Location to put the modifier mask
+ *
+ * This function gets a GTK shortcut as a key and a mask
+ * for use to set the accelerators.
+ */
void
dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier)
{
+ guint dummykey;
+ GdkModifierType dummymodifier;
+
+ if (key == NULL) {
+ key = &dummykey;
+ }
+
+ if (modifier == NULL) {
+ modifier = &dummymodifier;
+ }
+
*key = 0;
*modifier = 0;
@@ -283,15 +298,15 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke
if (g_variant_n_children(wrapper) != 1) {
g_warning("Unable to parse shortcut, too many keys");
- g_variant_unref(wrapper);
return;
}
GVariantIter iter;
- g_variant_iter_init(&iter, g_variant_get_child_value(wrapper, 0));
+ GVariant * child = g_variant_get_child_value(wrapper, 0);
+ g_variant_iter_init(&iter, child);
gchar * string;
- while(g_variant_iter_next(&iter, "s", &string)) {
+ while(g_variant_iter_loop(&iter, "s", &string)) {
if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_CONTROL) == 0) {
*modifier |= GDK_CONTROL_MASK;
} else if (g_strcmp0(string, DBUSMENU_MENUITEM_SHORTCUT_ALT) == 0) {
@@ -304,10 +319,10 @@ dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * ke
GdkModifierType tempmod;
gtk_accelerator_parse(string, key, &tempmod);
}
-
- g_free(string);
}
+ g_variant_unref(child);
+
return;
}
diff --git a/libdbusmenu-gtk/menuitem.h b/libdbusmenu-gtk/menuitem.h
index 4fc42f9..41f2187 100644
--- a/libdbusmenu-gtk/menuitem.h
+++ b/libdbusmenu-gtk/menuitem.h
@@ -43,8 +43,19 @@ GdkPixbuf * dbusmenu_menuitem_property_get_image (DbusmenuMenuitem * menuitem, c
gboolean dbusmenu_menuitem_property_set_shortcut (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier);
gboolean dbusmenu_menuitem_property_set_shortcut_string (DbusmenuMenuitem * menuitem, const gchar * shortcut);
gboolean dbusmenu_menuitem_property_set_shortcut_menuitem (DbusmenuMenuitem * menuitem, const GtkMenuItem * gmi);
-void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifiers);
-
+void dbusmenu_menuitem_property_get_shortcut (DbusmenuMenuitem * menuitem, guint * key, GdkModifierType * modifier);
+
+/**
+ SECTION:menuitem
+ @short_description: Helpers for #DbusmenuMenuitem properties that require a GTK dependency
+ @stability: Unstable
+ @include: libdbusmenu-gtk/menuitem.h
+
+ Some property helpers can't be done without picking up a GTK+
+ dependency. So those sit in libdbusmenu-gtk but have very similar
+ prototypes to the code in libdbusmenu-glib so your code will
+ look consistent, just with the extra depedency.
+*/
G_END_DECLS
#endif
diff --git a/libdbusmenu-gtk/parser.c b/libdbusmenu-gtk/parser.c
index 5d71585..3243c81 100644
--- a/libdbusmenu-gtk/parser.c
+++ b/libdbusmenu-gtk/parser.c
@@ -28,9 +28,19 @@ License version 3 and version 2.1 along with this program. If not, see
#include "parser.h"
#include "menuitem.h"
-#include "serializablemenuitem.h"
+#include "client.h"
#define CACHED_MENUITEM "dbusmenu-gtk-parser-cached-item"
+#define PARSER_DATA "dbusmenu-gtk-parser-data"
+
+typedef struct _ParserData
+{
+ GtkWidget *label;
+ GtkAction *action;
+ GtkWidget *widget;
+ GtkWidget *shell;
+ GtkWidget *image;
+} ParserData;
typedef struct _RecurseContext
{
@@ -42,83 +52,289 @@ static void parse_menu_structure_helper (GtkWidget * widget, RecurseContext * re
static DbusmenuMenuitem * construct_dbusmenu_for_widget (GtkWidget * widget);
static void accel_changed (GtkWidget * widget,
gpointer data);
-static gboolean update_stock_item (DbusmenuMenuitem * menuitem,
- GtkWidget * widget);
static void checkbox_toggled (GtkWidget * widget,
DbusmenuMenuitem * mi);
-static void update_icon_name (DbusmenuMenuitem * menuitem,
- GtkWidget * widget);
+static void update_icon (DbusmenuMenuitem * menuitem,
+ GtkImage * image);
static GtkWidget * find_menu_label (GtkWidget * widget);
static void label_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
+static void image_notify_cb (GtkWidget * widget,
+ GParamSpec * pspec,
+ gpointer data);
static void action_notify_cb (GtkAction * action,
GParamSpec * pspec,
gpointer data);
+static void child_added_cb (GtkContainer * menu,
+ GtkWidget * widget,
+ gpointer data);
+static void child_removed_cb (GtkContainer * menu,
+ GtkWidget * widget,
+ gpointer data);
+static void theme_changed_cb (GtkIconTheme * theme,
+ gpointer data);
static void item_activated (DbusmenuMenuitem * item,
guint timestamp,
gpointer user_data);
static gboolean item_about_to_show (DbusmenuMenuitem * item,
gpointer user_data);
+static gboolean item_handle_event (DbusmenuMenuitem * item,
+ const gchar * name,
+ GVariant * variant,
+ guint timestamp,
+ GtkWidget * widget);
static void widget_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
+static void widget_add_cb (GtkWidget * widget,
+ GtkWidget * child,
+ gpointer data);
static gboolean should_show_image (GtkImage * image);
static void menuitem_notify_cb (GtkWidget * widget,
GParamSpec * pspec,
gpointer data);
/**
- dbusmenu_gtk_parse_menu_structure:
- @widget: A #GtkMenuItem or #GtkMenuShell to turn into a #DbusmenuMenuitem
+ * dbusmenu_gtk_parse_menu_structure:
+ * @widget: A #GtkMenuItem or #GtkMenuShell to turn into a #DbusmenuMenuitem
+ *
+ * Goes through the GTK structures and turns them into the appropraite
+ * Dbusmenu structures along with setting up all the relationships
+ * between the objects. It also stores the dbusmenu items as a cache
+ * on the GTK items so that they'll be reused if necissary.
+ *
+ * Return value: (transfer full): A dbusmenu item representing the menu structure
+ */
+DbusmenuMenuitem *
+dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
+{
+ g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) || GTK_IS_MENU_SHELL(widget), NULL);
- Goes through the GTK structures and turns them into the appropraite
- Dbusmenu structures along with setting up all the relationships
- between the objects. It also stores the dbusmenu items as a cache
- on the GTK items so that they'll be reused if necissary.
+ DbusmenuMenuitem * returnval = NULL;
+ gpointer data = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
- Return value: A dbusmenu item representing the menu structure
-*/
+ if (data == NULL) {
+ RecurseContext recurse = {0};
+
+ recurse.toplevel = gtk_widget_get_toplevel(widget);
+
+ parse_menu_structure_helper(widget, &recurse);
+
+ returnval = recurse.parent;
+ } else {
+ returnval = DBUSMENU_MENUITEM(data);
+ g_object_ref(G_OBJECT(returnval));
+ }
+
+ return returnval;
+}
+
+/**
+ * dbusmenu_gtk_parse_get_cached_item:
+ * @widget: A #GtkMenuItem that may have a cached #DbusmenuMenuitem from the parser
+ *
+ * The Dbusmenu GTK parser adds cached items on the various
+ * menu items throughout the tree. Sometimes it can be useful
+ * to get that cached item to use directly. This function
+ * will retrieve it for you.
+ *
+ * Return value: (transfer none): A pointer to the cached item
+ * or NULL if it isn't there.
+ */
DbusmenuMenuitem *
-dbusmenu_gtk_parse_menu_structure (GtkWidget * widget)
+dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget)
{
- g_return_val_if_fail(GTK_IS_MENU_ITEM(widget) || GTK_IS_MENU_SHELL(widget), NULL);
+ if (!GTK_IS_MENU_ITEM(widget)) {
+ return NULL;
+ }
+ return DBUSMENU_MENUITEM(g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM));
+}
- RecurseContext recurse = {0};
+static void
+parse_data_free (gpointer data)
+{
+ ParserData *pdata = (ParserData *)data;
+
+ if (pdata != NULL && pdata->label != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->label, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(label_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->label), (gpointer*)&pdata->label);
+ }
+
+ if (pdata != NULL && pdata->action != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->action, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(action_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->action), (gpointer*)&pdata->action);
+ }
- recurse.toplevel = gtk_widget_get_toplevel(widget);
+ if (pdata != NULL && pdata->widget != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(widget_notify_cb), NULL);
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(widget_add_cb), NULL);
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(accel_changed), NULL);
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(checkbox_toggled), NULL);
+ g_signal_handlers_disconnect_matched(pdata->widget, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(menuitem_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->widget), (gpointer*)&pdata->widget);
+ }
+
+ if (pdata != NULL && pdata->shell != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->shell, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(child_added_cb), NULL);
+ g_signal_handlers_disconnect_matched(pdata->shell, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(child_removed_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->shell), (gpointer*)&pdata->shell);
+ }
+
+ if (pdata != NULL && pdata->image != NULL) {
+ g_signal_handlers_disconnect_matched(pdata->image, (GSignalMatchType)G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, G_CALLBACK(image_notify_cb), NULL);
+ g_object_remove_weak_pointer(G_OBJECT(pdata->image), (gpointer*)&pdata->image);
+ }
- parse_menu_structure_helper(widget, &recurse);
+ g_free(pdata);
- return recurse.parent;
+ return;
+}
+
+static void
+widget_freed (gpointer data, GObject * obj)
+{
+ g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), obj);
+
+ return;
}
/* Called when the dbusmenu item that we're keeping around
is finalized */
static void
-dbusmenu_cache_freed (gpointer data, GObject * obj)
+dbusmenu_item_freed (gpointer data, GObject * obj)
{
- /* If the dbusmenu item is killed we don't need to remove
- the weak ref as well. */
- g_object_steal_data(G_OBJECT(data), CACHED_MENUITEM);
- g_signal_handlers_disconnect_by_func(data, G_CALLBACK(widget_notify_cb), obj);
- return;
+ ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(obj), PARSER_DATA);
+
+ if (pdata != NULL && pdata->widget != NULL) {
+ g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), pdata->widget);
+ g_object_steal_data(G_OBJECT(pdata->widget), CACHED_MENUITEM);
+ g_object_weak_unref(G_OBJECT(pdata->widget), widget_freed, NULL);
+ }
+}
+
+/* Gets the positon of the child with its' parent if it has one.
+ Returns -1 if the position is unable to be calculated. */
+static gint
+get_child_position (GtkWidget * child)
+{
+ GtkWidget * parent = gtk_widget_get_parent (child);
+ if (parent == NULL || !GTK_IS_CONTAINER (parent))
+ return -1;
+
+ GList * children = gtk_container_get_children (GTK_CONTAINER (parent));
+ GList * iter;
+ gint position = 0;
+
+ for (iter = children; iter != NULL; iter = iter->next) {
+ if (iter->data == child)
+ break;
+ ++position;
+ }
+
+ g_list_free (children);
+
+ if (iter == NULL)
+ return -1;
+ else
+ return position;
+}
+
+/* Creates a new menu item that is attached to the widget and has
+ the data linkages hooked up. Also allocates the ParserData */
+static DbusmenuMenuitem *
+new_menuitem (GtkWidget * widget)
+{
+ DbusmenuMenuitem * item = dbusmenu_menuitem_new();
+
+ ParserData *pdata = g_new0 (ParserData, 1);
+ g_object_set_data_full(G_OBJECT(item), PARSER_DATA, pdata, parse_data_free);
+
+ g_object_weak_ref(G_OBJECT(item), dbusmenu_item_freed, NULL);
+ g_object_weak_ref(G_OBJECT(widget), widget_freed, NULL);
+
+ pdata->widget = widget;
+ g_object_add_weak_pointer(G_OBJECT (widget), (gpointer*)&pdata->widget);
+ g_object_set_data(G_OBJECT(widget), CACHED_MENUITEM, item);
+
+ return item;
+}
+
+static gboolean
+toggle_widget_visibility (GtkWidget * widget)
+{
+ gboolean vis = gtk_widget_get_visible (widget);
+ gtk_widget_set_visible (widget, !vis);
+ gtk_widget_set_visible (widget, vis);
+ g_object_unref (G_OBJECT (widget));
+ return FALSE;
}
-/* Called if we replace the cache on the object with a new
- dbusmenu menuitem */
static void
-object_cache_freed (gpointer data)
+watch_submenu(DbusmenuMenuitem * mi, GtkWidget * menu)
{
- if (!G_IS_OBJECT(data)) return;
- g_object_weak_unref(G_OBJECT(data), dbusmenu_cache_freed, data);
- return;
+ ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(mi), PARSER_DATA);
+
+ pdata->shell = menu;
+ g_signal_connect (G_OBJECT (menu),
+ "child-added",
+ G_CALLBACK (child_added_cb),
+ mi);
+ g_signal_connect (G_OBJECT (menu),
+ "child-removed",
+ G_CALLBACK (child_removed_cb),
+ mi);
+ g_object_add_weak_pointer(G_OBJECT (menu), (gpointer*)&pdata->shell);
+
+ /* Some apps (notably Eclipse RCP apps) don't fill contents of submenus
+ until the menu is shown. So we fake that by toggling the visibility of
+ any submenus we come across. Further, these apps need it done with a
+ delay while they finish initializing, so we put the call in the idle
+ queue. */
+ g_idle_add((GSourceFunc)toggle_widget_visibility,
+ g_object_ref (G_OBJECT (menu)));
}
static void
-parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
+activate_toplevel_item (GtkWidget * item)
{
+ /* Make sure that we have a menu item before we start calling
+ functions that depend on it. This should almost always be
+ the case. */
+ if (!GTK_IS_MENU_ITEM(item)) {
+ return;
+ }
+
+ /* If the item is not opening a submenu we don't want to activate
+ it as that'd cause an action. Like opening a preferences dialog
+ to the user. That's not a good idea. */
+ if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(item)) == NULL) {
+ return;
+ }
+
+ GtkWidget * shell = gtk_widget_get_parent (item);
+ if (!GTK_IS_MENU_BAR (shell)) {
+ return;
+ }
+ gtk_menu_shell_activate_item (GTK_MENU_SHELL (shell),
+ item,
+ TRUE);
+}
+
+static void
+parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
+{
/* If this is a shell, then let's handle the items in it. */
if (GTK_IS_MENU_SHELL (widget)) {
/* Okay, this is a little janky and all.. but some applications update some
@@ -132,19 +348,14 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
* Note that this will not force menuitems in submenus to be updated as well.
*/
if (recurse->parent == NULL && GTK_IS_MENU_BAR(widget)) {
- GList *children = gtk_container_get_children (GTK_CONTAINER (widget));
-
- for (; children != NULL; children = children->next) {
- gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget),
- children->data,
- TRUE);
- }
-
- g_list_free (children);
+ gtk_container_foreach (GTK_CONTAINER (widget),
+ (GtkCallback)activate_toplevel_item,
+ NULL);
}
if (recurse->parent == NULL) {
- recurse->parent = dbusmenu_menuitem_new();
+ recurse->parent = new_menuitem(widget);
+ watch_submenu(recurse->parent, widget);
}
gtk_container_foreach (GTK_CONTAINER (widget),
@@ -166,8 +377,6 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
/* We don't have one, so we'll need to build it */
if (thisitem == NULL) {
thisitem = construct_dbusmenu_for_widget (widget);
- g_object_set_data_full(G_OBJECT(widget), CACHED_MENUITEM, thisitem, object_cache_freed);
- g_object_weak_ref(G_OBJECT(thisitem), dbusmenu_cache_freed, widget);
if (!gtk_widget_get_visible (widget)) {
g_signal_connect (G_OBJECT (widget),
@@ -195,12 +404,18 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
/* Oops, let's tell our parents about us */
if (peek == NULL) {
- /* TODO: Should we set a weak ref on the parent? */
- g_object_set_data (G_OBJECT (thisitem),
- "dbusmenu-parent",
- recurse->parent);
- dbusmenu_menuitem_child_append (recurse->parent,
- thisitem);
+ if (dbusmenu_menuitem_get_parent(thisitem) != NULL) {
+ dbusmenu_menuitem_unparent(thisitem);
+ }
+
+ gint pos = get_child_position (widget);
+ if (pos >= 0)
+ dbusmenu_menuitem_child_add_position (recurse->parent,
+ thisitem,
+ pos);
+ else
+ dbusmenu_menuitem_child_append (recurse->parent,
+ thisitem);
}
}
@@ -222,38 +437,80 @@ parse_menu_structure_helper (GtkWidget * widget, RecurseContext * recurse)
return;
}
+static gchar *
+sanitize_label_text (const gchar * label)
+{
+ /* Label contains underscores, which we like, and pango markup,
+ which we don't. */
+ gchar * sanitized = NULL;
+ GError * error = NULL;
+
+ if (label == NULL) {
+ return NULL;
+ }
+
+ if (pango_parse_markup (label, -1, 0, NULL, &sanitized, NULL, &error)) {
+ return sanitized;
+ }
+
+ if (error != NULL) {
+ g_warning ("Could not parse '%s': %s", label, error->message);
+ g_error_free (error);
+ }
+ return g_strdup (label);
+}
+
+static gchar *
+sanitize_label (GtkLabel * label)
+{
+ gchar * text;
+
+ if (gtk_label_get_use_markup (label)) {
+ text = sanitize_label_text (gtk_label_get_label (label));
+ }
+ else {
+ text = g_strdup (gtk_label_get_label (label));
+ }
+
+ if (!gtk_label_get_use_underline (label)) {
+ /* Insert extra underscores */
+ GRegex * regex = g_regex_new ("_", 0, 0, NULL);
+ gchar * escaped = g_regex_replace_literal (regex, text, -1, 0, "__", 0, NULL);
+
+ g_regex_unref (regex);
+ g_free (text);
+
+ text = escaped;
+ }
+
+ return text;
+}
+
/* Turn a widget into a dbusmenu item depending on the type of GTK
object that it is. */
static DbusmenuMenuitem *
construct_dbusmenu_for_widget (GtkWidget * widget)
{
- /* If it's a subclass of our serializable menu item then we can
- use its own build function */
- if (DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(widget)) {
- DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(widget);
- return dbusmenu_gtk_serializable_menu_item_build_menuitem(smi);
- }
-
/* If it's a standard GTK Menu Item we need to do some of our own work */
if (GTK_IS_MENU_ITEM (widget))
{
- DbusmenuMenuitem *mi = dbusmenu_menuitem_new ();
+ DbusmenuMenuitem *mi = new_menuitem(widget);
+
+ ParserData *pdata = (ParserData *)g_object_get_data(G_OBJECT(mi), PARSER_DATA);
gboolean visible = FALSE;
gboolean sensitive = FALSE;
- if (GTK_IS_SEPARATOR_MENU_ITEM (widget))
+ if (GTK_IS_SEPARATOR_MENU_ITEM (widget) || !find_menu_label (widget))
{
dbusmenu_menuitem_property_set (mi,
- "type",
- "separator");
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ DBUSMENU_CLIENT_TYPES_SEPARATOR);
visible = gtk_widget_get_visible (widget);
sensitive = gtk_widget_get_sensitive (widget);
}
else
{
- gboolean label_set = FALSE;
-
g_signal_connect (widget,
"accel-closures-changed",
G_CALLBACK (accel_changed),
@@ -278,46 +535,41 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
if (GTK_IS_IMAGE_MENU_ITEM (widget))
{
GtkWidget *image;
- GtkImageType image_type;
image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (widget));
if (GTK_IS_IMAGE (image))
{
- image_type = gtk_image_get_storage_type (GTK_IMAGE (image));
-
- if (image_type == GTK_IMAGE_STOCK)
- {
- label_set = update_stock_item (mi, image);
- }
- else if (image_type == GTK_IMAGE_ICON_NAME)
- {
- update_icon_name (mi, image);
- }
- else if (image_type == GTK_IMAGE_PIXBUF)
- {
- dbusmenu_menuitem_property_set_image (mi,
- DBUSMENU_MENUITEM_PROP_ICON_DATA,
- gtk_image_get_pixbuf (GTK_IMAGE (image)));
- }
+ update_icon (mi, GTK_IMAGE (image));
+
+ /* Watch for theme changes because if gicon changes, we want to send a
+ different pixbuf. */
+ g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
+ "changed", G_CALLBACK(theme_changed_cb), widget);
+
+ pdata->image = image;
+ g_signal_connect (G_OBJECT (image),
+ "notify",
+ G_CALLBACK (image_notify_cb),
+ mi);
+ g_object_add_weak_pointer(G_OBJECT (image), (gpointer*)&pdata->image);
}
}
GtkWidget *label = find_menu_label (widget);
- dbusmenu_menuitem_property_set (mi,
- "label",
- label ? gtk_label_get_text (GTK_LABEL (label)) : NULL);
+ // Sometimes, an app will directly find and modify the label
+ // (like empathy), so watch the label especially for that.
+ gchar * text = sanitize_label (GTK_LABEL (label));
+ dbusmenu_menuitem_property_set (mi, "label", text);
+ g_free (text);
- if (label)
- {
- // Sometimes, an app will directly find and modify the label
- // (like empathy), so watch the label especially for that.
- g_signal_connect (G_OBJECT (label),
- "notify",
- G_CALLBACK (label_notify_cb),
- mi);
- }
+ pdata->label = label;
+ g_signal_connect (G_OBJECT (label),
+ "notify",
+ G_CALLBACK (label_notify_cb),
+ mi);
+ g_object_add_weak_pointer(G_OBJECT (label), (gpointer*)&pdata->label);
if (GTK_IS_ACTIVATABLE (widget))
{
@@ -332,14 +584,22 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
visible = gtk_action_is_visible (action);
sensitive = gtk_action_is_sensitive (action);
+ pdata->action = action;
g_signal_connect_object (action, "notify",
G_CALLBACK (action_notify_cb),
mi,
G_CONNECT_AFTER);
+ g_object_add_weak_pointer(G_OBJECT (action), (gpointer*)&pdata->action);
}
}
}
+ GtkWidget *submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
+ if (submenu)
+ {
+ watch_submenu(mi, submenu);
+ }
+
if (!g_object_get_data (G_OBJECT (widget), "gtk-empty-menu-item") && !GTK_IS_TEAROFF_MENU_ITEM (widget))
{
visible = gtk_widget_get_visible (widget);
@@ -357,6 +617,11 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW,
G_CALLBACK (item_about_to_show),
widget);
+
+ g_signal_connect (G_OBJECT (mi),
+ DBUSMENU_MENUITEM_SIGNAL_EVENT,
+ G_CALLBACK (item_handle_event),
+ widget);
}
dbusmenu_menuitem_property_set_bool (mi,
@@ -371,12 +636,18 @@ construct_dbusmenu_for_widget (GtkWidget * widget)
"notify",
G_CALLBACK (widget_notify_cb),
mi);
+
+ g_signal_connect (widget,
+ "add",
+ G_CALLBACK (widget_add_cb),
+ mi);
+
return mi;
}
/* If it's none of those we're going to just create a
generic menuitem as a place holder for it. */
- return dbusmenu_menuitem_new();
+ return new_menuitem(widget);
}
static void
@@ -408,48 +679,6 @@ accel_changed (GtkWidget *widget,
dbusmenu_menuitem_property_set_shortcut_menuitem (mi, GTK_MENU_ITEM (widget));
}
-static gboolean
-update_stock_item (DbusmenuMenuitem *menuitem,
- GtkWidget *widget)
-{
- GtkStockItem stock;
- GtkImage *image;
-
- g_return_val_if_fail (GTK_IS_IMAGE (widget), FALSE);
-
- image = GTK_IMAGE (widget);
-
- if (gtk_image_get_storage_type (image) != GTK_IMAGE_STOCK)
- return FALSE;
-
- gchar * stock_id = NULL;
- gtk_image_get_stock(image, &stock_id, NULL);
-
- gtk_stock_lookup (stock_id, &stock);
-
- if (should_show_image (image))
- dbusmenu_menuitem_property_set (menuitem,
- DBUSMENU_MENUITEM_PROP_ICON_NAME,
- stock_id);
- else
- dbusmenu_menuitem_property_remove (menuitem,
- DBUSMENU_MENUITEM_PROP_ICON_NAME);
-
- const gchar *label = dbusmenu_menuitem_property_get (menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL);
-
- if (stock.label != NULL && label != NULL)
- {
- dbusmenu_menuitem_property_set (menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL,
- stock.label);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
static void
checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
{
@@ -459,27 +688,89 @@ checkbox_toggled (GtkWidget *widget, DbusmenuMenuitem *mi)
}
static void
-update_icon_name (DbusmenuMenuitem *menuitem,
- GtkWidget *widget)
+update_icon (DbusmenuMenuitem *menuitem, GtkImage *image)
{
- GtkImage *image;
-
- g_return_if_fail (GTK_IS_IMAGE (widget));
-
- image = GTK_IMAGE (widget);
+ GdkPixbuf * pixbuf = NULL;
+ const gchar * icon_name = NULL;
+ GtkStockItem stock;
+ GIcon * gicon;
+ GtkIconInfo * info;
+ gint width;
+
+ if (image != NULL && should_show_image (image)) {
+ switch (gtk_image_get_storage_type (image)) {
+ case GTK_IMAGE_EMPTY:
+ break;
+
+ case GTK_IMAGE_PIXBUF:
+ pixbuf = g_object_ref (gtk_image_get_pixbuf (image));
+ break;
+
+ case GTK_IMAGE_ICON_NAME:
+ gtk_image_get_icon_name (image, &icon_name, NULL);
+ break;
+
+ case GTK_IMAGE_STOCK:
+ gtk_image_get_stock (image, (gchar **) &icon_name, NULL);
+ if (gtk_stock_lookup (icon_name, &stock)) {
+ /* Now set label too */
+ const gchar * label = NULL;
+ label = dbusmenu_menuitem_property_get (menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL);
+ if (stock.label != NULL && label != NULL) {
+ dbusmenu_menuitem_property_set (menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL,
+ stock.label);
+ }
+ }
+ break;
+
+ case GTK_IMAGE_GICON:
+ /* Load up a pixbuf and send that over. We don't bother differentiating
+ between icon-name gicons and pixbuf gicons because even when given a
+ icon-name gicon, there's no easy way to lookup which icon-name among
+ its set is present and should be used among the icon themes available.
+ So instead, we render to a pixbuf and watch icon theme changes. */
+ gtk_image_get_gicon (image, &gicon, NULL);
+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, NULL);
+ info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
+ gicon, width,
+ GTK_ICON_LOOKUP_FORCE_SIZE);
+ if (info != NULL) {
+ pixbuf = gtk_icon_info_load_icon (info, NULL);
+ gtk_icon_info_free (info);
+ }
+ break;
- if (gtk_image_get_storage_type (image) != GTK_IMAGE_ICON_NAME)
- return;
+ default:
+ g_debug ("Could not handle image type %i\n", gtk_image_get_storage_type (image));
+ break;
+ }
+ }
- if (should_show_image (image)) {
- const gchar * icon_name = NULL;
- gtk_image_get_icon_name(image, &icon_name, NULL);
+ if (icon_name != NULL) {
dbusmenu_menuitem_property_set (menuitem,
DBUSMENU_MENUITEM_PROP_ICON_NAME,
icon_name);
- } else {
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
+ }
+ else if (pixbuf != NULL) {
dbusmenu_menuitem_property_remove (menuitem,
DBUSMENU_MENUITEM_PROP_ICON_NAME);
+ dbusmenu_menuitem_property_set_image (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA,
+ pixbuf);
+ }
+ else {
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_NAME);
+ dbusmenu_menuitem_property_remove (menuitem,
+ DBUSMENU_MENUITEM_PROP_ICON_DATA);
+ }
+
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
}
}
@@ -513,17 +804,100 @@ find_menu_label (GtkWidget *widget)
}
static void
+recreate_menu_item (DbusmenuMenuitem * parent, DbusmenuMenuitem * child)
+{
+ if (parent == NULL)
+ {
+ /* We need a parent */
+ return;
+ }
+ ParserData * pdata = g_object_get_data (G_OBJECT (child), PARSER_DATA);
+ /* Keep a pointer to the GtkMenuItem, as pdata->widget might be
+ * invalidated when we delete the DbusmenuMenuitem
+ */
+ GtkWidget * menuitem = pdata->widget;
+
+ dbusmenu_menuitem_child_delete (parent, child);
+
+ RecurseContext recurse = {0};
+ recurse.toplevel = gtk_widget_get_toplevel(menuitem);
+ recurse.parent = parent;
+
+ parse_menu_structure_helper(menuitem, &recurse);
+}
+
+static gboolean
+recreate_menu_item_in_idle_cb (gpointer data)
+{
+ DbusmenuMenuitem * child = (DbusmenuMenuitem *)data;
+ DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (child);
+ g_object_unref (child);
+ recreate_menu_item (parent, child);
+ return FALSE;
+}
+
+static void
label_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
gpointer data)
{
DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
+ GValue prop_value = {0};
+
+ g_value_init (&prop_value, pspec->value_type);
+ g_object_get_property (G_OBJECT (widget), pspec->name, &prop_value);
if (pspec->name == g_intern_static_string ("label"))
{
+ gchar * text = sanitize_label (GTK_LABEL (widget));
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_label_get_text (GTK_LABEL (widget)));
+ text);
+ g_free (text);
+ }
+ else if (pspec->name == g_intern_static_string ("parent"))
+ {
+ if (GTK_WIDGET (g_value_get_object (&prop_value)) == NULL)
+ {
+ /* This label is being removed from its GtkMenuItem. The
+ * menuitem becomes a separator now. As the client doesn't handle
+ * changing types so well, we remove the current DbusmenuMenuitem
+ * and add a new one.
+ *
+ * Note, we have to defer this to idle, as we are called before
+ * bin->child member of our old parent is invalidated. If we go ahead
+ * and call parse_menu_structure_helper now, the GtkMenuItem will
+ * still appear to have a label and we never convert it to a separator
+ */
+ g_object_ref (child);
+ g_idle_add ((GSourceFunc)recreate_menu_item_in_idle_cb, child);
+ }
+ }
+
+ g_value_unset(&prop_value);
+ return;
+}
+
+static void
+image_notify_cb (GtkWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ DbusmenuMenuitem *mi = (DbusmenuMenuitem *)data;
+
+ if (pspec->name == g_intern_static_string ("file") ||
+ pspec->name == g_intern_static_string ("gicon") ||
+ pspec->name == g_intern_static_string ("icon-name") ||
+ pspec->name == g_intern_static_string ("icon-set") ||
+ pspec->name == g_intern_static_string ("image") ||
+ pspec->name == g_intern_static_string ("mask") ||
+ pspec->name == g_intern_static_string ("pixbuf") ||
+ pspec->name == g_intern_static_string ("pixbuf-animation") ||
+ pspec->name == g_intern_static_string ("pixmap") ||
+ pspec->name == g_intern_static_string ("stock") ||
+ pspec->name == g_intern_static_string ("storage-type"))
+ {
+ update_icon (mi, GTK_IMAGE (widget));
}
}
@@ -554,9 +928,11 @@ action_notify_cb (GtkAction *action,
}
else if (pspec->name == g_intern_static_string ("label"))
{
+ gchar * text = sanitize_label_text (gtk_action_get_label (action));
dbusmenu_menuitem_property_set (mi,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_action_get_label (action));
+ text);
+ g_free (text);
}
}
@@ -571,7 +947,9 @@ item_activated (DbusmenuMenuitem *item, guint timestamp, gpointer user_data)
if (GTK_IS_MENU_ITEM (child))
{
+ gdk_threads_enter ();
gtk_menu_item_activate (GTK_MENU_ITEM (child));
+ gdk_threads_leave ();
}
}
}
@@ -596,38 +974,94 @@ item_about_to_show (DbusmenuMenuitem *item, gpointer user_data)
return TRUE;
}
+static gboolean
+item_handle_event (DbusmenuMenuitem *item, const gchar *name,
+ GVariant *variant, guint timestamp, GtkWidget *widget)
+{
+ if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_OPENED) == 0)
+ {
+ GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (submenu != NULL)
+ {
+ // Show the submenu so the app can notice and futz with the menus as
+ // desired (empathy and geany do this)
+ gtk_widget_show (submenu);
+ }
+ }
+ else if (g_strcmp0 (name, DBUSMENU_MENUITEM_EVENT_CLOSED) == 0)
+ {
+ GtkWidget *submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
+ if (submenu != NULL)
+ {
+ // Hide the submenu so the app can notice and futz with the menus as
+ // desired (empathy and geany do this)
+ gtk_widget_hide (submenu);
+ }
+ }
+
+ return FALSE; // just pass through on everything
+}
+
+static gboolean
+handle_first_label (DbusmenuMenuitem *mi)
+{
+ ParserData *pdata = g_object_get_data (G_OBJECT (mi), PARSER_DATA);
+ if (!pdata->label)
+ {
+ /* GtkMenuItem's can start life as a separator if they have no child
+ * GtkLabel. In this case, we need to convert the DbusmenuMenuitem from
+ * a separator to a normal menuitem if the application adds a label.
+ * As changing types isn't handled too well by the client, we delete
+ * this menuitem for now and then recreate it
+ */
+ DbusmenuMenuitem * parent = dbusmenu_menuitem_get_parent (mi);
+ recreate_menu_item (parent, mi);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
widget_notify_cb (GtkWidget *widget,
GParamSpec *pspec,
gpointer data)
{
DbusmenuMenuitem *child = (DbusmenuMenuitem *)data;
+ GValue prop_value = {0};
+
+ g_value_init (&prop_value, pspec->value_type);
+ g_object_get_property (G_OBJECT (widget), pspec->name, &prop_value);
if (pspec->name == g_intern_static_string ("sensitive"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_ENABLED,
- gtk_widget_get_sensitive (widget));
+ g_value_get_boolean (&prop_value));
}
else if (pspec->name == g_intern_static_string ("label"))
{
+ if (handle_first_label (child))
+ {
+ return;
+ }
+
dbusmenu_menuitem_property_set (child,
DBUSMENU_MENUITEM_PROP_LABEL,
- gtk_menu_item_get_label (GTK_MENU_ITEM (widget)));
+ g_value_get_string (&prop_value));
}
else if (pspec->name == g_intern_static_string ("visible"))
{
dbusmenu_menuitem_property_set_bool (child,
DBUSMENU_MENUITEM_PROP_VISIBLE,
- gtk_widget_get_visible (widget));
+ g_value_get_boolean (&prop_value));
}
- else if (pspec->name == g_intern_static_string ("stock"))
+ else if (pspec->name == g_intern_static_string ("image") ||
+ pspec->name == g_intern_static_string ("always-show-image"))
{
- update_stock_item (child, widget);
- }
- else if (pspec->name == g_intern_static_string ("icon-name"))
- {
- update_icon_name (child, widget);
+ GtkWidget *image;
+ image = GTK_WIDGET (g_value_get_object (&prop_value));
+ update_icon (child, GTK_IMAGE (image));
}
else if (pspec->name == g_intern_static_string ("parent"))
{
@@ -635,13 +1069,13 @@ widget_notify_cb (GtkWidget *widget,
* We probably should have added a 'remove' method to the
* UbuntuMenuProxy early on, but it's late in the cycle now.
*/
- if (gtk_widget_get_parent (widget) == NULL)
+ if (GTK_WIDGET (g_value_get_object (&prop_value)) == NULL)
{
g_signal_handlers_disconnect_by_func (widget,
G_CALLBACK (widget_notify_cb),
child);
- DbusmenuMenuitem *parent = g_object_get_data (G_OBJECT (child), "dbusmenu-parent");
+ DbusmenuMenuitem *parent = dbusmenu_menuitem_get_parent (child);
if (DBUSMENU_IS_MENUITEM (parent) && DBUSMENU_IS_MENUITEM (child))
{
@@ -649,6 +1083,104 @@ widget_notify_cb (GtkWidget *widget,
}
}
}
+ else if (pspec->name == g_intern_static_string ("submenu"))
+ {
+ /* The underlying submenu got swapped out. Let's see what it is now. */
+ /* First, delete any children that may exist currently. */
+ DbusmenuMenuitem * item = DBUSMENU_MENUITEM(g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM));
+ if (item != NULL)
+ {
+ GList * children = dbusmenu_menuitem_take_children (item);
+ GList * child = children;
+ while (child != NULL) {
+ g_object_unref (G_OBJECT(child->data));
+ child = child->next;
+ }
+ g_list_free(children);
+ }
+
+ /* Now parse new submenu. */
+ RecurseContext recurse = {0};
+ recurse.toplevel = gtk_widget_get_toplevel(widget);
+ recurse.parent = item;
+
+ if (item != NULL) {
+ GtkWidget * menu = GTK_WIDGET (g_value_get_object (&prop_value));
+ parse_menu_structure_helper(menu, &recurse);
+ watch_submenu(item, menu);
+ } else {
+ /* Note: it would be really odd that we wouldn't have a cached
+ item, but we should handle that appropriately. */
+ parse_menu_structure_helper(widget, &recurse);
+ g_object_unref(G_OBJECT(recurse.parent));
+ }
+ }
+ g_value_unset (&prop_value);
+}
+
+static void
+widget_add_cb (GtkWidget *widget,
+ GtkWidget *child,
+ gpointer data)
+{
+ if (find_menu_label (child) != NULL)
+ handle_first_label (data);
+}
+
+/* A child item was added to a menu we're watching. Let's try to integrate it. */
+static void
+child_added_cb (GtkContainer *menu, GtkWidget *widget, gpointer data)
+{
+ DbusmenuMenuitem *menuitem = (DbusmenuMenuitem *)data;
+
+ RecurseContext recurse = {0};
+ recurse.toplevel = gtk_widget_get_toplevel(GTK_WIDGET(menu));
+ recurse.parent = menuitem;
+
+ if (GTK_IS_MENU_BAR(menu)) {
+ activate_toplevel_item (widget);
+ }
+
+ parse_menu_structure_helper(widget, &recurse);
+}
+
+/* A child item was added to a menu we're watching. Let's try to integrate it. */
+static void
+child_removed_cb (GtkContainer *menu, GtkWidget *widget, gpointer data)
+{
+ gpointer pmi = g_object_get_data(G_OBJECT(widget), CACHED_MENUITEM);
+ if (pmi == NULL) {
+ return;
+ }
+
+ DbusmenuMenuitem * child = DBUSMENU_MENUITEM(pmi);
+
+ pmi = g_object_get_data(G_OBJECT(menu), CACHED_MENUITEM);
+ if (pmi == NULL) {
+ return;
+ }
+
+ DbusmenuMenuitem * parent = DBUSMENU_MENUITEM(pmi);
+
+ dbusmenu_menuitem_child_delete(parent, child);
+ return;
+}
+
+static void
+theme_changed_cb (GtkIconTheme *theme, gpointer data)
+{
+ GtkWidget *image;
+
+ image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (data));
+
+ gpointer pmi = g_object_get_data(G_OBJECT(data), CACHED_MENUITEM);
+ if (pmi != NULL) {
+ update_icon(DBUSMENU_MENUITEM(pmi), GTK_IMAGE(image));
+ }
+
+ /* Switch signal to new theme */
+ g_signal_handlers_disconnect_by_func(theme, G_CALLBACK(theme_changed_cb), data);
+ g_signal_connect(gtk_icon_theme_get_default(), "changed", G_CALLBACK(theme_changed_cb), data);
}
static gboolean
diff --git a/libdbusmenu-gtk/parser.h b/libdbusmenu-gtk/parser.h
index a40d709..97fa9c6 100644
--- a/libdbusmenu-gtk/parser.h
+++ b/libdbusmenu-gtk/parser.h
@@ -32,6 +32,21 @@ License version 3 and version 2.1 along with this program. If not, see
#include <libdbusmenu-glib/menuitem.h>
#include <gtk/gtk.h>
+G_BEGIN_DECLS
+
DbusmenuMenuitem * dbusmenu_gtk_parse_menu_structure (GtkWidget * widget);
+DbusmenuMenuitem * dbusmenu_gtk_parse_get_cached_item (GtkWidget * widget);
+
+/**
+ SECTION:parser
+ @short_description: A parser of in-memory GTK menu trees
+ @stability: Unstable
+ @include: libdbusmenu-gtk/parser.h
+
+ The parser will take a GTK menu tree and attach it to a Dbusmenu menu
+ tree. Along with setting up all the signals for updates and destruction.
+ The returned item would be the root item of the given tree.
+*/
+G_END_DECLS
#endif /* DBUSMENU_GTK_PARSER_H__ */
diff --git a/libdbusmenu-gtk/serializablemenuitem.c b/libdbusmenu-gtk/serializablemenuitem.c
deleted file mode 100644
index cfd864d..0000000
--- a/libdbusmenu-gtk/serializablemenuitem.c
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
-An object to act as a base class for easy GTK widgets that can be
-transfered over dbusmenu.
-
-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 "client.h"
-#include "serializablemenuitem.h"
-
-/**
- DbusmenuGtkSerializableMenuItemPrivate:
- @mi: Menuitem to watch the property changes from
-*/
-struct _DbusmenuGtkSerializableMenuItemPrivate {
- DbusmenuMenuitem * mi;
-};
-
-/* Properties */
-enum {
- PROP_0,
- PROP_MENUITEM
-};
-
-/* Private macro, only used in object init */
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemPrivate))
-
-/* Function prototypes */
-static void dbusmenu_gtk_serializable_menu_item_class_init (DbusmenuGtkSerializableMenuItemClass *klass);
-static void dbusmenu_gtk_serializable_menu_item_init (DbusmenuGtkSerializableMenuItem *self);
-static void dbusmenu_gtk_serializable_menu_item_dispose (GObject *object);
-static void dbusmenu_gtk_serializable_menu_item_finalize (GObject *object);
-static void set_property (GObject * obj,
- guint id,
- const GValue * value,
- GParamSpec * pspec);
-static void get_property (GObject * obj,
- guint id,
- GValue * value,
- GParamSpec * pspec);
-
-/* GObject boiler plate */
-G_DEFINE_TYPE (DbusmenuGtkSerializableMenuItem, dbusmenu_gtk_serializable_menu_item, GTK_TYPE_MENU_ITEM);
-
-/* Initialize the stuff in the class structure */
-static void
-dbusmenu_gtk_serializable_menu_item_class_init (DbusmenuGtkSerializableMenuItemClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (DbusmenuGtkSerializableMenuItemPrivate));
-
- object_class->dispose = dbusmenu_gtk_serializable_menu_item_dispose;
- object_class->finalize = dbusmenu_gtk_serializable_menu_item_finalize;
- object_class->set_property = set_property;
- object_class->get_property = get_property;
-
- g_object_class_install_property (object_class, PROP_MENUITEM,
- g_param_spec_object(DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM, "DBusmenu Menuitem attached to item",
- "A menuitem who's properties are being watched and where changes should be watched for updates. It is the responsibility of subclasses to set up the signal handlers for those property changes.",
- DBUSMENU_TYPE_MENUITEM,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- return;
-}
-
-/* Initialize the object structures and private structure */
-static void
-dbusmenu_gtk_serializable_menu_item_init (DbusmenuGtkSerializableMenuItem *self)
-{
- self->priv = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_PRIVATE(self);
-
- self->priv->mi = NULL;
-
- return;
-}
-
-/* Free all references to objects */
-static void
-dbusmenu_gtk_serializable_menu_item_dispose (GObject *object)
-{
- DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(object);
- g_return_if_fail(smi != NULL);
-
- if (smi->priv->mi != NULL) {
- g_object_unref(G_OBJECT(smi->priv->mi));
- smi->priv->mi = NULL;
- }
-
-
- G_OBJECT_CLASS (dbusmenu_gtk_serializable_menu_item_parent_class)->dispose (object);
- return;
-}
-
-/* Free memory */
-static void
-dbusmenu_gtk_serializable_menu_item_finalize (GObject *object)
-{
-
-
-
- G_OBJECT_CLASS (dbusmenu_gtk_serializable_menu_item_parent_class)->finalize (object);
- return;
-}
-
-/* Set an object property */
-static void
-set_property (GObject * obj, guint id, const GValue * value, GParamSpec * pspec)
-{
- DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj);
-
- switch (id) {
- case PROP_MENUITEM:
- smi->priv->mi = g_value_get_object(value);
- break;
- default:
- g_return_if_reached();
- break;
- }
-
- return;
-}
-
-/* Get an object property */
-static void
-get_property (GObject * obj, guint id, GValue * value, GParamSpec * pspec)
-{
- DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj);
-
- switch (id) {
- case PROP_MENUITEM:
- g_value_set_object(value, smi->priv->mi);
- break;
- default:
- g_return_if_reached();
- break;
- }
-
- return;
-}
-
-/**
- dbusmenu_gtk_serializable_menu_item_build_menuitem:
- @smi: #DbusmenuGtkSerializableMenuItem to build a #DbusmenuMenuitem mirroring
-
- This function is for menu items that are instanciated from
- GTK and have their properites set using GTK functions. This
- builds a #DbusmenuMenuitem that then has the properties that
- should be sent over the bus to create a new item of this
- type on the other side.
-
- Return value: (transfer full) A #DbusmenuMenuitem who's values will be
- set by this object.
-*/
-DbusmenuMenuitem *
-dbusmenu_gtk_serializable_menu_item_build_menuitem (DbusmenuGtkSerializableMenuItem * smi)
-{
- g_return_val_if_fail(DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(smi), NULL);
-
- DbusmenuGtkSerializableMenuItemClass * klass = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_CLASS(smi);
- if (klass->build_dbusmenu_menuitem != NULL) {
- return klass->build_dbusmenu_menuitem(smi);
- }
-
- return NULL;
-}
-
-/* Callback to the generic type handler */
-typedef struct _type_handler_t type_handler_t;
-struct _type_handler_t {
- DbusmenuGtkSerializableMenuItemClass * class;
- GType type;
-};
-
-/* Handle the type with this item. */
-static gboolean
-type_handler (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
-{
- type_handler_t * th = (type_handler_t *)user_data;
-
- DbusmenuGtkSerializableMenuItem * smi = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(g_object_new(th->type, NULL));
- g_return_val_if_fail(smi != NULL, FALSE);
-
- dbusmenu_gtk_serializable_menu_item_set_menuitem(smi, newitem);
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(smi), parent);
-
- return TRUE;
-}
-
-/* Destruction is inevitable */
-static void
-type_destroy_handler (DbusmenuClient * client, const gchar * type, gpointer user_data)
-{
- g_return_if_fail(user_data != NULL);
- type_handler_t * th = (type_handler_t *)user_data;
- g_type_class_unref(th->class);
- g_free(user_data);
- return;
-}
-
-/**
- dbusmenu_gtk_serializable_menu_item_register_to_client:
- @client: #DbusmenuClient that we should register a type at.
- @item_type: The #GType of a class that is a subclass of #DbusmenuGtkSerializableMenuItem
-
- Registers a generic handler for dealing with all subclasses of
- #DbusmenuGtkSerializableMenuItem. This handler responds to the callback,
- creates a new object and attaches it to the appropriate #DbusmenuMenuitem
- object.
-*/
-void
-dbusmenu_gtk_serializable_menu_item_register_to_client (DbusmenuClient * client, GType item_type)
-{
- g_return_if_fail(g_type_is_a(item_type, DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM));
-
- gpointer type_class = g_type_class_ref(item_type);
- g_return_if_fail(type_class != NULL);
-
- DbusmenuGtkSerializableMenuItemClass * class = DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_CLASS(type_class);
-
- if (class->get_type_string == NULL) {
- g_type_class_unref(type_class);
- g_error("No 'get_type_string' in subclass of DbusmenuGtkSerializableMenuItem");
- return;
- }
-
- /* Register type */
- type_handler_t * th = g_new0(type_handler_t, 1);
- th->class = class;
- th->type = item_type;
- if (!dbusmenu_client_add_type_handler_full(client, class->get_type_string(), type_handler, th, type_destroy_handler)) {
- type_destroy_handler(client, class->get_type_string(), th);
- }
-
- /* Register defaults */
- /* TODO: Need API on another branch */
-
- return;
-}
-
-/**
- dbusmenu_gtk_serializable_menu_item_set_menuitem:
- @smi: #DbusmenuGtkSerializableMenuItem to set the @DbusmenuGtkSerializableMenuItem::dbusmenu-menuitem of
- @mi: Menuitem to get the properties from
-
- This function is used on the server side to signal to the object
- that it should get its' property change events from @mi instead
- of expecting calls to its' API. A call to this function sets the
- property and subclasses should listen to the notify signal to
- pick up this property being set.
-*/
-void
-dbusmenu_gtk_serializable_menu_item_set_menuitem (DbusmenuGtkSerializableMenuItem * smi, DbusmenuMenuitem * mi)
-{
- g_return_if_fail(DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(smi));
- g_return_if_fail(mi != NULL);
-
- smi->priv->mi = mi;
- g_object_notify(G_OBJECT(smi), DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM);
-
- return;
-}
diff --git a/libdbusmenu-gtk/serializablemenuitem.h b/libdbusmenu-gtk/serializablemenuitem.h
deleted file mode 100644
index db28a24..0000000
--- a/libdbusmenu-gtk/serializablemenuitem.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
-An object to act as a base class for easy GTK widgets that can be
-transfered over dbusmenu.
-
-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_GTK_SERIALIZABLE_MENU_ITEM_H__
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_H__ 1
-
-#include <glib.h>
-#include <glib-object.h>
-#include <gtk/gtk.h>
-#include <libdbusmenu-glib/menuitem.h>
-#include <libdbusmenu-glib/client.h>
-
-G_BEGIN_DECLS
-
-#define DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM (dbusmenu_gtk_serializable_menu_item_get_type ())
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItem))
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemClass))
-#define DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM))
-#define DBUSMENU_IS_GTK_SERIALIZABLE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM))
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_TYPE_GTK_SERIALIZABLE_MENU_ITEM, DbusmenuGtkSerializableMenuItemClass))
-
-#define DBUSMENU_GTK_SERIALIZABLE_MENU_ITEM_PROP_MENUITEM "dbusmenu-menuitem"
-
-typedef struct _DbusmenuGtkSerializableMenuItem DbusmenuGtkSerializableMenuItem;
-typedef struct _DbusmenuGtkSerializableMenuItemClass DbusmenuGtkSerializableMenuItemClass;
-typedef struct _DbusmenuGtkSerializableMenuItemPrivate DbusmenuGtkSerializableMenuItemPrivate;
-
-/**
- DbusmenuGtkSerializableMenuItemClass:
- @parent_class: Inherit from GtkMenuItem
- @get_type_string: Static function to get a string describing this type
- @get_default_properties: Return a hashtable of defaults for the menu item type
- @build_dbusmenu_menuitem: Build a menuitem that can be sent over dbus
- @_dbusmenu_gtk_serializable_menu_item_reserved1: Reserved for future use.
- @_dbusmenu_gtk_serializable_menu_item_reserved2: Reserved for future use.
- @_dbusmenu_gtk_serializable_menu_item_reserved3: Reserved for future use.
- @_dbusmenu_gtk_serializable_menu_item_reserved4: Reserved for future use.
- @_dbusmenu_gtk_serializable_menu_item_reserved5: Reserved for future use.
- @_dbusmenu_gtk_serializable_menu_item_reserved6: Reserved for future use.
-*/
-struct _DbusmenuGtkSerializableMenuItemClass {
- GtkMenuItemClass parent_class;
-
- /* Subclassable functions */
- const gchar * (*get_type_string) (void);
- GHashTable * (*get_default_properties) (void);
-
- DbusmenuMenuitem * (*build_dbusmenu_menuitem) (DbusmenuGtkSerializableMenuItem * smi);
-
- /* Signals */
-
-
-
- /* Empty Space */
- /*< Private >*/
- void (*_dbusmenu_gtk_serializable_menu_item_reserved1) (void);
- void (*_dbusmenu_gtk_serializable_menu_item_reserved2) (void);
- void (*_dbusmenu_gtk_serializable_menu_item_reserved3) (void);
- void (*_dbusmenu_gtk_serializable_menu_item_reserved4) (void);
- void (*_dbusmenu_gtk_serializable_menu_item_reserved5) (void);
- void (*_dbusmenu_gtk_serializable_menu_item_reserved6) (void);
-};
-
-/**
- DbusmenuGtkSerializableMenuItem:
- @parent: Inherit from GtkMenuItem
- @priv: Blind structure of private variables
-
- The Serializable Menuitem provides a way for menu items to be created
- that can easily be picked up by the Dbusmenu GTK Parser. This way
- you can create custom items, and transport them across dbusmenu to
- your menus or the appmenu on the other side of the bus. By providing
- these function the parser has enough information to both serialize, and
- deserialize on the other side, the menuitem you've so carefully created.
-*/
-struct _DbusmenuGtkSerializableMenuItem {
- GtkMenuItem parent;
-
- DbusmenuGtkSerializableMenuItemPrivate * priv;
-};
-
-GType dbusmenu_gtk_serializable_menu_item_get_type (void);
-
-DbusmenuMenuitem * dbusmenu_gtk_serializable_menu_item_build_menuitem (DbusmenuGtkSerializableMenuItem * smi);
-void dbusmenu_gtk_serializable_menu_item_register_to_client (DbusmenuClient * client, GType item_type);
-void dbusmenu_gtk_serializable_menu_item_set_menuitem (DbusmenuGtkSerializableMenuItem * smi, DbusmenuMenuitem * mi);
-
-G_END_DECLS
-
-#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e69de29..d6ac240 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -0,0 +1,2 @@
+libdbusmenu-glib/defaults.c
+libdbusmenu-glib/server.c
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 62142dc..e6882af 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,6 +2,8 @@
DBUS_RUNNER=dbus-test-runner
CLEANFILES=
+DISTCLEANFILES=
+EXTRA_DIST =
TESTS = \
test-glib-objects-test \
@@ -11,13 +13,28 @@ TESTS = \
test-glib-proxy \
test-glib-simple-items \
test-glib-submenu \
- test-json \
+ test-json
+
+if WANT_LIBDBUSMENUGTK
+TESTS += \
test-gtk-objects-test \
test-gtk-label \
test-gtk-shortcut \
test-gtk-reorder \
test-gtk-submenu \
test-gtk-parser-test
+endif
+
+XFAIL_TESTS = \
+ test-glib-proxy
+
+# The Python test only work on the system copy of
+# dbusmenu, so while they can be usefule they're not
+# good tests of what you're currently building. Handy
+# to check GI support though. FIXME!
+PYTHON_TESTS = \
+ test-gtk-shortcut-python \
+ test-glib-simple-items.py
check_PROGRAMS = \
glib-server-nomenu \
@@ -31,23 +48,30 @@ check_PROGRAMS = \
test-glib-proxy-client \
test-glib-proxy-server \
test-glib-proxy-proxy \
- test-gtk-objects \
test-glib-submenu-client \
test-glib-submenu-server \
+ test-glib-simple-items \
+ test-json-client \
+ test-json-server
+
+if WANT_LIBDBUSMENUGTK
+check_PROGRAMS += \
+ test-gtk-objects \
test-gtk-label-client \
test-gtk-label-server \
test-gtk-shortcut-client \
test-gtk-shortcut-server \
- test-glib-simple-items \
test-gtk-reorder-server \
- test-json-client \
- test-json-server \
test-gtk-submenu-server \
test-gtk-submenu-client \
test-gtk-parser
+endif
XVFB_RUN=". $(srcdir)/run-xvfb.sh"
+# for the GI tests, prefer/use the typelibs from the local build tree
+TESTS_ENVIRONMENT = env GI_TYPELIB_PATH=$(top_builddir)/libdbusmenu-glib:$(top_builddir)/libdbusmenu-gtk:$(GI_TYPELIB_PATH)
+
######################
# JSON Loader lib
######################
@@ -260,6 +284,8 @@ test_glib_objects_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
$(DBUSMENUGLIB_LIBS)
+DISTCLEANFILES += $(OBJECT_XML_REPORT)
+
######################
# Test Glib Properties
######################
@@ -358,6 +384,8 @@ test_glib_simple_items_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
$(DBUSMENUGLIB_LIBS)
+EXTRA_DIST += test-glib-simple-items.py
+
######################
# Test GTK Object
######################
@@ -382,10 +410,12 @@ test_gtk_objects_CFLAGS = \
test_gtk_objects_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGLIB_LIBS) \
$(DBUSMENUGTK_LIBS)
+DISTCLEANFILES += $(GTK_OBJECT_XML_REPORT)
+
######################
# Test GTK Parser
######################
@@ -410,10 +440,11 @@ test_gtk_parser_CFLAGS = \
test_gtk_parser_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGLIB_LIBS) \
$(DBUSMENUGTK_LIBS)
+DISTCLEANFILES += $(GTK_PARSER_XML_REPORT)
#########################
# Test GTK Label
@@ -436,7 +467,7 @@ test_gtk_label_server_CFLAGS = \
test_gtk_label_server_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
libdbusmenu-jsonloader.la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -452,7 +483,7 @@ test_gtk_label_client_CFLAGS = \
test_gtk_label_client_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -477,7 +508,7 @@ test_gtk_shortcut_server_CFLAGS = \
test_gtk_shortcut_server_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -492,11 +523,24 @@ test_gtk_shortcut_client_CFLAGS = \
test_gtk_shortcut_client_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
#########################
+# Test GTK Shortcut Python
+#########################
+
+test-gtk-shortcut-python: test-gtk-shortcut-server test-gtk-shortcut-client.py Makefile.am
+ @echo "#!/bin/bash" > $@
+ @echo $(XVFB_RUN) >> $@
+ @echo $(DBUS_RUNNER) --task $(srcdir)/test-gtk-shortcut-client.py --task-name Client --task ./test-gtk-shortcut-server --task-name Server --ignore-return >> $@
+ @chmod +x $@
+
+EXTRA_DIST += test-gtk-shortcut-client.py
+CLEANFILES += test-gtk-shortcut-client.pyc
+
+#########################
# Test GTK Reorder
#########################
@@ -517,7 +561,7 @@ test_gtk_reorder_server_CFLAGS = \
test_gtk_reorder_server_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -542,7 +586,7 @@ test_gtk_submenu_server_CFLAGS = \
test_gtk_submenu_server_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -557,7 +601,7 @@ test_gtk_submenu_client_CFLAGS = \
test_gtk_submenu_client_LDADD = \
../libdbusmenu-glib/libdbusmenu-glib.la \
- ../libdbusmenu-gtk/libdbusmenu-gtk.la \
+ ../libdbusmenu-gtk/libdbusmenu-gtk$(VER).la \
$(DBUSMENUGTK_LIBS) \
$(DBUSMENUTESTS_LIBS)
@@ -590,7 +634,7 @@ jsondir = $(datadir)/${PACKAGE}/json/
json_DATA = \
test-gtk-label.json
-EXTRA_DIST = \
+EXTRA_DIST += \
$(examples_DATA) \
run-xvfb.sh \
$(json_DATA) \
@@ -622,8 +666,11 @@ CLEANFILES += \
distclean-local:
-rm -rf $(builddir)/mago.results
-DISTCLEANFILES = \
- $(TESTS) \
- $(OBJECT_XML_REPORT) \
- $(GTK_OBJECT_XML_REPORT)
+DISTCLEANFILES += \
+ $(filter-out %.py, $(TESTS))
+if USE_GTK3
+VER=3
+else
+VER=
+endif
diff --git a/tests/json-loader.c b/tests/json-loader.c
index 14e90e0..36157dc 100644
--- a/tests/json-loader.c
+++ b/tests/json-loader.c
@@ -109,7 +109,6 @@ set_props (DbusmenuMenuitem * mi, JsonObject * node)
if (variant != NULL) {
dbusmenu_menuitem_property_set_variant(mi, member, variant);
- g_variant_unref(variant);
}
}
diff --git a/tests/test-glib-layout-client.c b/tests/test-glib-layout-client.c
index 5ea0cf8..3afe042 100644
--- a/tests/test-glib-layout-client.c
+++ b/tests/test-glib-layout-client.c
@@ -81,6 +81,11 @@ layout_updated (DbusmenuClient * client, gpointer data)
g_debug("Layout Updated");
DbusmenuMenuitem * menuroot = dbusmenu_client_get_root(client);
+ if (menuroot == NULL) {
+ g_debug("Root NULL, waiting");
+ return;
+ }
+
layout_t * layout = &layouts[layouton];
if (!verify_root_to_layout(menuroot, layout)) {
diff --git a/tests/test-glib-simple-items.py b/tests/test-glib-simple-items.py
new file mode 100755
index 0000000..d7ad7d7
--- /dev/null
+++ b/tests/test-glib-simple-items.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# This is the Python GI version of test-glib-simple-items.c
+
+import gobject
+from gi.repository import Dbusmenu
+
+dummies = ['Bob', 'Jim', 'Alvin', 'Mary']
+
+def dummy_users(root):
+ count = 0
+ for user in dummies:
+ mi = Dbusmenu.Menuitem()
+ print 'Creating item: %d %s' % (mi.get_id(), user)
+ print '\tRoot ID:', root.get_id()
+ mi.property_set('label', user)
+ root.child_add_position(mi, count)
+ assert mi.property_get('label') == user
+ count += 1
+
+def quititall(mainloop):
+ mainloop.quit()
+ return False
+
+# main
+
+server = Dbusmenu.Server.new('/test/object')
+root_menuitem = Dbusmenu.Menuitem()
+server.set_root(root_menuitem)
+print 'Root ID:', root_menuitem.get_id()
+
+dummy_users(root_menuitem)
+
+mainloop = gobject.MainLoop()
+gobject.timeout_add_seconds(1, quititall, mainloop)
+mainloop.run()
diff --git a/tests/test-gtk-label-client.c b/tests/test-gtk-label-client.c
index 14eb5bd..415c6b0 100644
--- a/tests/test-gtk-label-client.c
+++ b/tests/test-gtk-label-client.c
@@ -160,7 +160,7 @@ main (int argc, char ** argv)
GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test")));
gtk_widget_show(menuitem);
- gtk_menu_bar_append(menubar, menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem);
gtk_widget_show(menubar);
gtk_container_add(GTK_CONTAINER(window), menubar);
gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
diff --git a/tests/test-gtk-label.json b/tests/test-gtk-label.json
index 755bd44..86d18e8 100644
--- a/tests/test-gtk-label.json
+++ b/tests/test-gtk-label.json
@@ -45,7 +45,7 @@
"label": "value27"},
{"id": 28, "type": "standard",
"label": "value28"},
- {"id": 29, "type": "standard", "visible": "false",
+ {"id": 29, "type": "standard", "visible": false,
"label": "value29"}
]
},
@@ -54,45 +54,45 @@
"submenu": [
{"id": 40,
"type": "standard",
- "enabled": "true",
+ "enabled": true,
"label": "value40"},
{"id": 41,
"type": "standard",
- "enabled": "false",
+ "enabled": false,
"label": "value41"},
{"id": 42,
"type": "standard",
- "enabled": "true",
+ "enabled": true,
"label": "value42"},
{"id": 43,
"type": "standard",
- "enabled": "false",
+ "enabled": false,
"label": "value43"},
{"id": 44,
"type": "standard",
- "enabled": "true",
+ "enabled": true,
"label": "value44"},
{"id": 45,
"type": "standard",
- "enabled": "false",
+ "enabled": false,
"label": "value45"},
{"id": 46,
"type": "standard",
- "enabled": "true",
+ "enabled": true,
"label": "value46"},
{"id": 47,
"type": "standard",
- "enabled": "false",
+ "enabled": false,
"label": "value47"},
{"id": 48,
"type": "standard",
- "enabled": "true",
+ "enabled": true,
"label": "value48"},
{"id": 49,
"type": "standard",
- "visible": "false",
- "enabled": "false",
- "label": "value49"}
+ "visible": false,
+ "enabled": false,
+ "label": "value49 - hidden"}
]
},
{"id": 3, "type": "standard",
@@ -367,5 +367,58 @@ QmCC",
"toggle-state": -1
}
]
+ },
+ {"id": 1, "type": "standard",
+ "label": "value1",
+ "submenu": [
+ {"id": 60,
+ "label": "Disposition Normal",
+ "disposition": "normal"
+ },
+ {"id": 61,
+ "label": "Disposition Info",
+ "disposition": "informational"
+ },
+ {"id": 62,
+ "label": "Disposition Warning",
+ "disposition": "warning"
+ },
+ {"id": 63,
+ "label": "Disposition Alert",
+ "disposition": "alert"
+ }
+ ]
+ },
+ {"id": 1, "type": "standard",
+ "label": "value1",
+ "submenu": [
+ {"id": 70,
+ "label": "_Mnemonic"
+ },
+ {"id": 71,
+ "label": "M_nemonic"
+ },
+ {"id": 72,
+ "label": "Mn_emonic"
+ },
+ {"id": 73,
+ "label": "Mne_monic"
+ },
+ {"id": 74,
+ "label": "Mnem_onic"
+ },
+ {"id": 75,
+ "label": "Mnemo_nic"
+ },
+ {"id": 76,
+ "label": "Mnemon_ic"
+ },
+ {"id": 77,
+ "label": "Mnemoni_c"
+ },
+ {"id": 78,
+ "label": "__Underline__"
+ }
+ ]
}
]
diff --git a/tests/test-gtk-objects.c b/tests/test-gtk-objects.c
index 30fc022..4bd26d3 100644
--- a/tests/test-gtk-objects.c
+++ b/tests/test-gtk-objects.c
@@ -97,7 +97,11 @@ test_object_prop_shortcut (void)
g_assert(G_IS_OBJECT(item));
g_assert(DBUSMENU_IS_MENUITEM(item));
+#if GTK_CHECK_VERSION(3,0,0)
+ guint key = GDK_KEY_c;
+#else
guint key = GDK_c;
+#endif
GdkModifierType modifier = GDK_CONTROL_MASK;
/* Set a shortcut */
diff --git a/tests/test-gtk-parser.c b/tests/test-gtk-parser.c
index b66b46a..87b0a7f 100644
--- a/tests/test-gtk-parser.c
+++ b/tests/test-gtk-parser.c
@@ -55,9 +55,23 @@ const gchar * test_parser_children_builder =
"</object>"
"</interface>";
+/* Checks the log level to let warnings not stop the program */
+static gboolean
+test_parser_children_log_handler (const gchar * domain, GLogLevelFlags level, const gchar * message, gpointer user_data)
+{
+ if (level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/* Ensure the parser can find children */
static void
test_parser_children (void) {
+ /* Hide GTK errors */
+ g_test_log_set_fatal_handler(test_parser_children_log_handler, NULL);
+
GtkBuilder * builder = gtk_builder_new();
g_assert(builder != NULL);
diff --git a/tests/test-gtk-shortcut-client.c b/tests/test-gtk-shortcut-client.c
index 003885c..259e177 100644
--- a/tests/test-gtk-shortcut-client.c
+++ b/tests/test-gtk-shortcut-client.c
@@ -53,7 +53,7 @@ main (int argc, char ** argv)
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dmenu));
gtk_widget_show(menuitem);
- gtk_menu_bar_append(menubar, menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem);
gtk_widget_show(menubar);
gtk_container_add(GTK_CONTAINER(window), menubar);
gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
diff --git a/tests/test-gtk-shortcut-client.py b/tests/test-gtk-shortcut-client.py
new file mode 100755
index 0000000..885d227
--- /dev/null
+++ b/tests/test-gtk-shortcut-client.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+# A test for libdbusmenu to ensure its quality. This is the Python GI version
+# of test-gtk-shortcut-client.c
+#
+# Copyright 2011 Canonical Ltd.
+# Authors:
+# Martin Pitt <martin.pitt@ubuntu.com>
+
+import sys
+import gobject
+from gi.repository import Gtk, DbusmenuGtk
+Gtk.require_version('2.0')
+
+passed = True
+main_loop = gobject.MainLoop()
+
+def timer_func(data):
+ passed = True
+ main_loop.quit()
+ return False
+
+# main
+print 'Building Window'
+window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
+menubar = Gtk.MenuBar()
+menuitem = Gtk.MenuItem(label='Test')
+
+dmenu = DbusmenuGtk.Menu(dbus_name='glib.label.test', dbus_object='/org/test')
+dclient = dmenu.get_client()
+agroup = Gtk.AccelGroup()
+dclient.set_accel_group(agroup)
+
+menuitem.set_submenu(dmenu)
+menuitem.show()
+menubar.append(menuitem)
+menubar.show()
+window.add(menubar)
+window.set_title('libdbusmenu-gtk test')
+window.add_accel_group(agroup)
+window.show_all()
+
+gobject.timeout_add_seconds(10, timer_func, window)
+
+print 'Entering Mainloop'
+main_loop.run()
+
+if passed:
+ print 'Quiting'
+else:
+ print "Quiting as we're a failure"
+ sys.exit(1)
diff --git a/tests/test-gtk-shortcut-server.c b/tests/test-gtk-shortcut-server.c
index b205d03..5df4881 100644
--- a/tests/test-gtk-shortcut-server.c
+++ b/tests/test-gtk-shortcut-server.c
@@ -46,7 +46,11 @@ build_menu (void)
item = dbusmenu_menuitem_new();
dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Control-L");
+#if GTK_CHECK_VERSION(3,0,0)
+ dbusmenu_menuitem_property_set_shortcut(item, GDK_KEY_l, GDK_CONTROL_MASK);
+#else
dbusmenu_menuitem_property_set_shortcut(item, GDK_l, GDK_CONTROL_MASK);
+#endif
dbusmenu_menuitem_child_append(root, item);
g_object_unref(item);
diff --git a/tests/test-gtk-submenu-client.c b/tests/test-gtk-submenu-client.c
index ec46122..81637f1 100644
--- a/tests/test-gtk-submenu-client.c
+++ b/tests/test-gtk-submenu-client.c
@@ -127,7 +127,7 @@ main (int argc, char ** argv)
GtkWidget * menuitem = gtk_menu_item_new_with_label("Test");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(dbusmenu_gtkmenu_new ("glib.label.test", "/org/test")));
gtk_widget_show(menuitem);
- gtk_menu_bar_append(menubar, menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem);
gtk_widget_show(menubar);
gtk_container_add(GTK_CONTAINER(window), menubar);
gtk_window_set_title(GTK_WINDOW(window), "libdbusmenu-gtk test");
diff --git a/tests/test-json-01.json b/tests/test-json-01.json
index b626d20..e3b1e17 100644
--- a/tests/test-json-01.json
+++ b/tests/test-json-01.json
@@ -5,30 +5,22 @@
{
"id": 5,
"children-display": 'submenu',
- "enabled": true,
"label": 'File',
- "visible": true,
"submenu": [
{
"id": 6,
- "enabled": true,
"label": 'Quit',
- "shortcut": [['Control', 'q']],
- "visible": true
+ "shortcut": [['Control', 'q']]
},
{
"id": 7,
- "enabled": true,
"label": 'Close all',
- "shortcut": [['Control', 'Shift', 'w']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'w']]
},
{
"id": 8,
- "enabled": true,
"label": 'Close',
- "shortcut": [['Control', 'w']],
- "visible": true
+ "shortcut": [['Control', 'w']]
},
{
"id": 9,
@@ -36,22 +28,16 @@
},
{
"id": 10,
- "enabled": true,
- "label": 'Send by Email...',
- "visible": true
+ "label": 'Send by Email...'
},
{
"id": 11,
- "enabled": true,
"label": 'Print...',
- "shortcut": [['Control', 'p']],
- "visible": true
+ "shortcut": [['Control', 'p']]
},
{
"id": 12,
- "enabled": true,
- "label": 'Page Setup',
- "visible": true
+ "label": 'Page Setup'
},
{
"id": 13,
@@ -59,35 +45,25 @@
},
{
"id": 14,
- "enabled": true,
- "label": 'Revert',
- "visible": true
+ "label": 'Revert'
},
{
"id": 15,
- "enabled": true,
- "label": 'Save as Template...',
- "visible": true
+ "label": 'Save as Template...'
},
{
"id": 16,
- "enabled": true,
- "label": 'Save a Copy...',
- "visible": true
+ "label": 'Save a Copy...'
},
{
"id": 17,
- "enabled": true,
"label": 'Save As...',
- "shortcut": [['Control', 'Shift', 's']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 's']]
},
{
"id": 18,
- "enabled": true,
"label": 'Save',
- "shortcut": [['Control', 's']],
- "visible": true
+ "shortcut": [['Control', 's']]
},
{
"id": 19,
@@ -96,15 +72,11 @@
{
"id": 20,
"children-display": 'submenu',
- "enabled": true,
"label": 'Open Recent',
- "visible": true,
"submenu": [
{
"id": 21,
- "enabled": true,
- "label": 'Document History',
- "visible": true
+ "label": 'Document History'
},
{
"id": 22,
@@ -112,168 +84,118 @@
},
{
"id": 23,
- "enabled": true,
"label": 'giggity.jpg',
- "shortcut": [['Control', '2']],
- "visible": true
+ "shortcut": [['Control', '2']]
},
{
"id": 24,
- "enabled": true,
"label": 'Icon Height.svg',
- "shortcut": [['Control', '1']],
- "visible": true
+ "shortcut": [['Control', '1']]
}
]
},
{
"id": 25,
- "enabled": true,
- "label": 'Open Location...',
- "visible": true
+ "label": 'Open Location...'
},
{
"id": 26,
- "enabled": true,
"label": 'Open as Layers...',
- "shortcut": [['Control', 'Alt', 'o']],
- "visible": true
+ "shortcut": [['Control', 'Alt', 'o']]
},
{
"id": 27,
- "enabled": true,
"label": 'Open...',
- "shortcut": [['Control', 'o']],
- "visible": true
+ "shortcut": [['Control', 'o']]
},
{
"id": 28,
"children-display": 'submenu',
- "enabled": true,
"label": 'Create',
- "visible": true,
"submenu": [
{
"id": 29,
"children-display": 'submenu',
- "enabled": true,
"label": 'Web Page Themes',
- "visible": true,
"submenu": [
{
"id": 30,
"children-display": 'submenu',
- "enabled": true,
"label": 'Classic.Gimp.Org',
- "visible": true,
"submenu": [
{
"id": 31,
- "enabled": true,
- "label": 'Tube Sub-Sub-Button Label...',
- "visible": true
+ "label": 'Tube Sub-Sub-Button Label...'
},
{
"id": 32,
- "enabled": true,
- "label": 'Tube Sub-Button Label...',
- "visible": true
+ "label": 'Tube Sub-Button Label...'
},
{
"id": 33,
- "enabled": true,
- "label": 'Tube Button Label...',
- "visible": true
+ "label": 'Tube Button Label...'
},
{
"id": 34,
- "enabled": true,
- "label": 'Small Header...',
- "visible": true
+ "label": 'Small Header...'
},
{
"id": 35,
- "enabled": true,
- "label": 'General Tube Labels...',
- "visible": true
+ "label": 'General Tube Labels...'
},
{
"id": 36,
- "enabled": true,
- "label": 'Big Header...',
- "visible": true
+ "label": 'Big Header...'
}
]
},
{
"id": 37,
"children-display": 'submenu',
- "enabled": true,
"label": 'Beveled Pattern',
- "visible": true,
"submenu": [
{
"id": 38,
- "enabled": true,
- "label": 'Hrule...',
- "visible": true
+ "label": 'Hrule...'
},
{
"id": 39,
- "enabled": true,
- "label": 'Heading...',
- "visible": true
+ "label": 'Heading...'
},
{
"id": 40,
- "enabled": true,
- "label": 'Button...',
- "visible": true
+ "label": 'Button...'
},
{
"id": 41,
- "enabled": true,
- "label": 'Bullet...',
- "visible": true
+ "label": 'Bullet...'
},
{
"id": 42,
- "enabled": true,
- "label": 'Arrow...',
- "visible": true
+ "label": 'Arrow...'
}
]
},
{
"id": 43,
"children-display": 'submenu',
- "enabled": true,
"label": 'Alien Glow',
- "visible": true,
"submenu": [
{
"id": 44,
- "enabled": true,
- "label": 'Hrule...',
- "visible": true
+ "label": 'Hrule...'
},
{
"id": 45,
- "enabled": true,
- "label": 'Button...',
- "visible": true
+ "label": 'Button...'
},
{
"id": 46,
- "enabled": true,
- "label": 'Bullet...',
- "visible": true
+ "label": 'Bullet...'
},
{
"id": 47,
- "enabled": true,
- "label": 'Arrow...',
- "visible": true
+ "label": 'Arrow...'
}
]
}
@@ -282,255 +204,173 @@
{
"id": 48,
"children-display": 'submenu',
- "enabled": true,
"label": 'Patterns',
- "visible": true,
"submenu": [
{
"id": 49,
- "enabled": true,
- "label": 'Truchet...',
- "visible": true
+ "label": 'Truchet...'
},
{
"id": 50,
- "enabled": true,
- "label": 'Swirly...',
- "visible": true
+ "label": 'Swirly...'
},
{
"id": 51,
- "enabled": true,
- "label": 'Swirl-Tile...',
- "visible": true
+ "label": 'Swirl-Tile...'
},
{
"id": 52,
- "enabled": true,
- "label": 'Render Map...',
- "visible": true
+ "label": 'Render Map...'
},
{
"id": 53,
- "enabled": true,
- "label": 'Land...',
- "visible": true
+ "label": 'Land...'
},
{
"id": 54,
- "enabled": true,
- "label": 'Flatland...',
- "visible": true
+ "label": 'Flatland...'
},
{
"id": 55,
- "enabled": true,
- "label": 'Camouflage...',
- "visible": true
+ "label": 'Camouflage...'
},
{
"id": 56,
- "enabled": true,
- "label": '3D Truchet...',
- "visible": true
+ "label": '3D Truchet...'
}
]
},
{
"id": 57,
"children-display": 'submenu',
- "enabled": true,
"label": 'Logos',
- "visible": true,
"submenu": [
{
"id": 58,
- "enabled": true,
- "label": 'Web Title Header...',
- "visible": true
+ "label": 'Web Title Header...'
},
{
"id": 59,
- "enabled": true,
- "label": 'Textured...',
- "visible": true
+ "label": 'Textured...'
},
{
"id": 60,
- "enabled": true,
- "label": 'Text Circle...',
- "visible": true
+ "label": 'Text Circle...'
},
{
"id": 61,
- "enabled": true,
- "label": 'Starscape...',
- "visible": true
+ "label": 'Starscape...'
},
{
"id": 62,
- "enabled": true,
- "label": 'Speed Text...',
- "visible": true
+ "label": 'Speed Text...'
},
{
"id": 63,
- "enabled": true,
- "label": 'SOTA Chrome...',
- "visible": true
+ "label": 'SOTA Chrome...'
},
{
"id": 64,
- "enabled": true,
- "label": 'Particle Trace...',
- "visible": true
+ "label": 'Particle Trace...'
},
{
"id": 65,
- "enabled": true,
- "label": 'Newsprint Text...',
- "visible": true
+ "label": 'Newsprint Text...'
},
{
"id": 66,
- "enabled": true,
- "label": 'Neon...',
- "visible": true
+ "label": 'Neon...'
},
{
"id": 67,
- "enabled": true,
- "label": 'Imigre-26...',
- "visible": true
+ "label": 'Imigre-26...'
},
{
"id": 68,
- "enabled": true,
- "label": 'Gradient Bevel...',
- "visible": true
+ "label": 'Gradient Bevel...'
},
{
"id": 69,
- "enabled": true,
- "label": 'Glowing Hot...',
- "visible": true
+ "label": 'Glowing Hot...'
},
{
"id": 70,
- "enabled": true,
- "label": 'Glossy...',
- "visible": true
+ "label": 'Glossy...'
},
{
"id": 71,
- "enabled": true,
- "label": 'Frosty...',
- "visible": true
+ "label": 'Frosty...'
},
{
"id": 72,
- "enabled": true,
- "label": 'Crystal...',
- "visible": true
+ "label": 'Crystal...'
},
{
"id": 73,
- "enabled": true,
- "label": 'Cool Metal...',
- "visible": true
+ "label": 'Cool Metal...'
},
{
"id": 74,
- "enabled": true,
- "label": 'Comic Book...',
- "visible": true
+ "label": 'Comic Book...'
},
{
"id": 75,
- "enabled": true,
- "label": 'Chrome...',
- "visible": true
+ "label": 'Chrome...'
},
{
"id": 76,
- "enabled": true,
- "label": 'Chip Away...',
- "visible": true
+ "label": 'Chip Away...'
},
{
"id": 77,
- "enabled": true,
- "label": 'Chalk...',
- "visible": true
+ "label": 'Chalk...'
},
{
"id": 78,
- "enabled": true,
- "label": 'Carved...',
- "visible": true
+ "label": 'Carved...'
},
{
"id": 79,
- "enabled": true,
- "label": 'Bovination...',
- "visible": true
+ "label": 'Bovination...'
},
{
"id": 80,
- "enabled": true,
- "label": 'Blended...',
- "visible": true
+ "label": 'Blended...'
},
{
"id": 81,
- "enabled": true,
- "label": 'Basic I...',
- "visible": true
+ "label": 'Basic I...'
},
{
"id": 82,
- "enabled": true,
- "label": 'Basic II...',
- "visible": true
+ "label": 'Basic II...'
},
{
"id": 83,
- "enabled": true,
- "label": 'Alien Neon...',
- "visible": true
+ "label": 'Alien Neon...'
},
{
"id": 84,
- "enabled": true,
- "label": 'Alien Glow...',
- "visible": true
+ "label": 'Alien Glow...'
},
{
"id": 85,
- "enabled": true,
- "label": '3D Outline...',
- "visible": true
+ "label": '3D Outline...'
}
]
},
{
"id": 86,
"children-display": 'submenu',
- "enabled": true,
"label": 'Buttons',
- "visible": true,
"submenu": [
{
"id": 87,
- "enabled": true,
- "label": 'Simple Beveled Button...',
- "visible": true
+ "label": 'Simple Beveled Button...'
},
{
"id": 88,
- "enabled": true,
- "label": 'Round Button...',
- "visible": true
+ "label": 'Round Button...'
}
]
},
@@ -541,72 +381,53 @@
{
"id": 90,
"children-display": 'submenu',
- "enabled": true,
"label": 'xscanimage',
- "visible": true,
"submenu": [
{
"id": 91,
"enabled": false,
- "label": 'Device dialog...',
- "visible": true
+ "label": 'Device dialog...'
}
]
},
{
"id": 92,
- "enabled": true,
- "label": 'Screenshot...',
- "visible": true
+ "label": 'Screenshot...'
},
{
"id": 93,
- "enabled": true,
"label": 'From Clipboard',
- "shortcut": [['Control', 'Shift', 'v']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'v']]
}
]
},
{
"id": 94,
- "enabled": true,
"label": 'New...',
- "shortcut": [['Control', 'n']],
- "visible": true
+ "shortcut": [['Control', 'n']]
}
]
},
{
"id": 95,
"children-display": 'submenu',
- "enabled": true,
"label": 'Edit',
- "visible": true,
"submenu": [
{
"id": 96,
- "enabled": true,
- "label": 'Units',
- "visible": true
+ "label": 'Units'
},
{
"id": 97,
- "enabled": true,
- "label": 'Modules',
- "visible": true
+ "label": 'Modules'
},
{
"id": 98,
- "enabled": true,
- "label": 'Keyboard Shortcuts',
- "visible": true
+ "label": 'Keyboard Shortcuts'
},
{
"id": 99,
- "enabled": true,
- "label": 'Preferences',
- "visible": true
+ "label": 'Preferences'
},
{
"id": 100,
@@ -615,42 +436,32 @@
{
"id": 101,
"enabled": false,
- "label": 'Stroke Path...',
- "visible": true
+ "label": 'Stroke Path...'
},
{
"id": 102,
"enabled": false,
- "label": 'Stroke Selection...',
- "visible": true
+ "label": 'Stroke Selection...'
},
{
"id": 103,
- "enabled": true,
"label": 'Fill with Pattern',
- "shortcut": [['Control', 'semicolon']],
- "visible": true
+ "shortcut": [['Control', 'semicolon']]
},
{
"id": 104,
- "enabled": true,
"label": 'Fill with BG Color',
- "shortcut": [['Control', 'period']],
- "visible": true
+ "shortcut": [['Control', 'period']]
},
{
"id": 105,
- "enabled": true,
"label": 'Fill with FG Color',
- "shortcut": [['Control', 'comma']],
- "visible": true
+ "shortcut": [['Control', 'comma']]
},
{
"id": 106,
- "enabled": true,
"label": 'Clear',
- "shortcut": [['Delete']],
- "visible": true
+ "shortcut": [['Delete']]
},
{
"id": 107,
@@ -659,103 +470,73 @@
{
"id": 108,
"children-display": 'submenu',
- "enabled": true,
"label": 'Buffer',
- "visible": true,
"submenu": [
{
"id": 109,
- "enabled": true,
- "label": 'Paste Named...',
- "visible": true
+ "label": 'Paste Named...'
},
{
"id": 110,
- "enabled": true,
- "label": 'Copy Visible Named...',
- "visible": true
+ "label": 'Copy Visible Named...'
},
{
"id": 111,
- "enabled": true,
- "label": 'Copy Named...',
- "visible": true
+ "label": 'Copy Named...'
},
{
"id": 112,
- "enabled": true,
- "label": 'Cut Named...',
- "visible": true
+ "label": 'Cut Named...'
}
]
},
{
"id": 113,
"children-display": 'submenu',
- "enabled": true,
"label": 'Paste as',
- "visible": true,
"submenu": [
{
"id": 114,
- "enabled": true,
- "label": 'New Pattern...',
- "visible": true
+ "label": 'New Pattern...'
},
{
"id": 115,
- "enabled": true,
- "label": 'New Brush...',
- "visible": true
+ "label": 'New Brush...'
},
{
"id": 116,
- "enabled": true,
- "label": 'New Layer',
- "visible": true
+ "label": 'New Layer'
},
{
"id": 117,
- "enabled": true,
"label": 'New Image',
- "shortcut": [['Control', 'Shift', 'v']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'v']]
}
]
},
{
"id": 118,
- "enabled": true,
- "label": 'Paste Into',
- "visible": true
+ "label": 'Paste Into'
},
{
"id": 119,
- "enabled": true,
"label": 'Paste',
- "shortcut": [['Control', 'v']],
- "visible": true
+ "shortcut": [['Control', 'v']]
},
{
"id": 120,
- "enabled": true,
"label": 'Copy Visible',
- "shortcut": [['Control', 'Shift', 'c']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'c']]
},
{
"id": 121,
- "enabled": true,
"label": 'Copy',
- "shortcut": [['Control', 'c']],
- "visible": true
+ "shortcut": [['Control', 'c']]
},
{
"id": 122,
- "enabled": true,
"label": 'Cut',
- "shortcut": [['Control', 'x']],
- "visible": true
+ "shortcut": [['Control', 'x']]
},
{
"id": 123,
@@ -763,59 +544,47 @@
},
{
"id": 124,
- "enabled": true,
- "label": 'Undo History',
- "visible": true
+ "label": 'Undo History'
},
{
"id": 3,
"enabled": false,
- "label": '_Fade...',
- "visible": true
+ "label": '_Fade...'
},
{
"id": 2,
"enabled": false,
"label": '_Redo',
- "shortcut": [['Control', 'y']],
- "visible": true
+ "shortcut": [['Control', 'y']]
},
{
"id": 1,
"enabled": false,
"label": '_Undo',
- "shortcut": [['Control', 'z']],
- "visible": true
+ "shortcut": [['Control', 'z']]
}
]
},
{
"id": 125,
"children-display": 'submenu',
- "enabled": true,
"label": 'Select',
- "visible": true,
"submenu": [
{
"id": 126,
"enabled": false,
- "label": 'To Path',
- "visible": true
+ "label": 'To Path'
},
{
"id": 127,
- "enabled": true,
- "label": 'Save to Channel',
- "visible": true
+ "label": 'Save to Channel'
},
{
"id": 128,
- "enabled": true,
"label": 'Toggle Quick Mask',
"shortcut": [['Shift', 'q']],
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 129,
@@ -823,39 +592,32 @@
},
{
"id": 130,
- "enabled": true,
- "label": 'Distort...',
- "visible": true
+ "label": 'Distort...'
},
{
"id": 131,
"enabled": false,
- "label": 'Border...',
- "visible": true
+ "label": 'Border...'
},
{
"id": 132,
"enabled": false,
- "label": 'Grow...',
- "visible": true
+ "label": 'Grow...'
},
{
"id": 133,
"enabled": false,
- "label": 'Shrink...',
- "visible": true
+ "label": 'Shrink...'
},
{
"id": 134,
"enabled": false,
- "label": 'Sharpen',
- "visible": true
+ "label": 'Sharpen'
},
{
"id": 135,
"enabled": false,
- "label": 'Feather...',
- "visible": true
+ "label": 'Feather...'
},
{
"id": 136,
@@ -863,106 +625,81 @@
},
{
"id": 137,
- "enabled": true,
- "label": 'Selection Editor',
- "visible": true
+ "label": 'Selection Editor'
},
{
"id": 138,
"enabled": false,
"label": 'From Path',
- "shortcut": [['Shift', 'v']],
- "visible": true
+ "shortcut": [['Shift', 'v']]
},
{
"id": 139,
- "enabled": true,
"label": 'By Color',
- "shortcut": [['Shift', 'o']],
- "visible": true
+ "shortcut": [['Shift', 'o']]
},
{
"id": 140,
"enabled": false,
"label": 'Float',
- "shortcut": [['Control', 'Shift', 'l']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'l']]
},
{
"id": 141,
- "enabled": true,
"label": 'Invert',
- "shortcut": [['Control', 'i']],
- "visible": true
+ "shortcut": [['Control', 'i']]
},
{
"id": 142,
"enabled": false,
"label": 'None',
- "shortcut": [['Control', 'Shift', 'a']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'a']]
},
{
"id": 143,
- "enabled": true,
"label": 'All',
- "shortcut": [['Control', 'a']],
- "visible": true
+ "shortcut": [['Control', 'a']]
}
]
},
{
"id": 144,
"children-display": 'submenu',
- "enabled": true,
"label": 'View',
- "visible": true,
"submenu": [
{
"id": 145,
- "enabled": true,
"label": 'Show Statusbar',
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 146,
- "enabled": true,
"label": 'Show Scrollbars',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 147,
- "enabled": true,
"label": 'Show Rulers',
"shortcut": [['Control', 'Shift', 'r']],
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 148,
- "enabled": true,
"label": 'Show Menubar',
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 149,
"children-display": 'submenu',
- "enabled": true,
"label": 'Padding Color',
- "visible": true,
"submenu": [
{
"id": 150,
- "enabled": true,
- "label": 'As in Preferences',
- "visible": true
+ "label": 'As in Preferences'
},
{
"id": 151,
@@ -970,27 +707,19 @@
},
{
"id": 152,
- "enabled": true,
- "label": 'Select Custom Color...',
- "visible": true
+ "label": 'Select Custom Color...'
},
{
"id": 153,
- "enabled": true,
- "label": 'Dark Check Color',
- "visible": true
+ "label": 'Dark Check Color'
},
{
"id": 154,
- "enabled": true,
- "label": 'Light Check Color',
- "visible": true
+ "label": 'Light Check Color'
},
{
"id": 155,
- "enabled": true,
- "label": 'From Theme',
- "visible": true
+ "label": 'From Theme'
}
]
},
@@ -1000,35 +729,27 @@
},
{
"id": 157,
- "enabled": true,
"label": 'Snap to Active Path',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 158,
- "enabled": true,
"label": 'Snap to Canvas Edges',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 159,
- "enabled": true,
"label": 'Snap to Grid',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 160,
- "enabled": true,
"label": 'Snap to Guides',
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 161,
@@ -1036,45 +757,35 @@
},
{
"id": 162,
- "enabled": true,
"label": 'Show Sample Points',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 163,
- "enabled": true,
"label": 'Show Grid',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 164,
- "enabled": true,
"label": 'Show Guides',
"shortcut": [['Control', 'Shift', 't']],
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 165,
- "enabled": true,
"label": 'Show Layer Boundary',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 166,
- "enabled": true,
"label": 'Show Selection',
"shortcut": [['Control', 't']],
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 167,
@@ -1082,15 +793,11 @@
},
{
"id": 168,
- "enabled": true,
- "label": 'Display Filters...',
- "visible": true
+ "label": 'Display Filters...'
},
{
"id": 169,
- "enabled": true,
- "label": 'Navigation Window',
- "visible": true
+ "label": 'Navigation Window'
},
{
"id": 170,
@@ -1099,27 +806,21 @@
{
"id": 171,
"children-display": 'submenu',
- "enabled": true,
"label": 'Fullscreen',
"shortcut": [['F11']],
"toggle-state": 0,
"toggle-type": 'checkmark',
- "visible": true,
"submenu": [
{
"id": 172,
- "enabled": true,
- "label": 'Open Display...',
- "visible": true
+ "label": 'Open Display...'
}
]
},
{
"id": 173,
- "enabled": true,
"label": 'Shrink Wrap',
- "shortcut": [['Control', 'e']],
- "visible": true
+ "shortcut": [['Control', 'e']]
},
{
"id": 174,
@@ -1128,17 +829,13 @@
{
"id": 175,
"children-display": 'submenu',
- "enabled": true,
"label": '_Zoom (67%)',
- "visible": true,
"submenu": [
{
"id": 176,
- "enabled": true,
"label": 'Othe_r (67%)...',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 177,
@@ -1146,76 +843,58 @@
},
{
"id": 178,
- "enabled": true,
"label": '1:16 (6.25%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 179,
- "enabled": true,
"label": '1:8 (12.5%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 180,
- "enabled": true,
"label": '1:4 (25%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 181,
- "enabled": true,
"label": '1:2 (50%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 182,
- "enabled": true,
"label": '1:1 (100%)',
"shortcut": [['1']],
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 183,
- "enabled": true,
"label": '2:1 (200%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 184,
- "enabled": true,
"label": '4:1 (400%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 185,
- "enabled": true,
"label": '8:1 (800%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 186,
- "enabled": true,
"label": '16:1 (1600%)',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 187,
@@ -1223,106 +902,76 @@
},
{
"id": 188,
- "enabled": true,
- "label": 'Fill Window',
- "visible": true
+ "label": 'Fill Window'
},
{
"id": 189,
- "enabled": true,
"label": 'Fit Image in Window',
- "shortcut": [['Control', 'Shift', 'e']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'e']]
},
{
"id": 190,
- "enabled": true,
"label": 'Zoom In',
- "shortcut": [['plus']],
- "visible": true
+ "shortcut": [['plus']]
},
{
"id": 191,
- "enabled": true,
"label": 'Zoom Out',
- "shortcut": [['minus']],
- "visible": true
+ "shortcut": [['minus']]
},
{
"id": 4,
- "enabled": true,
"label": 'Re_vert Zoom (67%)',
- "shortcut": [['grave']],
- "visible": true
+ "shortcut": [['grave']]
}
]
},
{
"id": 192,
- "enabled": true,
"label": 'Dot for Dot',
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 193,
- "enabled": true,
- "label": 'New View',
- "visible": true
+ "label": 'New View'
}
]
},
{
"id": 194,
"children-display": 'submenu',
- "enabled": true,
"label": 'Image',
- "visible": true,
"submenu": [
{
"id": 195,
- "enabled": true,
"label": 'Image Properties',
- "shortcut": [['Alt', 'Return']],
- "visible": true
+ "shortcut": [['Alt', 'Return']]
},
{
"id": 196,
- "enabled": true,
- "label": 'Configure Grid...',
- "visible": true
+ "label": 'Configure Grid...'
},
{
"id": 197,
"children-display": 'submenu',
- "enabled": true,
"label": 'Guides',
- "visible": true,
"submenu": [
{
"id": 198,
- "enabled": true,
- "label": 'Remove all Guides',
- "visible": true
+ "label": 'Remove all Guides'
},
{
"id": 199,
- "enabled": true,
- "label": 'New Guides from Selection',
- "visible": true
+ "label": 'New Guides from Selection'
},
{
"id": 200,
- "enabled": true,
- "label": 'New Guide...',
- "visible": true
+ "label": 'New Guide...'
},
{
"id": 201,
- "enabled": true,
- "label": 'New Guide (by Percent)...',
- "visible": true
+ "label": 'New Guide (by Percent)...'
}
]
},
@@ -1332,22 +981,16 @@
},
{
"id": 203,
- "enabled": true,
- "label": 'Align Visible Layers...',
- "visible": true
+ "label": 'Align Visible Layers...'
},
{
"id": 204,
- "enabled": true,
- "label": 'Flatten Image',
- "visible": true
+ "label": 'Flatten Image'
},
{
"id": 205,
- "enabled": true,
"label": 'Merge Visible Layers...',
- "shortcut": [['Control', 'm']],
- "visible": true
+ "shortcut": [['Control', 'm']]
},
{
"id": 206,
@@ -1355,21 +998,16 @@
},
{
"id": 207,
- "enabled": true,
- "label": 'Zealous Crop',
- "visible": true
+ "label": 'Zealous Crop'
},
{
"id": 208,
- "enabled": true,
- "label": 'Autocrop Image',
- "visible": true
+ "label": 'Autocrop Image'
},
{
"id": 209,
"enabled": false,
- "label": 'Crop to Selection',
- "visible": true
+ "label": 'Crop to Selection'
},
{
"id": 210,
@@ -1377,33 +1015,24 @@
},
{
"id": 211,
- "enabled": true,
- "label": 'Scale Image...',
- "visible": true
+ "label": 'Scale Image...'
},
{
"id": 212,
- "enabled": true,
- "label": 'Print Size...',
- "visible": true
+ "label": 'Print Size...'
},
{
"id": 213,
"enabled": false,
- "label": 'Fit Canvas to Selection',
- "visible": true
+ "label": 'Fit Canvas to Selection'
},
{
"id": 214,
- "enabled": true,
- "label": 'Fit Canvas to Layers',
- "visible": true
+ "label": 'Fit Canvas to Layers'
},
{
"id": 215,
- "enabled": true,
- "label": 'Canvas Size...',
- "visible": true
+ "label": 'Canvas Size...'
},
{
"id": 216,
@@ -1412,15 +1041,11 @@
{
"id": 217,
"children-display": 'submenu',
- "enabled": true,
"label": 'Transform',
- "visible": true,
"submenu": [
{
"id": 218,
- "enabled": true,
- "label": 'Guillotine',
- "visible": true
+ "label": 'Guillotine'
},
{
"id": 219,
@@ -1428,21 +1053,15 @@
},
{
"id": 220,
- "enabled": true,
- "label": 'Rotate 180?',
- "visible": true
+ "label": 'Rotate 180?'
},
{
"id": 221,
- "enabled": true,
- "label": 'Rotate 90? counter-clockwise',
- "visible": true
+ "label": 'Rotate 90? counter-clockwise'
},
{
"id": 222,
- "enabled": true,
- "label": 'Rotate 90? clockwise',
- "visible": true
+ "label": 'Rotate 90? clockwise'
},
{
"id": 223,
@@ -1450,36 +1069,26 @@
},
{
"id": 224,
- "enabled": true,
- "label": 'Flip Vertically',
- "visible": true
+ "label": 'Flip Vertically'
},
{
"id": 225,
- "enabled": true,
- "label": 'Flip Horizontally',
- "visible": true
+ "label": 'Flip Horizontally'
}
]
},
{
"id": 226,
"children-display": 'submenu',
- "enabled": true,
"label": 'Mode',
- "visible": true,
"submenu": [
{
"id": 227,
- "enabled": true,
- "label": 'Convert to Color Profile...',
- "visible": true
+ "label": 'Convert to Color Profile...'
},
{
"id": 228,
- "enabled": true,
- "label": 'Assign Color Profile...',
- "visible": true
+ "label": 'Assign Color Profile...'
},
{
"id": 229,
@@ -1487,75 +1096,56 @@
},
{
"id": 230,
- "enabled": true,
"label": 'Indexed...',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 231,
- "enabled": true,
"label": 'Grayscale',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 232,
- "enabled": true,
"label": 'RGB',
"toggle-state": 1,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
}
]
},
{
"id": 233,
- "enabled": true,
"label": 'Duplicate',
- "shortcut": [['Control', 'd']],
- "visible": true
+ "shortcut": [['Control', 'd']]
}
]
},
{
"id": 234,
"children-display": 'submenu',
- "enabled": true,
"label": 'Layer',
- "visible": true,
"submenu": [
{
"id": 235,
- "enabled": true,
- "label": 'Autocrop Layer',
- "visible": true
+ "label": 'Autocrop Layer'
},
{
"id": 236,
"enabled": false,
- "label": 'Crop to Selection',
- "visible": true
+ "label": 'Crop to Selection'
},
{
"id": 237,
- "enabled": true,
- "label": 'Scale Layer...',
- "visible": true
+ "label": 'Scale Layer...'
},
{
"id": 238,
- "enabled": true,
- "label": 'Layer to Image Size',
- "visible": true
+ "label": 'Layer to Image Size'
},
{
"id": 239,
- "enabled": true,
- "label": 'Layer Boundary Size...',
- "visible": true
+ "label": 'Layer Boundary Size...'
},
{
"id": 240,
@@ -1564,16 +1154,12 @@
{
"id": 241,
"children-display": 'submenu',
- "enabled": true,
"label": 'Transform',
- "visible": true,
"submenu": [
{
"id": 242,
- "enabled": true,
"label": 'Offset...',
- "shortcut": [['Control', 'Shift', 'o']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'o']]
},
{
"id": 243,
@@ -1581,27 +1167,19 @@
},
{
"id": 244,
- "enabled": true,
- "label": 'Arbitrary Rotation...',
- "visible": true
+ "label": 'Arbitrary Rotation...'
},
{
"id": 245,
- "enabled": true,
- "label": 'Rotate 180?',
- "visible": true
+ "label": 'Rotate 180?'
},
{
"id": 246,
- "enabled": true,
- "label": 'Rotate 90? counter-clockwise',
- "visible": true
+ "label": 'Rotate 90? counter-clockwise'
},
{
"id": 247,
- "enabled": true,
- "label": 'Rotate 90? clockwise',
- "visible": true
+ "label": 'Rotate 90? clockwise'
},
{
"id": 248,
@@ -1609,48 +1187,34 @@
},
{
"id": 249,
- "enabled": true,
- "label": 'Flip Vertically',
- "visible": true
+ "label": 'Flip Vertically'
},
{
"id": 250,
- "enabled": true,
- "label": 'Flip Horizontally',
- "visible": true
+ "label": 'Flip Horizontally'
}
]
},
{
"id": 251,
"children-display": 'submenu',
- "enabled": true,
"label": 'Transparency',
- "visible": true,
"submenu": [
{
"id": 252,
- "enabled": true,
- "label": 'Intersect with Selection',
- "visible": true
+ "label": 'Intersect with Selection'
},
{
"id": 253,
- "enabled": true,
- "label": 'Subtract from Selection',
- "visible": true
+ "label": 'Subtract from Selection'
},
{
"id": 254,
- "enabled": true,
- "label": 'Add to Selection',
- "visible": true
+ "label": 'Add to Selection'
},
{
"id": 255,
- "enabled": true,
- "label": 'Alpha to Selection',
- "visible": true
+ "label": 'Alpha to Selection'
},
{
"id": 256,
@@ -1658,66 +1222,51 @@
},
{
"id": 257,
- "enabled": true,
- "label": 'Threshold Alpha...',
- "visible": true
+ "label": 'Threshold Alpha...'
},
{
"id": 258,
- "enabled": true,
- "label": 'Semi-Flatten',
- "visible": true
+ "label": 'Semi-Flatten'
},
{
"id": 259,
- "enabled": true,
- "label": 'Color to Alpha...',
- "visible": true
+ "label": 'Color to Alpha...'
},
{
"id": 260,
- "enabled": true,
- "label": 'Remove Alpha Channel',
- "visible": true
+ "label": 'Remove Alpha Channel'
},
{
"id": 261,
"enabled": false,
- "label": 'Add Alpha Channel',
- "visible": true
+ "label": 'Add Alpha Channel'
}
]
},
{
"id": 262,
"children-display": 'submenu',
- "enabled": true,
"label": 'Mask',
- "visible": true,
"submenu": [
{
"id": 263,
"enabled": false,
- "label": 'Intersect with Selection',
- "visible": true
+ "label": 'Intersect with Selection'
},
{
"id": 264,
"enabled": false,
- "label": 'Subtract from Selection',
- "visible": true
+ "label": 'Subtract from Selection'
},
{
"id": 265,
"enabled": false,
- "label": 'Add to Selection',
- "visible": true
+ "label": 'Add to Selection'
},
{
"id": 266,
"enabled": false,
- "label": 'Mask to Selection',
- "visible": true
+ "label": 'Mask to Selection'
},
{
"id": 267,
@@ -1728,24 +1277,21 @@
"enabled": false,
"label": 'Disable Layer Mask',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 269,
"enabled": false,
"label": 'Edit Layer Mask',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 270,
"enabled": false,
"label": 'Show Layer Mask',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 271,
@@ -1754,35 +1300,27 @@
{
"id": 272,
"enabled": false,
- "label": 'Delete Layer Mask',
- "visible": true
+ "label": 'Delete Layer Mask'
},
{
"id": 273,
"enabled": false,
- "label": 'Apply Layer Mask',
- "visible": true
+ "label": 'Apply Layer Mask'
},
{
"id": 274,
- "enabled": true,
- "label": 'Add Layer Mask...',
- "visible": true
+ "label": 'Add Layer Mask...'
}
]
},
{
"id": 275,
"children-display": 'submenu',
- "enabled": true,
"label": 'Stack',
- "visible": true,
"submenu": [
{
"id": 276,
- "enabled": true,
- "label": 'Reverse Layer Order',
- "visible": true
+ "label": 'Reverse Layer Order'
},
{
"id": 277,
@@ -1791,26 +1329,22 @@
{
"id": 278,
"enabled": false,
- "label": 'Layer to Bottom',
- "visible": true
+ "label": 'Layer to Bottom'
},
{
"id": 279,
"enabled": false,
- "label": 'Layer to Top',
- "visible": true
+ "label": 'Layer to Top'
},
{
"id": 280,
"enabled": false,
- "label": 'Lower Layer',
- "visible": true
+ "label": 'Lower Layer'
},
{
"id": 281,
"enabled": false,
- "label": 'Raise Layer',
- "visible": true
+ "label": 'Raise Layer'
},
{
"id": 282,
@@ -1820,29 +1354,25 @@
"id": 283,
"enabled": false,
"label": 'Select Bottom Layer',
- "shortcut": [['End']],
- "visible": true
+ "shortcut": [['End']]
},
{
"id": 284,
"enabled": false,
"label": 'Select Top Layer',
- "shortcut": [['Home']],
- "visible": true
+ "shortcut": [['Home']]
},
{
"id": 285,
"enabled": false,
"label": 'Select Next Layer',
- "shortcut": [['Page_Down']],
- "visible": true
+ "shortcut": [['Page_Down']]
},
{
"id": 286,
"enabled": false,
"label": 'Select Previous Layer',
- "shortcut": [['Page_Up']],
- "visible": true
+ "shortcut": [['Page_Up']]
}
]
},
@@ -1854,94 +1384,70 @@
{
"id": 288,
"enabled": false,
- "label": 'Empty',
- "visible": true
+ "label": 'Empty'
}
]
},
{
"id": 289,
- "enabled": true,
- "label": 'Delete Layer',
- "visible": true
+ "label": 'Delete Layer'
},
{
"id": 290,
"enabled": false,
- "label": 'Merge Down',
- "visible": true
+ "label": 'Merge Down'
},
{
"id": 291,
"enabled": false,
"label": 'Anchor Layer',
- "shortcut": [['Control', 'h']],
- "visible": true
+ "shortcut": [['Control', 'h']]
},
{
"id": 292,
- "enabled": true,
"label": 'Duplicate Layer',
- "shortcut": [['Control', 'Shift', 'd']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'd']]
},
{
"id": 293,
- "enabled": true,
- "label": 'New from Visible',
- "visible": true
+ "label": 'New from Visible'
},
{
"id": 294,
- "enabled": true,
"label": 'New Layer...',
- "shortcut": [['Control', 'Shift', 'n']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'n']]
}
]
},
{
"id": 295,
"children-display": 'submenu',
- "enabled": true,
"label": 'Colors',
- "visible": true,
"submenu": [
{
"id": 296,
- "enabled": true,
- "label": 'Retinex...',
- "visible": true
+ "label": 'Retinex...'
},
{
"id": 297,
- "enabled": true,
- "label": 'Maximum RGB...',
- "visible": true
+ "label": 'Maximum RGB...'
},
{
"id": 298,
"enabled": false,
- "label": 'Hot...',
- "visible": true
+ "label": 'Hot...'
},
{
"id": 299,
- "enabled": true,
- "label": 'Filter Pack...',
- "visible": true
+ "label": 'Filter Pack...'
},
{
"id": 300,
- "enabled": true,
- "label": 'Color to Alpha...',
- "visible": true
+ "label": 'Color to Alpha...'
},
{
"id": 301,
- "enabled": true,
- "label": 'Colorify...',
- "visible": true
+ "label": 'Colorify...'
},
{
"id": 302,
@@ -1950,78 +1456,54 @@
{
"id": 303,
"children-display": 'submenu',
- "enabled": true,
"label": 'Info',
- "visible": true,
"submenu": [
{
"id": 304,
- "enabled": true,
- "label": 'Smooth Palette...',
- "visible": true
+ "label": 'Smooth Palette...'
},
{
"id": 305,
- "enabled": true,
- "label": 'Colorcube Analysis...',
- "visible": true
+ "label": 'Colorcube Analysis...'
},
{
"id": 306,
- "enabled": true,
- "label": 'Border Average...',
- "visible": true
+ "label": 'Border Average...'
},
{
"id": 307,
- "enabled": true,
- "label": 'Histogram',
- "visible": true
+ "label": 'Histogram'
}
]
},
{
"id": 308,
"children-display": 'submenu',
- "enabled": true,
"label": 'Map',
- "visible": true,
"submenu": [
{
"id": 309,
- "enabled": true,
- "label": 'Sample Colorize...',
- "visible": true
+ "label": 'Sample Colorize...'
},
{
"id": 310,
- "enabled": true,
- "label": 'Rotate Colors...',
- "visible": true
+ "label": 'Rotate Colors...'
},
{
"id": 311,
- "enabled": true,
- "label": 'Palette Map',
- "visible": true
+ "label": 'Palette Map'
},
{
"id": 312,
- "enabled": true,
- "label": 'Gradient Map',
- "visible": true
+ "label": 'Gradient Map'
},
{
"id": 313,
- "enabled": true,
- "label": 'Color Exchange...',
- "visible": true
+ "label": 'Color Exchange...'
},
{
"id": 314,
- "enabled": true,
- "label": 'Alien Map...',
- "visible": true
+ "label": 'Alien Map...'
},
{
"id": 315,
@@ -2030,92 +1512,68 @@
{
"id": 316,
"enabled": false,
- "label": 'Set Colormap...',
- "visible": true
+ "label": 'Set Colormap...'
},
{
"id": 317,
"enabled": false,
- "label": 'Rearrange Colormap...',
- "visible": true
+ "label": 'Rearrange Colormap...'
}
]
},
{
"id": 318,
"children-display": 'submenu',
- "enabled": true,
"label": 'Components',
- "visible": true,
"submenu": [
{
"id": 319,
"enabled": false,
- "label": 'Recompose',
- "visible": true
+ "label": 'Recompose'
},
{
"id": 320,
- "enabled": true,
- "label": 'Decompose...',
- "visible": true
+ "label": 'Decompose...'
},
{
"id": 321,
"enabled": false,
- "label": 'Compose...',
- "visible": true
+ "label": 'Compose...'
},
{
"id": 322,
- "enabled": true,
- "label": 'Channel Mixer...',
- "visible": true
+ "label": 'Channel Mixer...'
}
]
},
{
"id": 323,
"children-display": 'submenu',
- "enabled": true,
"label": 'Auto',
- "visible": true,
"submenu": [
{
"id": 324,
- "enabled": true,
- "label": 'Stretch HSV',
- "visible": true
+ "label": 'Stretch HSV'
},
{
"id": 325,
- "enabled": true,
- "label": 'Stretch Contrast',
- "visible": true
+ "label": 'Stretch Contrast'
},
{
"id": 326,
- "enabled": true,
- "label": 'Normalize',
- "visible": true
+ "label": 'Normalize'
},
{
"id": 327,
- "enabled": true,
- "label": 'Color Enhance',
- "visible": true
+ "label": 'Color Enhance'
},
{
"id": 328,
- "enabled": true,
- "label": 'White Balance',
- "visible": true
+ "label": 'White Balance'
},
{
"id": 329,
- "enabled": true,
- "label": 'Equalize',
- "visible": true
+ "label": 'Equalize'
}
]
},
@@ -2125,11 +1583,9 @@
},
{
"id": 331,
- "enabled": true,
"label": 'Use GEGL',
"toggle-state": 0,
- "toggle-type": 'checkmark',
- "visible": true
+ "toggle-type": 'checkmark'
},
{
"id": 332,
@@ -2137,15 +1593,11 @@
},
{
"id": 333,
- "enabled": true,
- "label": 'Value Invert',
- "visible": true
+ "label": 'Value Invert'
},
{
"id": 334,
- "enabled": true,
- "label": 'Invert',
- "visible": true
+ "label": 'Invert'
},
{
"id": 335,
@@ -2153,87 +1605,61 @@
},
{
"id": 336,
- "enabled": true,
- "label": 'Desaturate...',
- "visible": true
+ "label": 'Desaturate...'
},
{
"id": 337,
- "enabled": true,
- "label": 'Posterize...',
- "visible": true
+ "label": 'Posterize...'
},
{
"id": 338,
- "enabled": true,
- "label": 'Curves...',
- "visible": true
+ "label": 'Curves...'
},
{
"id": 339,
- "enabled": true,
- "label": 'Levels...',
- "visible": true
+ "label": 'Levels...'
},
{
"id": 340,
- "enabled": true,
- "label": 'Threshold...',
- "visible": true
+ "label": 'Threshold...'
},
{
"id": 341,
- "enabled": true,
- "label": 'Brightness-Contrast...',
- "visible": true
+ "label": 'Brightness-Contrast...'
},
{
"id": 342,
- "enabled": true,
- "label": 'Colorize...',
- "visible": true
+ "label": 'Colorize...'
},
{
"id": 343,
- "enabled": true,
- "label": 'Hue-Saturation...',
- "visible": true
+ "label": 'Hue-Saturation...'
},
{
"id": 344,
- "enabled": true,
- "label": 'Color Balance...',
- "visible": true
+ "label": 'Color Balance...'
}
]
},
{
"id": 345,
"children-display": 'submenu',
- "enabled": true,
"label": 'Tools',
- "visible": true,
"submenu": [
{
"id": 346,
- "enabled": true,
"label": 'Swap Colors',
- "shortcut": [['x']],
- "visible": true
+ "shortcut": [['x']]
},
{
"id": 347,
- "enabled": true,
"label": 'Default Colors',
- "shortcut": [['d']],
- "visible": true
+ "shortcut": [['d']]
},
{
"id": 348,
- "enabled": true,
"label": 'Toolbox',
- "shortcut": [['Control', 'b']],
- "visible": true
+ "shortcut": [['Control', 'b']]
},
{
"id": 349,
@@ -2241,326 +1667,232 @@
},
{
"id": 350,
- "enabled": true,
- "label": 'GEGL Operation...',
- "visible": true
+ "label": 'GEGL Operation...'
},
{
"id": 351,
- "enabled": true,
"label": 'Text',
- "shortcut": [['t']],
- "visible": true
+ "shortcut": [['t']]
},
{
"id": 352,
- "enabled": true,
"label": 'Measure',
- "shortcut": [['Shift', 'm']],
- "visible": true
+ "shortcut": [['Shift', 'm']]
},
{
"id": 353,
- "enabled": true,
"label": 'Zoom',
- "shortcut": [['z']],
- "visible": true
+ "shortcut": [['z']]
},
{
"id": 354,
- "enabled": true,
"label": 'Color Picker',
- "shortcut": [['o']],
- "visible": true
+ "shortcut": [['o']]
},
{
"id": 355,
- "enabled": true,
"label": 'Paths',
- "shortcut": [['b']],
- "visible": true
+ "shortcut": [['b']]
},
{
"id": 356,
"children-display": 'submenu',
- "enabled": true,
"label": 'Color Tools',
- "visible": true,
"submenu": [
{
"id": 357,
- "enabled": true,
- "label": 'Desaturate...',
- "visible": true
+ "label": 'Desaturate...'
},
{
"id": 358,
- "enabled": true,
- "label": 'Posterize...',
- "visible": true
+ "label": 'Posterize...'
},
{
"id": 359,
- "enabled": true,
- "label": 'Curves...',
- "visible": true
+ "label": 'Curves...'
},
{
"id": 360,
- "enabled": true,
- "label": 'Levels...',
- "visible": true
+ "label": 'Levels...'
},
{
"id": 361,
- "enabled": true,
- "label": 'Threshold...',
- "visible": true
+ "label": 'Threshold...'
},
{
"id": 362,
- "enabled": true,
- "label": 'Brightness-Contrast...',
- "visible": true
+ "label": 'Brightness-Contrast...'
},
{
"id": 363,
- "enabled": true,
- "label": 'Colorize...',
- "visible": true
+ "label": 'Colorize...'
},
{
"id": 364,
- "enabled": true,
- "label": 'Hue-Saturation...',
- "visible": true
+ "label": 'Hue-Saturation...'
},
{
"id": 365,
- "enabled": true,
- "label": 'Color Balance...',
- "visible": true
+ "label": 'Color Balance...'
}
]
},
{
"id": 366,
"children-display": 'submenu',
- "enabled": true,
"label": 'Transform Tools',
- "visible": true,
"submenu": [
{
"id": 367,
- "enabled": true,
"label": 'Flip',
- "shortcut": [['Shift', 'f']],
- "visible": true
+ "shortcut": [['Shift', 'f']]
},
{
"id": 368,
- "enabled": true,
"label": 'Perspective',
- "shortcut": [['Shift', 'p']],
- "visible": true
+ "shortcut": [['Shift', 'p']]
},
{
"id": 369,
- "enabled": true,
"label": 'Shear',
- "shortcut": [['Shift', 's']],
- "visible": true
+ "shortcut": [['Shift', 's']]
},
{
"id": 370,
- "enabled": true,
"label": 'Scale',
- "shortcut": [['Shift', 't']],
- "visible": true
+ "shortcut": [['Shift', 't']]
},
{
"id": 371,
- "enabled": true,
"label": 'Rotate',
- "shortcut": [['Shift', 'r']],
- "visible": true
+ "shortcut": [['Shift', 'r']]
},
{
"id": 372,
- "enabled": true,
"label": 'Crop',
- "shortcut": [['Shift', 'c']],
- "visible": true
+ "shortcut": [['Shift', 'c']]
},
{
"id": 373,
- "enabled": true,
"label": 'Move',
- "shortcut": [['m']],
- "visible": true
+ "shortcut": [['m']]
},
{
"id": 374,
- "enabled": true,
"label": 'Align',
- "shortcut": [['q']],
- "visible": true
+ "shortcut": [['q']]
}
]
},
{
"id": 375,
"children-display": 'submenu',
- "enabled": true,
"label": 'Paint Tools',
- "visible": true,
"submenu": [
{
"id": 376,
- "enabled": true,
"label": 'Dodge / Burn',
- "shortcut": [['Shift', 'd']],
- "visible": true
+ "shortcut": [['Shift', 'd']]
},
{
"id": 377,
- "enabled": true,
"label": 'Smudge',
- "shortcut": [['s']],
- "visible": true
+ "shortcut": [['s']]
},
{
"id": 378,
- "enabled": true,
"label": 'Blur / Sharpen',
- "shortcut": [['Shift', 'u']],
- "visible": true
+ "shortcut": [['Shift', 'u']]
},
{
"id": 379,
- "enabled": true,
- "label": 'Perspective Clone',
- "visible": true
+ "label": 'Perspective Clone'
},
{
"id": 380,
- "enabled": true,
"label": 'Heal',
- "shortcut": [['h']],
- "visible": true
+ "shortcut": [['h']]
},
{
"id": 381,
- "enabled": true,
"label": 'Clone',
- "shortcut": [['c']],
- "visible": true
+ "shortcut": [['c']]
},
{
"id": 382,
- "enabled": true,
"label": 'Ink',
- "shortcut": [['k']],
- "visible": true
+ "shortcut": [['k']]
},
{
"id": 383,
- "enabled": true,
"label": 'Airbrush',
- "shortcut": [['a']],
- "visible": true
+ "shortcut": [['a']]
},
{
"id": 384,
- "enabled": true,
"label": 'Eraser',
- "shortcut": [['Shift', 'e']],
- "visible": true
+ "shortcut": [['Shift', 'e']]
},
{
"id": 385,
- "enabled": true,
"label": 'Paintbrush',
- "shortcut": [['p']],
- "visible": true
+ "shortcut": [['p']]
},
{
"id": 386,
- "enabled": true,
"label": 'Pencil',
- "shortcut": [['n']],
- "visible": true
+ "shortcut": [['n']]
},
{
"id": 387,
- "enabled": true,
"label": 'Blend',
- "shortcut": [['l']],
- "visible": true
+ "shortcut": [['l']]
},
{
"id": 388,
- "enabled": true,
"label": 'Bucket Fill',
- "shortcut": [['Shift', 'b']],
- "visible": true
+ "shortcut": [['Shift', 'b']]
}
]
},
{
"id": 389,
"children-display": 'submenu',
- "enabled": true,
"label": 'Selection Tools',
- "visible": true,
"submenu": [
{
"id": 390,
- "enabled": true,
"label": 'Intelligent Scissors',
- "shortcut": [['i']],
- "visible": true
+ "shortcut": [['i']]
},
{
"id": 391,
- "enabled": true,
"label": 'By Color Select',
- "shortcut": [['Shift', 'o']],
- "visible": true
+ "shortcut": [['Shift', 'o']]
},
{
"id": 392,
- "enabled": true,
"label": 'Fuzzy Select',
- "shortcut": [['u']],
- "visible": true
+ "shortcut": [['u']]
},
{
"id": 393,
- "enabled": true,
- "label": 'Foreground Select',
- "visible": true
+ "label": 'Foreground Select'
},
{
"id": 394,
- "enabled": true,
"label": 'Free Select',
- "shortcut": [['f']],
- "visible": true
+ "shortcut": [['f']]
},
{
"id": 395,
- "enabled": true,
"label": 'Ellipse Select',
- "shortcut": [['e']],
- "visible": true
+ "shortcut": [['e']]
},
{
"id": 396,
- "enabled": true,
"label": 'Rectangle Select',
- "shortcut": [['r']],
- "visible": true
+ "shortcut": [['r']]
}
]
}
@@ -2569,49 +1901,35 @@
{
"id": 397,
"children-display": 'submenu',
- "enabled": true,
"label": 'Filters',
- "visible": true,
"submenu": [
{
"id": 398,
"children-display": 'submenu',
- "enabled": true,
"label": 'Script-Fu',
- "visible": true,
"submenu": [
{
"id": 399,
- "enabled": true,
- "label": 'Start Server...',
- "visible": true
+ "label": 'Start Server...'
},
{
"id": 400,
- "enabled": true,
- "label": 'Refresh Scripts',
- "visible": true
+ "label": 'Refresh Scripts'
},
{
"id": 401,
- "enabled": true,
- "label": 'Console',
- "visible": true
+ "label": 'Console'
}
]
},
{
"id": 402,
"children-display": 'submenu',
- "enabled": true,
"label": 'Python-Fu',
- "visible": true,
"submenu": [
{
"id": 403,
- "enabled": true,
- "label": 'Console',
- "visible": true
+ "label": 'Console'
}
]
},
@@ -2622,123 +1940,83 @@
{
"id": 405,
"children-display": 'submenu',
- "enabled": true,
"label": 'Alpha to Logo',
- "visible": true,
"submenu": [
{
"id": 406,
- "enabled": true,
- "label": 'Textured...',
- "visible": true
+ "label": 'Textured...'
},
{
"id": 407,
- "enabled": true,
- "label": 'Particle Trace...',
- "visible": true
+ "label": 'Particle Trace...'
},
{
"id": 408,
- "enabled": true,
- "label": 'Neon...',
- "visible": true
+ "label": 'Neon...'
},
{
"id": 409,
- "enabled": true,
- "label": 'Gradient Bevel...',
- "visible": true
+ "label": 'Gradient Bevel...'
},
{
"id": 410,
- "enabled": true,
- "label": 'Glowing Hot...',
- "visible": true
+ "label": 'Glowing Hot...'
},
{
"id": 411,
- "enabled": true,
- "label": 'Glossy...',
- "visible": true
+ "label": 'Glossy...'
},
{
"id": 412,
- "enabled": true,
- "label": 'Frosty...',
- "visible": true
+ "label": 'Frosty...'
},
{
"id": 413,
- "enabled": true,
- "label": 'Cool Metal...',
- "visible": true
+ "label": 'Cool Metal...'
},
{
"id": 414,
- "enabled": true,
- "label": 'Comic Book...',
- "visible": true
+ "label": 'Comic Book...'
},
{
"id": 415,
- "enabled": true,
- "label": 'Chrome...',
- "visible": true
+ "label": 'Chrome...'
},
{
"id": 416,
- "enabled": true,
- "label": 'Chip Away...',
- "visible": true
+ "label": 'Chip Away...'
},
{
"id": 417,
- "enabled": true,
- "label": 'Chalk...',
- "visible": true
+ "label": 'Chalk...'
},
{
"id": 418,
- "enabled": true,
- "label": 'Bovination...',
- "visible": true
+ "label": 'Bovination...'
},
{
"id": 419,
- "enabled": true,
- "label": 'Blended...',
- "visible": true
+ "label": 'Blended...'
},
{
"id": 420,
- "enabled": true,
- "label": 'Basic I...',
- "visible": true
+ "label": 'Basic I...'
},
{
"id": 421,
- "enabled": true,
- "label": 'Basic II...',
- "visible": true
+ "label": 'Basic II...'
},
{
"id": 422,
- "enabled": true,
- "label": 'Alien Neon...',
- "visible": true
+ "label": 'Alien Neon...'
},
{
"id": 423,
- "enabled": true,
- "label": 'Alien Glow...',
- "visible": true
+ "label": 'Alien Glow...'
},
{
"id": 424,
- "enabled": true,
- "label": '3D Outline...',
- "visible": true
+ "label": '3D Outline...'
}
]
},
@@ -2749,33 +2027,23 @@
{
"id": 426,
"children-display": 'submenu',
- "enabled": true,
"label": 'Animation',
- "visible": true,
"submenu": [
{
"id": 427,
- "enabled": true,
- "label": 'Unoptimize',
- "visible": true
+ "label": 'Unoptimize'
},
{
"id": 428,
- "enabled": true,
- "label": 'Playback...',
- "visible": true
+ "label": 'Playback...'
},
{
"id": 429,
- "enabled": true,
- "label": 'Optimize (for GIF)',
- "visible": true
+ "label": 'Optimize (for GIF)'
},
{
"id": 430,
- "enabled": true,
- "label": 'Optimize (Difference)',
- "visible": true
+ "label": 'Optimize (Difference)'
},
{
"id": 431,
@@ -2783,111 +2051,77 @@
},
{
"id": 432,
- "enabled": true,
- "label": 'Waves...',
- "visible": true
+ "label": 'Waves...'
},
{
"id": 433,
- "enabled": true,
- "label": 'Spinning Globe...',
- "visible": true
+ "label": 'Spinning Globe...'
},
{
"id": 434,
- "enabled": true,
- "label": 'Rippling...',
- "visible": true
+ "label": 'Rippling...'
},
{
"id": 435,
- "enabled": true,
- "label": 'Burn-In...',
- "visible": true
+ "label": 'Burn-In...'
},
{
"id": 436,
- "enabled": true,
- "label": 'Blend...',
- "visible": true
+ "label": 'Blend...'
}
]
},
{
"id": 437,
"children-display": 'submenu',
- "enabled": true,
"label": 'Web',
- "visible": true,
"submenu": [
{
"id": 438,
- "enabled": true,
- "label": 'Slice...',
- "visible": true
+ "label": 'Slice...'
},
{
"id": 439,
- "enabled": true,
- "label": 'Semi-Flatten',
- "visible": true
+ "label": 'Semi-Flatten'
},
{
"id": 440,
- "enabled": true,
- "label": 'Image Map...',
- "visible": true
+ "label": 'Image Map...'
}
]
},
{
"id": 441,
"children-display": 'submenu',
- "enabled": true,
"label": 'Render',
- "visible": true,
"submenu": [
{
"id": 442,
- "enabled": true,
- "label": 'Spyrogimp...',
- "visible": true
+ "label": 'Spyrogimp...'
},
{
"id": 443,
- "enabled": true,
- "label": 'Sphere Designer...',
- "visible": true
+ "label": 'Sphere Designer...'
},
{
"id": 444,
- "enabled": true,
- "label": 'Line Nova...',
- "visible": true
+ "label": 'Line Nova...'
},
{
"id": 445,
- "enabled": true,
- "label": 'Lava...',
- "visible": true
+ "label": 'Lava...'
},
{
"id": 446,
- "enabled": true,
- "label": 'Gfig...',
- "visible": true
+ "label": 'Gfig...'
},
{
"id": 447,
- "enabled": true,
- "label": 'Fractal Explorer...',
- "visible": true
+ "label": 'Fractal Explorer...'
},
{
"id": 448,
- "enabled": true,
- "label": 'Circuit...',
- "visible": true
+ "label": 'Circuit...'
},
{
"id": 449,
@@ -2896,111 +2130,77 @@
{
"id": 450,
"children-display": 'submenu',
- "enabled": true,
"label": 'Pattern',
- "visible": true,
"submenu": [
{
"id": 451,
- "enabled": true,
- "label": 'Sinus...',
- "visible": true
+ "label": 'Sinus...'
},
{
"id": 452,
- "enabled": true,
- "label": 'Qbist...',
- "visible": true
+ "label": 'Qbist...'
},
{
"id": 453,
- "enabled": true,
- "label": 'Maze...',
- "visible": true
+ "label": 'Maze...'
},
{
"id": 454,
- "enabled": true,
- "label": 'Jigsaw...',
- "visible": true
+ "label": 'Jigsaw...'
},
{
"id": 455,
- "enabled": true,
- "label": 'Grid...',
- "visible": true
+ "label": 'Grid...'
},
{
"id": 456,
- "enabled": true,
- "label": 'Diffraction Patterns...',
- "visible": true
+ "label": 'Diffraction Patterns...'
},
{
"id": 457,
- "enabled": true,
- "label": 'CML Explorer...',
- "visible": true
+ "label": 'CML Explorer...'
},
{
"id": 458,
- "enabled": true,
- "label": 'Checkerboard...',
- "visible": true
+ "label": 'Checkerboard...'
}
]
},
{
"id": 459,
"children-display": 'submenu',
- "enabled": true,
"label": 'Nature',
- "visible": true,
"submenu": [
{
"id": 460,
- "enabled": true,
- "label": 'IFS Fractal...',
- "visible": true
+ "label": 'IFS Fractal...'
},
{
"id": 461,
- "enabled": true,
- "label": 'Flame...',
- "visible": true
+ "label": 'Flame...'
}
]
},
{
"id": 462,
"children-display": 'submenu',
- "enabled": true,
"label": 'Clouds',
- "visible": true,
"submenu": [
{
"id": 463,
- "enabled": true,
- "label": 'Solid Noise...',
- "visible": true
+ "label": 'Solid Noise...'
},
{
"id": 464,
- "enabled": true,
- "label": 'Plasma...',
- "visible": true
+ "label": 'Plasma...'
},
{
"id": 465,
- "enabled": true,
- "label": 'Fog...',
- "visible": true
+ "label": 'Fog...'
},
{
"id": 466,
- "enabled": true,
- "label": 'Difference Clouds...',
- "visible": true
+ "label": 'Difference Clouds...'
}
]
}
@@ -3009,360 +2209,252 @@
{
"id": 467,
"children-display": 'submenu',
- "enabled": true,
"label": 'Map',
- "visible": true,
"submenu": [
{
"id": 468,
- "enabled": true,
- "label": 'Warp...',
- "visible": true
+ "label": 'Warp...'
},
{
"id": 469,
- "enabled": true,
- "label": 'Tile...',
- "visible": true
+ "label": 'Tile...'
},
{
"id": 470,
- "enabled": true,
- "label": 'Small Tiles...',
- "visible": true
+ "label": 'Small Tiles...'
},
{
"id": 471,
- "enabled": true,
- "label": 'Paper Tile...',
- "visible": true
+ "label": 'Paper Tile...'
},
{
"id": 472,
- "enabled": true,
- "label": 'Map Object...',
- "visible": true
+ "label": 'Map Object...'
},
{
"id": 473,
- "enabled": true,
- "label": 'Make Seamless',
- "visible": true
+ "label": 'Make Seamless'
},
{
"id": 474,
- "enabled": true,
- "label": 'Illusion...',
- "visible": true
+ "label": 'Illusion...'
},
{
"id": 475,
- "enabled": true,
- "label": 'Fractal Trace...',
- "visible": true
+ "label": 'Fractal Trace...'
},
{
"id": 476,
- "enabled": true,
- "label": 'Displace...',
- "visible": true
+ "label": 'Displace...'
},
{
"id": 477,
- "enabled": true,
- "label": 'Bump Map...',
- "visible": true
+ "label": 'Bump Map...'
}
]
},
{
"id": 478,
"children-display": 'submenu',
- "enabled": true,
"label": 'Decor',
- "visible": true,
"submenu": [
{
"id": 479,
"enabled": false,
- "label": 'Stencil Chrome...',
- "visible": true
+ "label": 'Stencil Chrome...'
},
{
"id": 480,
"enabled": false,
- "label": 'Stencil Carve...',
- "visible": true
+ "label": 'Stencil Carve...'
},
{
"id": 481,
"enabled": false,
- "label": 'Slide...',
- "visible": true
+ "label": 'Slide...'
},
{
"id": 482,
"enabled": false,
- "label": 'Round Corners...',
- "visible": true
+ "label": 'Round Corners...'
},
{
"id": 483,
- "enabled": true,
- "label": 'Old Photo...',
- "visible": true
+ "label": 'Old Photo...'
},
{
"id": 484,
- "enabled": true,
- "label": 'Fuzzy Border...',
- "visible": true
+ "label": 'Fuzzy Border...'
},
{
"id": 485,
- "enabled": true,
- "label": 'Coffee Stain...',
- "visible": true
+ "label": 'Coffee Stain...'
},
{
"id": 486,
- "enabled": true,
- "label": 'Add Border...',
- "visible": true
+ "label": 'Add Border...'
},
{
"id": 487,
- "enabled": true,
- "label": 'Add Bevel...',
- "visible": true
+ "label": 'Add Bevel...'
}
]
},
{
"id": 488,
"children-display": 'submenu',
- "enabled": true,
"label": 'Artistic',
- "visible": true,
"submenu": [
{
"id": 489,
- "enabled": true,
- "label": 'Weave...',
- "visible": true
+ "label": 'Weave...'
},
{
"id": 490,
- "enabled": true,
- "label": 'Van Gogh (LIC)...',
- "visible": true
+ "label": 'Van Gogh (LIC)...'
},
{
"id": 491,
- "enabled": true,
- "label": 'Softglow...',
- "visible": true
+ "label": 'Softglow...'
},
{
"id": 492,
- "enabled": true,
- "label": 'Predator...',
- "visible": true
+ "label": 'Predator...'
},
{
"id": 493,
- "enabled": true,
- "label": 'Photocopy...',
- "visible": true
+ "label": 'Photocopy...'
},
{
"id": 494,
- "enabled": true,
- "label": 'Oilify...',
- "visible": true
+ "label": 'Oilify...'
},
{
"id": 495,
- "enabled": true,
- "label": 'GIMPressionist...',
- "visible": true
+ "label": 'GIMPressionist...'
},
{
"id": 496,
- "enabled": true,
- "label": 'Cubism...',
- "visible": true
+ "label": 'Cubism...'
},
{
"id": 497,
- "enabled": true,
- "label": 'Clothify...',
- "visible": true
+ "label": 'Clothify...'
},
{
"id": 498,
- "enabled": true,
- "label": 'Cartoon...',
- "visible": true
+ "label": 'Cartoon...'
},
{
"id": 499,
- "enabled": true,
- "label": 'Apply Canvas...',
- "visible": true
+ "label": 'Apply Canvas...'
}
]
},
{
"id": 500,
"children-display": 'submenu',
- "enabled": true,
"label": 'Combine',
- "visible": true,
"submenu": [
{
"id": 501,
- "enabled": true,
- "label": 'Filmstrip...',
- "visible": true
+ "label": 'Filmstrip...'
},
{
"id": 502,
- "enabled": true,
- "label": 'Depth Merge...',
- "visible": true
+ "label": 'Depth Merge...'
}
]
},
{
"id": 503,
"children-display": 'submenu',
- "enabled": true,
"label": 'Generic',
- "visible": true,
"submenu": [
{
"id": 504,
- "enabled": true,
- "label": 'Erode',
- "visible": true
+ "label": 'Erode'
},
{
"id": 505,
- "enabled": true,
- "label": 'Dilate',
- "visible": true
+ "label": 'Dilate'
},
{
"id": 506,
- "enabled": true,
- "label": 'Convolution Matrix...',
- "visible": true
+ "label": 'Convolution Matrix...'
}
]
},
{
"id": 507,
"children-display": 'submenu',
- "enabled": true,
"label": 'Edge-Detect',
- "visible": true,
"submenu": [
{
"id": 508,
- "enabled": true,
- "label": 'Sobel...',
- "visible": true
+ "label": 'Sobel...'
},
{
"id": 509,
- "enabled": true,
- "label": 'Neon...',
- "visible": true
+ "label": 'Neon...'
},
{
"id": 510,
- "enabled": true,
- "label": 'Laplace',
- "visible": true
+ "label": 'Laplace'
},
{
"id": 511,
- "enabled": true,
- "label": 'Edge...',
- "visible": true
+ "label": 'Edge...'
},
{
"id": 512,
- "enabled": true,
- "label": 'Difference of Gaussians...',
- "visible": true
+ "label": 'Difference of Gaussians...'
}
]
},
{
"id": 513,
"children-display": 'submenu',
- "enabled": true,
"label": 'Noise',
- "visible": true,
"submenu": [
{
"id": 514,
- "enabled": true,
- "label": 'Spread...',
- "visible": true
+ "label": 'Spread...'
},
{
"id": 515,
- "enabled": true,
- "label": 'Slur...',
- "visible": true
+ "label": 'Slur...'
},
{
"id": 516,
- "enabled": true,
- "label": 'RGB Noise...',
- "visible": true
+ "label": 'RGB Noise...'
},
{
"id": 517,
- "enabled": true,
- "label": 'Pick...',
- "visible": true
+ "label": 'Pick...'
},
{
"id": 518,
- "enabled": true,
- "label": 'Hurl...',
- "visible": true
+ "label": 'Hurl...'
},
{
"id": 519,
- "enabled": true,
- "label": 'HSV Noise...',
- "visible": true
+ "label": 'HSV Noise...'
}
]
},
{
"id": 520,
"children-display": 'submenu',
- "enabled": true,
"label": 'Light and Shadow',
- "visible": true,
"submenu": [
{
"id": 521,
- "enabled": true,
- "label": 'Glass Tile...',
- "visible": true
+ "label": 'Glass Tile...'
},
{
"id": 522,
- "enabled": true,
- "label": 'Apply Lens...',
- "visible": true
+ "label": 'Apply Lens...'
},
{
"id": 523,
@@ -3370,21 +2462,15 @@
},
{
"id": 524,
- "enabled": true,
- "label": 'Xach-Effect...',
- "visible": true
+ "label": 'Xach-Effect...'
},
{
"id": 525,
- "enabled": true,
- "label": 'Perspective...',
- "visible": true
+ "label": 'Perspective...'
},
{
"id": 526,
- "enabled": true,
- "label": 'Drop Shadow...',
- "visible": true
+ "label": 'Drop Shadow...'
},
{
"id": 527,
@@ -3392,252 +2478,173 @@
},
{
"id": 528,
- "enabled": true,
- "label": 'Supernova...',
- "visible": true
+ "label": 'Supernova...'
},
{
"id": 529,
- "enabled": true,
- "label": 'Sparkle...',
- "visible": true
+ "label": 'Sparkle...'
},
{
"id": 530,
- "enabled": true,
- "label": 'Lighting Effects...',
- "visible": true
+ "label": 'Lighting Effects...'
},
{
"id": 531,
- "enabled": true,
- "label": 'Lens Flare...',
- "visible": true
+ "label": 'Lens Flare...'
},
{
"id": 532,
- "enabled": true,
- "label": 'Gradient Flare...',
- "visible": true
+ "label": 'Gradient Flare...'
}
]
},
{
"id": 533,
"children-display": 'submenu',
- "enabled": true,
"label": 'Distorts',
- "visible": true,
"submenu": [
{
"id": 534,
- "enabled": true,
- "label": 'Wind...',
- "visible": true
+ "label": 'Wind...'
},
{
"id": 535,
- "enabled": true,
- "label": 'Whirl and Pinch...',
- "visible": true
+ "label": 'Whirl and Pinch...'
},
{
"id": 536,
- "enabled": true,
- "label": 'Waves...',
- "visible": true
+ "label": 'Waves...'
},
{
"id": 537,
- "enabled": true,
- "label": 'Video...',
- "visible": true
+ "label": 'Video...'
},
{
"id": 538,
- "enabled": true,
- "label": 'Value Propagate...',
- "visible": true
+ "label": 'Value Propagate...'
},
{
"id": 539,
- "enabled": true,
- "label": 'Shift...',
- "visible": true
+ "label": 'Shift...'
},
{
"id": 540,
- "enabled": true,
- "label": 'Ripple...',
- "visible": true
+ "label": 'Ripple...'
},
{
"id": 541,
- "enabled": true,
- "label": 'Polar Coordinates...',
- "visible": true
+ "label": 'Polar Coordinates...'
},
{
"id": 542,
- "enabled": true,
- "label": 'Pagecurl...',
- "visible": true
+ "label": 'Pagecurl...'
},
{
"id": 543,
- "enabled": true,
- "label": 'Newsprint...',
- "visible": true
+ "label": 'Newsprint...'
},
{
"id": 544,
- "enabled": true,
- "label": 'Mosaic...',
- "visible": true
+ "label": 'Mosaic...'
},
{
"id": 545,
- "enabled": true,
- "label": 'Lens Distortion...',
- "visible": true
+ "label": 'Lens Distortion...'
},
{
"id": 546,
- "enabled": true,
- "label": 'IWarp...',
- "visible": true
+ "label": 'IWarp...'
},
{
"id": 547,
- "enabled": true,
- "label": 'Erase Every Other Row...',
- "visible": true
+ "label": 'Erase Every Other Row...'
},
{
"id": 548,
- "enabled": true,
- "label": 'Engrave...',
- "visible": true
+ "label": 'Engrave...'
},
{
"id": 549,
- "enabled": true,
- "label": 'Emboss...',
- "visible": true
+ "label": 'Emboss...'
},
{
"id": 550,
- "enabled": true,
- "label": 'Curve Bend...',
- "visible": true
+ "label": 'Curve Bend...'
},
{
"id": 551,
- "enabled": true,
- "label": 'Blinds...',
- "visible": true
+ "label": 'Blinds...'
}
]
},
{
"id": 552,
"children-display": 'submenu',
- "enabled": true,
"label": 'Enhance',
- "visible": true,
"submenu": [
{
"id": 553,
- "enabled": true,
- "label": 'Unsharp Mask...',
- "visible": true
+ "label": 'Unsharp Mask...'
},
{
"id": 554,
- "enabled": true,
- "label": 'Sharpen...',
- "visible": true
+ "label": 'Sharpen...'
},
{
"id": 555,
- "enabled": true,
- "label": 'Red Eye Removal...',
- "visible": true
+ "label": 'Red Eye Removal...'
},
{
"id": 556,
"enabled": false,
- "label": 'NL Filter...',
- "visible": true
+ "label": 'NL Filter...'
},
{
"id": 557,
- "enabled": true,
- "label": 'Destripe...',
- "visible": true
+ "label": 'Destripe...'
},
{
"id": 558,
- "enabled": true,
- "label": 'Despeckle...',
- "visible": true
+ "label": 'Despeckle...'
},
{
"id": 559,
- "enabled": true,
- "label": 'Deinterlace...',
- "visible": true
+ "label": 'Deinterlace...'
},
{
"id": 560,
- "enabled": true,
- "label": 'Antialias',
- "visible": true
+ "label": 'Antialias'
}
]
},
{
"id": 561,
"children-display": 'submenu',
- "enabled": true,
"label": 'Blur',
- "visible": true,
"submenu": [
{
"id": 562,
- "enabled": true,
- "label": 'Tileable Blur...',
- "visible": true
+ "label": 'Tileable Blur...'
},
{
"id": 563,
- "enabled": true,
- "label": 'Selective Gaussian Blur...',
- "visible": true
+ "label": 'Selective Gaussian Blur...'
},
{
"id": 564,
- "enabled": true,
- "label": 'Pixelize...',
- "visible": true
+ "label": 'Pixelize...'
},
{
"id": 565,
- "enabled": true,
- "label": 'Motion Blur...',
- "visible": true
+ "label": 'Motion Blur...'
},
{
"id": 566,
- "enabled": true,
- "label": 'Gaussian Blur...',
- "visible": true
+ "label": 'Gaussian Blur...'
},
{
"id": 567,
- "enabled": true,
- "label": 'Blur',
- "visible": true
+ "label": 'Blur'
}
]
},
@@ -3647,9 +2654,7 @@
},
{
"id": 569,
- "enabled": true,
- "label": 'Reset all Filters',
- "visible": true
+ "label": 'Reset all Filters'
},
{
"id": 570,
@@ -3657,13 +2662,11 @@
"enabled": false,
"label": 'Re-Show Last',
"shortcut": [['Control', 'Shift', 'f']],
- "visible": true,
"submenu": [
{
"id": 571,
"enabled": false,
- "label": 'Empty',
- "visible": true
+ "label": 'Empty'
}
]
},
@@ -3671,24 +2674,19 @@
"id": 572,
"enabled": false,
"label": 'Repeat Last',
- "shortcut": [['Control', 'f']],
- "visible": true
+ "shortcut": [['Control', 'f']]
}
]
},
{
"id": 573,
"children-display": 'submenu',
- "enabled": true,
"label": 'Windows',
- "visible": true,
"submenu": [
{
"id": 574,
- "enabled": true,
"label": 'Toolbox',
- "shortcut": [['Control', 'b']],
- "visible": true
+ "shortcut": [['Control', 'b']]
},
{
"id": 575,
@@ -3697,39 +2695,27 @@
{
"id": 576,
"children-display": 'submenu',
- "enabled": true,
"label": 'Dockable Dialogs',
- "visible": true,
"submenu": [
{
"id": 577,
- "enabled": true,
- "label": 'Error Console',
- "visible": true
+ "label": 'Error Console'
},
{
"id": 578,
- "enabled": true,
- "label": 'Tools',
- "visible": true
+ "label": 'Tools'
},
{
"id": 579,
- "enabled": true,
- "label": 'Templates',
- "visible": true
+ "label": 'Templates'
},
{
"id": 580,
- "enabled": true,
- "label": 'Document History',
- "visible": true
+ "label": 'Document History'
},
{
"id": 581,
- "enabled": true,
- "label": 'Images',
- "visible": true
+ "label": 'Images'
},
{
"id": 582,
@@ -3737,48 +2723,34 @@
},
{
"id": 583,
- "enabled": true,
- "label": 'Buffers',
- "visible": true
+ "label": 'Buffers'
},
{
"id": 584,
- "enabled": true,
- "label": 'Fonts',
- "visible": true
+ "label": 'Fonts'
},
{
"id": 585,
- "enabled": true,
- "label": 'Palettes',
- "visible": true
+ "label": 'Palettes'
},
{
"id": 586,
- "enabled": true,
"label": 'Gradients',
- "shortcut": [['Control', 'g']],
- "visible": true
+ "shortcut": [['Control', 'g']]
},
{
"id": 587,
- "enabled": true,
"label": 'Patterns',
- "shortcut": [['Control', 'Shift', 'p']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'p']]
},
{
"id": 588,
- "enabled": true,
"label": 'Brushes',
- "shortcut": [['Control', 'Shift', 'b']],
- "visible": true
+ "shortcut": [['Control', 'Shift', 'b']]
},
{
"id": 589,
- "enabled": true,
- "label": 'Colors',
- "visible": true
+ "label": 'Colors'
},
{
"id": 590,
@@ -3786,64 +2758,44 @@
},
{
"id": 591,
- "enabled": true,
- "label": 'Sample Points',
- "visible": true
+ "label": 'Sample Points'
},
{
"id": 592,
- "enabled": true,
- "label": 'Pointer',
- "visible": true
+ "label": 'Pointer'
},
{
"id": 593,
- "enabled": true,
- "label": 'Undo History',
- "visible": true
+ "label": 'Undo History'
},
{
"id": 594,
- "enabled": true,
- "label": 'Navigation',
- "visible": true
+ "label": 'Navigation'
},
{
"id": 595,
- "enabled": true,
- "label": 'Selection Editor',
- "visible": true
+ "label": 'Selection Editor'
},
{
"id": 596,
- "enabled": true,
- "label": 'Histogram',
- "visible": true
+ "label": 'Histogram'
},
{
"id": 597,
- "enabled": true,
- "label": 'Colormap',
- "visible": true
+ "label": 'Colormap'
},
{
"id": 598,
- "enabled": true,
- "label": 'Paths',
- "visible": true
+ "label": 'Paths'
},
{
"id": 599,
- "enabled": true,
- "label": 'Channels',
- "visible": true
+ "label": 'Channels'
},
{
"id": 600,
- "enabled": true,
"label": 'Layers',
- "shortcut": [['Control', 'l']],
- "visible": true
+ "shortcut": [['Control', 'l']]
},
{
"id": 601,
@@ -3851,30 +2803,23 @@
},
{
"id": 602,
- "enabled": true,
- "label": 'Device Status',
- "visible": true
+ "label": 'Device Status'
},
{
"id": 603,
- "enabled": true,
- "label": 'Tool Options',
- "visible": true
+ "label": 'Tool Options'
}
]
},
{
"id": 604,
"children-display": 'submenu',
- "enabled": true,
"label": 'Recently Closed Docks',
- "visible": true,
"submenu": [
{
"id": 605,
"enabled": false,
- "label": 'Empty',
- "visible": true
+ "label": 'Empty'
}
]
}
@@ -3883,91 +2828,63 @@
{
"id": 606,
"children-display": 'submenu',
- "enabled": true,
"label": 'Help',
- "visible": true,
"submenu": [
{
"id": 607,
"children-display": 'submenu',
- "enabled": true,
"label": 'User Manual',
- "visible": true,
"submenu": [
{
"id": 608,
- "enabled": true,
- "label": 'Working with Digital Camera Photos',
- "visible": true
+ "label": 'Working with Digital Camera Photos'
},
{
"id": 609,
- "enabled": true,
- "label": 'Using Paths',
- "visible": true
+ "label": 'Using Paths'
},
{
"id": 610,
- "enabled": true,
- "label": 'Preparing your Images for the Web',
- "visible": true
+ "label": 'Preparing your Images for the Web'
},
{
"id": 611,
- "enabled": true,
- "label": 'How to Use Dialogs',
- "visible": true
+ "label": 'How to Use Dialogs'
},
{
"id": 612,
- "enabled": true,
- "label": 'Drawing Simple Objects',
- "visible": true
+ "label": 'Drawing Simple Objects'
},
{
"id": 613,
- "enabled": true,
- "label": 'Create, Open and Save Files',
- "visible": true
+ "label": 'Create, Open and Save Files'
},
{
"id": 614,
- "enabled": true,
- "label": 'Basic Concepts',
- "visible": true
+ "label": 'Basic Concepts'
}
]
},
{
"id": 615,
"children-display": 'submenu',
- "enabled": true,
"label": 'GIMP Online',
- "visible": true,
"submenu": [
{
"id": 616,
- "enabled": true,
- "label": 'User Manual Web Site',
- "visible": true
+ "label": 'User Manual Web Site'
},
{
"id": 617,
- "enabled": true,
- "label": 'Plug-in Registry',
- "visible": true
+ "label": 'Plug-in Registry'
},
{
"id": 618,
- "enabled": true,
- "label": 'Main Web Site',
- "visible": true
+ "label": 'Main Web Site'
},
{
"id": 619,
- "enabled": true,
- "label": 'Developer Web Site',
- "visible": true
+ "label": 'Developer Web Site'
}
]
},
@@ -3977,15 +2894,11 @@
},
{
"id": 621,
- "enabled": true,
- "label": 'Procedure Browser',
- "visible": true
+ "label": 'Procedure Browser'
},
{
"id": 622,
- "enabled": true,
- "label": 'Plug-In Browser',
- "visible": true
+ "label": 'Plug-In Browser'
},
{
"id": 623,
@@ -3993,29 +2906,21 @@
},
{
"id": 624,
- "enabled": true,
- "label": 'About',
- "visible": true
+ "label": 'About'
},
{
"id": 625,
- "enabled": true,
- "label": 'Tip of the Day',
- "visible": true
+ "label": 'Tip of the Day'
},
{
"id": 626,
- "enabled": true,
"label": 'Context Help',
- "shortcut": [['Shift', 'F1']],
- "visible": true
+ "shortcut": [['Shift', 'F1']]
},
{
"id": 627,
- "enabled": true,
"label": 'Help',
- "shortcut": [['F1']],
- "visible": true
+ "shortcut": [['F1']]
}
]
}
diff --git a/tools/dbusmenu-dumper.c b/tools/dbusmenu-dumper.c
index bd986e8..2db437d 100644
--- a/tools/dbusmenu-dumper.c
+++ b/tools/dbusmenu-dumper.c
@@ -270,10 +270,12 @@ init_dbus_vars_from_window(Window window)
error = NULL;
GVariant * retval;
+ GVariant * args[1];
+ args[0] = g_variant_new("u", window);
retval = g_dbus_proxy_call_sync(proxy,
"GetMenuForWindow",
- g_variant_new("u", window),
+ g_variant_new_tuple(args, 1),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
@@ -285,7 +287,7 @@ init_dbus_vars_from_window(Window window)
return FALSE;
}
- g_variant_get(retval, "so", &dbusname, &dbusobject);
+ g_variant_get(retval, "(so)", &dbusname, &dbusobject);
g_variant_unref(retval);
g_object_unref(proxy);
diff --git a/tools/testapp/Makefile.am b/tools/testapp/Makefile.am
index f59f839..e2bbde7 100644
--- a/tools/testapp/Makefile.am
+++ b/tools/testapp/Makefile.am
@@ -1,5 +1,9 @@
-libexec_PROGRAMS = dbusmenu-testapp
+libexec_PROGRAMS =
+
+if WANT_TESTS
+libexec_PROGRAMS += dbusmenu-testapp
+endif
dbusmenu_testapp_SOURCES = \
main.c