summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog50
-rw-r--r--README16
-rw-r--r--autostart/.cvsignore14
-rw-r--r--autostart/AUTHORS5
-rw-r--r--autostart/COPYING2
-rw-r--r--autostart/ChangeLog4
-rw-r--r--autostart/INSTALL236
-rw-r--r--autostart/Makefile.am16
-rw-r--r--autostart/NEWS0
-rw-r--r--autostart/README6
-rwxr-xr-xautostart/autogen.sh3
-rw-r--r--autostart/autostart-spec.xml273
-rw-r--r--autostart/configure.in61
-rw-r--r--basedir/.gitignore1
-rw-r--r--basedir/Makefile7
-rw-r--r--basedir/basedir-spec.xml298
-rw-r--r--desktop-entry/.cvsignore2
-rw-r--r--desktop-entry/ChangeLog182
-rw-r--r--desktop-entry/desktop-entry-spec.xml1232
-rw-r--r--help-system/help-system-spec.xml195
-rw-r--r--menu/.cvsignore11
-rw-r--r--menu/AUTHORS0
-rw-r--r--menu/COPYING2
-rw-r--r--menu/ChangeLog143
-rw-r--r--menu/Makefile.am16
-rw-r--r--menu/NEWS0
-rw-r--r--menu/README1
-rwxr-xr-xmenu/autogen.sh3
-rw-r--r--menu/configure.in61
-rw-r--r--menu/menu-spec.xml2383
-rw-r--r--menu/menu.dtd84
-rw-r--r--menu/tests/ChangeLog70
-rw-r--r--menu/tests/README44
-rw-r--r--menu/tests/TODO5
-rw-r--r--menu/tests/data/Help.desktop68
-rw-r--r--menu/tests/data/Home.desktop127
-rw-r--r--menu/tests/data/KEdit.desktop34
-rw-r--r--menu/tests/data/Kfind.desktop66
-rw-r--r--menu/tests/data/apps.directory66
-rw-r--r--menu/tests/data/freecell.desktop83
-rw-r--r--menu/tests/data/gataxx.desktop81
-rw-r--r--menu/tests/data/gideon-legacy.desktop12
-rw-r--r--menu/tests/data/gideon.desktop13
-rw-r--r--menu/tests/data/glines-2.desktop83
-rw-r--r--menu/tests/data/glines.desktop82
-rw-r--r--menu/tests/data/hidden.desktop8
-rw-r--r--menu/tests/data/hidden.directory5
-rw-r--r--menu/tests/data/kate.desktop30
-rw-r--r--menu/tests/data/kbabel.desktop69
-rw-r--r--menu/tests/data/kedit-legacy.desktop33
-rw-r--r--menu/tests/data/kwrite.desktop84
-rw-r--r--menu/tests/data/mahjongg-2.desktop86
-rw-r--r--menu/tests/data/mahjongg.desktop86
-rw-r--r--menu/tests/data/quanta.desktop10
-rwxr-xr-xmenu/tests/expand12
-rwxr-xr-xmenu/tests/menutest200
-rw-r--r--menu/tests/tests/All/result4
-rw-r--r--menu/tests/tests/All/test26
-rw-r--r--menu/tests/tests/And/result1
-rw-r--r--menu/tests/tests/And/test29
-rw-r--r--menu/tests/tests/AppDir-relative/result3
-rw-r--r--menu/tests/tests/AppDir-relative/test27
-rw-r--r--menu/tests/tests/AppDir/test4
-rw-r--r--menu/tests/tests/Category/result3
-rw-r--r--menu/tests/tests/Category/test29
-rw-r--r--menu/tests/tests/DefaultMergeDirs/result5
-rw-r--r--menu/tests/tests/DefaultMergeDirs/test43
-rw-r--r--menu/tests/tests/Deleted/result2
-rw-r--r--menu/tests/tests/Deleted/test35
-rw-r--r--menu/tests/tests/DesktopFileID/result4
-rw-r--r--menu/tests/tests/DesktopFileID/test26
-rw-r--r--menu/tests/tests/Directory/result3
-rw-r--r--menu/tests/tests/Directory/test29
-rw-r--r--menu/tests/tests/DirectoryDir-relative/result3
-rw-r--r--menu/tests/tests/DirectoryDir-relative/test28
-rw-r--r--menu/tests/tests/DirectoryDir/test4
-rw-r--r--menu/tests/tests/Exclude/result3
-rw-r--r--menu/tests/tests/Exclude/test32
-rw-r--r--menu/tests/tests/Filename/result1
-rw-r--r--menu/tests/tests/Filename/test26
-rw-r--r--menu/tests/tests/LegacyDir-Move/result2
-rw-r--r--menu/tests/tests/LegacyDir-Move/test39
-rw-r--r--menu/tests/tests/LegacyDir-relative/result9
-rw-r--r--menu/tests/tests/LegacyDir-relative/test42
-rw-r--r--menu/tests/tests/Merge-combined/result1
-rw-r--r--menu/tests/tests/Merge-combined/test46
-rw-r--r--menu/tests/tests/MergeDir-absolute/test3
-rw-r--r--menu/tests/tests/MergeDir-relative/result5
-rw-r--r--menu/tests/tests/MergeDir-relative/test61
-rw-r--r--menu/tests/tests/MergeFile-absolute/test3
-rw-r--r--menu/tests/tests/MergeFile-parent/result5
-rw-r--r--menu/tests/tests/MergeFile-parent/test61
-rw-r--r--menu/tests/tests/MergeFile-path/result5
-rw-r--r--menu/tests/tests/MergeFile-path/test61
-rw-r--r--menu/tests/tests/MergeFile-recursive/result5
-rw-r--r--menu/tests/tests/MergeFile-recursive/test58
-rw-r--r--menu/tests/tests/MergeFile-relative/result5
-rw-r--r--menu/tests/tests/MergeFile-relative/test44
-rw-r--r--menu/tests/tests/MergeFile2/result5
-rw-r--r--menu/tests/tests/MergeFile2/test57
-rw-r--r--menu/tests/tests/MergeFile3/result5
-rw-r--r--menu/tests/tests/MergeFile3/test56
-rw-r--r--menu/tests/tests/Move-collapsing/result4
-rw-r--r--menu/tests/tests/Move-collapsing/test40
-rw-r--r--menu/tests/tests/Move-ordering/result3
-rw-r--r--menu/tests/tests/Move-ordering/test49
-rw-r--r--menu/tests/tests/Move-submenu/result1
-rw-r--r--menu/tests/tests/Move-submenu/test32
-rw-r--r--menu/tests/tests/Move/result2
-rw-r--r--menu/tests/tests/Move/test34
-rw-r--r--menu/tests/tests/NoDisplay/result1
-rw-r--r--menu/tests/tests/NoDisplay/test37
-rw-r--r--menu/tests/tests/NoDisplay2/result1
-rw-r--r--menu/tests/tests/NoDisplay2/test38
-rw-r--r--menu/tests/tests/NotOnlyUnallocated-default/result2
-rw-r--r--menu/tests/tests/NotOnlyUnallocated-default/test33
-rw-r--r--menu/tests/tests/OnlyUnallocated/result3
-rw-r--r--menu/tests/tests/OnlyUnallocated/test44
-rw-r--r--menu/tests/tests/Or/result4
-rw-r--r--menu/tests/tests/Or/test29
-rw-r--r--menu/tests/tests/boolean-logic/result3
-rw-r--r--menu/tests/tests/boolean-logic/test36
-rw-r--r--menu/tests/tests/desktop-name-collision/result3
-rw-r--r--menu/tests/tests/desktop-name-collision/test53
-rw-r--r--menu/tests/tests/menu-multiple-matching/result5
-rw-r--r--menu/tests/tests/menu-multiple-matching/test36
-rw-r--r--menu/tests/tests/official-categories/categories.list10
-rw-r--r--menu/tests/tests/official-categories/test73
-rw-r--r--menu/tests/tests/official-categories/unique-entry.desktop9
-rw-r--r--menu/tests/tests/submenu-collision/result5
-rw-r--r--menu/tests/tests/submenu-collision/test32
-rwxr-xr-xmenu/tests/tet_menutest39
-rw-r--r--menu/tet_scen5
-rw-r--r--menu/tetexec.cfg1
-rw-r--r--secret-service/.gitignore3
-rw-r--r--secret-service/Makefile17
-rw-r--r--secret-service/README20
-rw-r--r--secret-service/docbook-params.xsl39
-rw-r--r--secret-service/html/style.css127
-rw-r--r--secret-service/org.freedesktop.Secrets.xml490
-rw-r--r--secret-service/specification.xml580
-rw-r--r--secret-service/tools/resolve-type.xsl122
-rw-r--r--secret-service/tools/spec-to-docbook.xsl1242
-rw-r--r--secret-service/tools/spec-to-introspect.xsl147
-rw-r--r--systemtray/ChangeLog10
-rw-r--r--systemtray/systemtray-spec.xml444
146 files changed, 11620 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..0c70587
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,50 @@
+=== No ChangeLog ===
+
+ Since this module is using git, we rely on commit messages to provide change
+ history. Please write commit messages in the format described at
+ http://live.gnome.org/Git/CommitMessages
+
+ Below is a copy of this format:
+
+=== begin example commit ===
+tag: Short explanation of the commit
+
+Longer explanation explaining exactly what's changed, whether any
+external or private interfaces changed, what bugs were fixed (with bug
+tracker reference if applicable) and so forth. Be concise but not too brief.
+=== end example commit ===
+
+ - The commit message is mainly for the other people, so they should be able
+ to understand it now and six months later.
+
+ - Always add a brief description of the commit to the _first_ line of the
+ commit and terminate by two newlines (it will work without the second
+ newline, but that is not nice for the interfaces).
+
+ - First line (the brief description) must only be one sentence and should
+ start with a capital letter unless it starts with a lowercase symbol or
+ identifier. Don't use a trailing period either. Don't exceed 72 characters.
+
+ - You can prefix the first line with one tag, to make it easier to know to
+ which part of the module the commit applies. For example, a commit with
+ "fish: Make it work with newer fortune" in the gnome-panel module clearly
+ applies to the fish applet.
+
+ - The main description (the body) is normal prose and should use normal
+ punctuation and capital letters where appropriate. Normally, for patches
+ sent to a mailing list, the body is copied from there. This main
+ description can be empty if the change is self-explanatory (eg: "Add DOAP
+ file").
+
+ - When committing code on behalf of others use the --author option, e.g. git
+ commit -a --author "Joe Coder <joe@coder.org>".
+
+ - When referring to a bug, you can use this form: bgo#12345. Use bgo for
+ bugzilla.gnome.org, but you can also reference bugs in other bug trackers:
+ rh means bugzilla.redhat.com, bnc means bugzilla.novell.com, lp means
+ launchpad.net, etc. Whenever possible, use the full URL of the bug, though.
+
+ - When a commit closes a bug, the commit message should contain a line like:
+ Closes: http://bugzilla.gnome.org/show_bug.cgi?id=12345
+ or simply:
+ http://bugzilla.gnome.org/show_bug.cgi?id=12345
diff --git a/README b/README
new file mode 100644
index 0000000..490170f
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+xdg-specs
+=========
+
+This repository contains the XDG specifications.
+
+To discuss the specifications, you may use the xdg mailing list:
+
+ http://lists.freedesktop.org/mailman/listinfo/xdg
+
+
+How to report issues
+====================
+
+Issues should be reported to the freedesktop.org bug tracking system:
+
+ https://bugs.freedesktop.org/ (product Specifications)
diff --git a/autostart/.cvsignore b/autostart/.cvsignore
new file mode 100644
index 0000000..f5518ef
--- /dev/null
+++ b/autostart/.cvsignore
@@ -0,0 +1,14 @@
+config.log
+config.status
+config.sub
+configure
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h
+config.h.in
+install-sh
+missing
+autostart-spec.html
diff --git a/autostart/AUTHORS b/autostart/AUTHORS
new file mode 100644
index 0000000..cc42641
--- /dev/null
+++ b/autostart/AUTHORS
@@ -0,0 +1,5 @@
+John Palmieri <johnp@redhat.com>
+Kévin Ottens <ervin@ipsquad.net>
+Renato Caldas <seventhguardian_@hotmail.com>
+Rodrigo Moya <rodrigo@gnome-db.org>
+Waldo Bastian <waldo.bastian@intel.com>
diff --git a/autostart/COPYING b/autostart/COPYING
new file mode 100644
index 0000000..73e7995
--- /dev/null
+++ b/autostart/COPYING
@@ -0,0 +1,2 @@
+FIXME - we need a license for specs
+
diff --git a/autostart/ChangeLog b/autostart/ChangeLog
new file mode 100644
index 0000000..b7b5318
--- /dev/null
+++ b/autostart/ChangeLog
@@ -0,0 +1,4 @@
+2006-02-13 Waldo Bastian <waldo.bastian@intel.com>
+
+ * Import of autostart spec
+
diff --git a/autostart/INSTALL b/autostart/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/autostart/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/autostart/Makefile.am b/autostart/Makefile.am
new file mode 100644
index 0000000..f13d2c9
--- /dev/null
+++ b/autostart/Makefile.am
@@ -0,0 +1,16 @@
+HTML_FILES= autostart-spec.html
+
+XML_FILES= autostart-spec.xml
+
+EXTRA_DIST= $(HTML_FILES) $(XML_FILES)
+
+if XML_DOCS_ENABLED
+all-local: $(HTML_FILES)
+endif
+
+%.html: %.xml
+ $(XMLTO) html-nochunks $<
+
+maintainer-clean-local:
+ rm -f $(HTML_FILES)
+
diff --git a/autostart/NEWS b/autostart/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/autostart/NEWS
diff --git a/autostart/README b/autostart/README
new file mode 100644
index 0000000..7700629
--- /dev/null
+++ b/autostart/README
@@ -0,0 +1,6 @@
+This is the autostart specification.
+
+It covers
+* automatic launching of applications when the user logs in
+* automatic launching of applications when a new media is mounted
+
diff --git a/autostart/autogen.sh b/autostart/autogen.sh
new file mode 100755
index 0000000..b1376df
--- /dev/null
+++ b/autostart/autogen.sh
@@ -0,0 +1,3 @@
+#! /bin/sh
+autoreconf -v --install || exit 1
+./configure --enable-maintainer-mode "$@"
diff --git a/autostart/autostart-spec.xml b/autostart/autostart-spec.xml
new file mode 100644
index 0000000..ba5487e
--- /dev/null
+++ b/autostart/autostart-spec.xml
@@ -0,0 +1,273 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ <!ENTITY version "0.5">
+ <!ENTITY dtd-version "0.5">
+ ]>
+
+<article id="index">
+ <articleinfo>
+ <title>Desktop Application Autostart Specification</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <date>13 February 2006</date>
+ <authorgroup>
+ <author>
+ <firstname>John</firstname>
+ <surname>Palmieri</surname>
+ <affiliation>
+ <address>
+ <email>johnp@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Kévin</firstname>
+ <surname>Ottens</surname>
+ <affiliation>
+ <address>
+ <email>ervin@ipsquad.net</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Renato</firstname>
+ <surname>Caldas</surname>
+ <affiliation>
+ <address>
+ <email>seventhguardian_@hotmail.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Rodrigo</firstname>
+ <surname>Moya</surname>
+ <affiliation>
+ <address>
+ <email>rodrigo@gnome-db.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>bastian@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ This DRAFT document defines a method for automatically starting
+ applications during the startup of a desktop environment and after
+ mounting a removable medium.
+ </para>
+ <para>
+ Some of the file locations in this specification are specified based
+ on the <ulink url="http://standards.freedesktop.org/basedir-spec/">
+ "desktop base directory specification"</ulink>.
+ </para>
+ <para>
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
+ NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
+ "OPTIONAL" in this document are to be interpreted as
+ described in RFC 2119.
+ </para>
+ </sect1>
+ <sect1 id="startup">
+ <title>Autostart Of Applications During Startup</title>
+ <para>
+ By placing an application's .desktop file
+ in one of the Autostart directories the application will be
+ automatically launched during startup of the user's desktop environment after the user
+ has logged in.
+ </para>
+ <sect2>
+ <title>Autostart Directories</title>
+ <para>
+ The Autostart Directories are $XDG_CONFIG_DIRS/autostart as defined
+ in accordance with the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#referencing">
+ "Referencing this specification"</ulink> section in the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/">
+ "desktop base directory specification"</ulink>.
+ </para>
+ <para>
+ If the same filename is located under multiple Autostart Directories
+ only the file under the most important directory should be used.
+ </para>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_HOME is not set the Autostart Directory
+ in the user's home directory is ~/.config/autostart/
+ </para>
+ </informalexample>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_DIRS is not set the system wide Autostart
+ Directory is /etc/xdg/autostart/
+ </para>
+ </informalexample>
+ <informalexample>
+ <para>
+ Example: If $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS are not set and
+ the two files /etc/xdg/autostart/foo.desktop and
+ ~/.config/autostart/foo.desktop exist then only the file
+ ~/.config/autostart/foo.desktop will be used because
+ ~/.config/autostart/ is more important than /etc/xdg/autostart/
+ </para>
+ </informalexample>
+ </sect2>
+ <sect2>
+ <title>Application .desktop Files</title>
+ <para>
+ An application .desktop file must have the format as defined in
+ the <ulink url="http://standards.freedesktop.org/desktop-entry-spec/">
+ "Desktop Entry Specification"</ulink>. All keys should be
+ interpreted as defined with the following exceptions in order to
+ take into account that the .desktop files in an autostart directory
+ are not shown in a menu.
+ </para>
+ <sect3>
+ <title>Hidden Key</title>
+ <para>
+ When the .desktop file has the Hidden key set to true, the .desktop
+ file MUST be ignored. When multiple .desktop files with the
+ same name exists in multiple directories then only the Hidden key
+ in the most important .desktop file must be considered: If it is
+ set to true all .desktop files with the same name
+ in the other directories MUST be ignored as well.
+ </para>
+ </sect3>
+ <sect3>
+ <title>OnlyShowIn and NotShowIn Keys</title>
+ <para>
+ The OnlyShownIn entry may contain a list of strings identifying
+ the desktop environments that MUST autostart this application,
+ all other desktop environments MUST NOT autostart this application.
+ </para>
+ <para>
+ The NotShownIn entry may contain a list of strings identifying
+ the desktop environments that MUST NOT autostart this
+ application, all other desktop environments MUST autostart this
+ application.
+ </para>
+ <para>
+ Only one of these keys, either OnlyShowIn or NotShowIn, may appear
+ in a single .desktop file.
+ </para>
+ </sect3>
+ <sect3>
+ <title>TryExec Key</title>
+ <para>
+ A .desktop file with a non-empty TryExec field MUST NOT be
+ autostarted if the value of the TryExec key does NOT match with
+ an installed executable program. The value of the TryExec field
+ may either be an absolute path or the name of an executable
+ without any path components. If the name of an executable is
+ specified without any path components then the $PATH environment
+ is searched to find a matching executable program.
+ </para>
+ </sect3>
+ </sect2>
+ <sect2>
+ <title>Implementation Notes</title>
+ <informalexample>
+ <para>
+ If an application autostarts by having a .desktop file installed
+ in the system wide autostart directory, an individual user can
+ disable the autotomatic start of this application by placing a
+ .desktop file of the same name in its personal autostart directory
+ which contains the key Hidden=true.
+ </para>
+ </informalexample>
+ </sect2>
+ </sect1>
+ <sect1 id="mounting">
+ <title>Autostart Of Applications After Mount</title>
+ <para>
+ When a desktop environment mounts a new medium, the medium may contain
+ an Autostart file that can suggest to start an application or
+ an Autoopen file that can suggest to open a specific file located
+ on the medium.
+ </para>
+ <sect2>
+ <title>Autostart Files</title>
+ <para>
+ When a new medium is mounted the root directory of the medium should
+ be checked for the following Autostart files in order of precendence:
+ .autorun, autorun, autorun.sh
+ Only the first file that is present should be considered.
+ </para>
+ <para>
+ The desktop environment MAY ignore Autostart files altogether
+ based on policy set by the user, system administrator or vendor.
+ </para>
+ <para>
+ The desktop environment MUST prompt the user for confirmation before
+ automatically starting an application.
+ </para>
+ <para>
+ When an Autostart file has been detected and the user has confirmed
+ its execution the autostart file MUST be executed with the
+ current working directory (CWD) set to the root directory of the
+ medium.
+ </para>
+ </sect2>
+ <sect2>
+ <title>Autoopen Files</title>
+ <para>
+ When a new medium is mounted and a) the medium does not contain an
+ Autostart file or b) a policy to ignore Autostart files is in effect
+ then the root directory of the medium should be checked for the
+ following Autoopen files in order of precedence:
+ .autoopen, autoopen .
+ Only the first file that is present should be considered.
+ </para>
+ <para>
+ The desktop environment MAY ignore Autoopen files altogether
+ based on policy set by the user, system administrator or vendor.
+ </para>
+ <para>
+ An Autoopen file MUST contain a single relative path that points to
+ a non-executable file contained on the medium.
+ If the file contains a newline or carriage return character then the
+ newline or carriage return character itself and all characters that
+ follow MUST be ignored.
+ </para>
+ <para>
+ The relative path MUST NOT contain path components that refer to a
+ parent directory (../)
+ </para>
+ <para>
+ The relative path MUST NOT point to an executable file.
+ </para>
+ <para>
+ The desktop environment MUST verify that the relative path points to
+ a file that is actually located on the medium, taking into account any
+ symbolic or other links and MUST ignore any relative path that
+ points to a file location outside the medium itself.
+ </para>
+ <para>
+ If the relative path points to an executable file then the desktop
+ environment MUST NOT execute the file.
+ </para>
+ <para>
+ The desktop environment MUST prompt the user for confirmation before
+ opening the file.
+ </para>
+ <para>
+ When an Autoopen file has been detected and the user has confirmed
+ that the file indicated in the Autoopen file should be opened then
+ the file indicated in the Autoopen file MUST be opened in the
+ application normally preferred by the user for files of its kind
+ UNLESS the user instructed otherwise.
+ </para>
+ </sect2>
+ </sect1>
+
+</article>
+
diff --git a/autostart/configure.in b/autostart/configure.in
new file mode 100644
index 0000000..7df76ac
--- /dev/null
+++ b/autostart/configure.in
@@ -0,0 +1,61 @@
+dnl -*- mode: m4 -*-
+AC_PREREQ(2.52)
+
+AC_INIT(autostart-spec.xml)
+
+AM_INIT_AUTOMAKE(autostart-spec, 0.5)
+
+# Honor aclocal flags
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+ ## must come before we use the $USE_MAINTAINER_MODE variable later
+AM_MAINTAINER_MODE
+
+AC_ARG_ENABLE(xml-docs, [ --enable-xml-docs build XML into HTML (requires xmlto)],enable_xml_docs=$enableval,enable_xml_docs=auto)
+
+### XML tools
+
+AC_PATH_PROG(XMLTO, xmlto, no)
+
+AC_MSG_CHECKING([whether to build XML documentation])
+
+if test x$XMLTO = xno ; then
+ have_xmlto=no
+else
+ have_xmlto=yes
+fi
+
+if test x$enable_xml_docs = xauto ; then
+ if test x$have_xmlto = xno ; then
+ enable_xml_docs=no
+ else
+ enable_xml_docs=yes
+ fi
+fi
+
+if test x$enable_xml_docs = xyes; then
+ if test x$have_xmlto = xno; then
+ AC_MSG_ERROR([Building XML docs explicitly required, but xmlto not found])
+ fi
+fi
+
+AM_CONDITIONAL(XML_DOCS_ENABLED, test x$enable_xml_docs = xyes)
+AC_MSG_RESULT(yes)
+
+AC_OUTPUT([
+Makefile
+])
+
+dnl ==========================================================================
+echo "
+ Autostart specification $VERSION
+ ==============
+
+ prefix: ${prefix}
+ source code location: ${srcdir}
+ xmlto: ${XMLTO}"
+
+echo "
+ Maintainer mode: ${USE_MAINTAINER_MODE}
+ Building XML docs: ${enable_xml_docs}
+"
diff --git a/basedir/.gitignore b/basedir/.gitignore
new file mode 100644
index 0000000..126e056
--- /dev/null
+++ b/basedir/.gitignore
@@ -0,0 +1 @@
+basedir-spec.html
diff --git a/basedir/Makefile b/basedir/Makefile
new file mode 100644
index 0000000..35a73df
--- /dev/null
+++ b/basedir/Makefile
@@ -0,0 +1,7 @@
+all: basedir-spec.html
+
+%.html: %.xml
+ xmlto html-nochunks $<
+
+clean:
+ rm -f basedir-spec.html
diff --git a/basedir/basedir-spec.xml b/basedir/basedir-spec.xml
new file mode 100644
index 0000000..03a982b
--- /dev/null
+++ b/basedir/basedir-spec.xml
@@ -0,0 +1,298 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ ]>
+<article id="index">
+ <articleinfo>
+ <title>XDG Base Directory Specification</title>
+ <releaseinfo>Version 0.7</releaseinfo>
+ <date>24th November 2010</date>
+ <authorgroup>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>bastian@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Ryan</firstname>
+ <surname>Lortie</surname>
+ <affiliation>
+ <address>
+ <email>desrt@desrt.ca</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <affiliation>
+ <address>
+ <email>lennart@poettering.net</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ Various specifications specify files and file formats. This
+ specification defines where these files should be looked for by
+ defining one or more base directories relative to which files
+ should be located.
+ </para>
+ </sect1>
+
+ <sect1 id="basics">
+ <title>Basics</title>
+ <para>
+ The XDG Base Directory Specification is based on the following concepts:
+ <itemizedlist>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ data files should be written. This directory is defined by the
+ environment variable <literal>$XDG_DATA_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ configuration files should be written. This directory is defined by the
+ environment variable <literal>$XDG_CONFIG_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a set of preference ordered base directories relative to
+ which data files should be searched. This set of directories is defined
+ by the environment variable <literal>$XDG_DATA_DIRS</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a set of preference ordered base directories relative to
+ which configuration files should be searched.
+ This set of directories is defined
+ by the environment variable <literal>$XDG_CONFIG_DIRS</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which user-specific
+ non-essential (cached) data should be written.
+ This directory is defined by the
+ environment variable <literal>$XDG_CACHE_HOME</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There is a single base directory relative to which
+ user-specific runtime files and other file objects should
+ be placed. This directory is defined by the environment
+ variable <literal>$XDG_RUNTIME_DIR</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>All paths set in these environment variables must be
+ absolute. If an implementation encounters a relative path in any
+ of these variables it should consider the path invalid and ignore
+ it.</para>
+ </sect1>
+
+
+ <sect1 id="variables">
+ <title>Environment variables</title>
+ <para>
+ <literal>$XDG_DATA_HOME</literal> defines the base directory relative to
+ which user specific data files should be stored. If
+ <literal>$XDG_DATA_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.local/share should be used.
+ </para>
+ <para>
+ <literal>$XDG_CONFIG_HOME</literal> defines the base directory relative to
+ which user specific configuration files should be stored. If
+ <literal>$XDG_CONFIG_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.config should be used.
+ </para>
+ <para>
+ <literal>$XDG_DATA_DIRS</literal> defines the preference-ordered set of
+ base directories to search for data files in addition to the
+ <literal>$XDG_DATA_HOME</literal> base directory.
+ The directories in <literal>$XDG_DATA_DIRS</literal> should be seperated
+ with a colon ':'.
+ </para>
+ <para>
+ If <literal>$XDG_DATA_DIRS</literal> is either not set or empty, a value equal to
+ /usr/local/share/:/usr/share/ should be used.
+ </para>
+ <para>
+ <literal>$XDG_CONFIG_DIRS</literal> defines the preference-ordered set of
+ base directories to search for configuration files in addition to the
+ <literal>$XDG_CONFIG_HOME</literal> base directory.
+ The directories in <literal>$XDG_CONFIG_DIRS</literal> should be seperated
+ with a colon ':'.
+ </para>
+ <para>
+ If <literal>$XDG_CONFIG_DIRS</literal> is either not set or empty, a value equal to
+ /etc/xdg should be used.
+ </para>
+ <para>
+ The order of base directories denotes their importance; the first
+ directory listed is the most important. When the same information is
+ defined in multiple places the information defined relative to the more
+ important base directory takes precedent. The base directory defined
+ by <literal>$XDG_DATA_HOME</literal> is considered more important than
+ any of the base directories defined by <literal>$XDG_DATA_DIRS</literal>.
+ The base directory defined
+ by <literal>$XDG_CONFIG_HOME</literal> is considered more important than
+ any of the base directories defined by <literal>$XDG_CONFIG_DIRS</literal>.
+ </para>
+ <para>
+ <literal>$XDG_CACHE_HOME</literal> defines the base directory relative to
+ which user specific non-essential data files should be stored. If
+ <literal>$XDG_CACHE_HOME</literal> is either not set or empty, a default equal to
+ <literal>$HOME</literal>/.cache should be used.
+ </para>
+
+ <para>
+ <literal>$XDG_RUNTIME_DIR</literal> defines the base directory
+ relative to which user-specific non-essential runtime files and
+ other file objects (such as sockets, named pipes, ...) should be
+ stored. The directory MUST be owned by the user, and he MUST be
+ the only one having read and write access to it. Its Unix access
+ mode MUST be 0700.
+ </para>
+ <para>
+ The lifetime of the directory MUST be bound to the user being
+ logged in. It MUST be created when the user first logs in and if
+ the user fully logs out the directory MUST be removed. If the
+ user logs in more than once he should get pointed to the same
+ directory, and it is mandatory that the directory continues to
+ exist from his first login to his last logout on the system, and
+ not removed in between. Files in the directory MUST not survive
+ reboot or a full logout/login cycle.
+ </para>
+ <para>
+ The directory MUST be on a local file system and not shared with
+ any other system. The directory MUST by fully-featured by the
+ standards of the operating system. More specifically, on
+ Unix-like operating systems AF_UNIX sockets, symbolic links,
+ hard links, proper permissions, file locking, sparse files,
+ memory mapping, file change notifications, a reliable hard link
+ count must be supported, and no restrictions on the file name
+ character set should be imposed. Files in this directory MAY be
+ subjected to periodic clean-up. To ensure that your files are
+ not removed, they should have their access time timestamp
+ modified at least once every 6 hours of monotonic time or the
+ 'sticky' bit should be set on the file.
+ </para>
+ <para>
+ If <literal>$XDG_RUNTIME_DIR</literal> is not set applications
+ should fall back to a replacement directory with similar
+ capabilities and print a warning message. Applications should
+ use this directory for communication and synchronization
+ purposes and should not place larger files in it, since it might
+ reside in runtime memory and cannot necessarily be swapped out
+ to disk.
+ </para>
+ </sect1>
+
+ <sect1 id="referencing">
+ <title>Referencing this specification</title>
+ <para>
+ Other specifications may reference this specification by specifying the
+ location of a data file as
+ <literal>$XDG_DATA_DIRS</literal>/subdir/filename. This implies that:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Such file should be installed to <literal>$datadir</literal>/subdir/filename
+ with <literal>$datadir</literal> defaulting to /usr/share.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A user specific version of the data file may be created in
+ <literal>$XDG_DATA_HOME</literal>/subdir/filename, taking into
+ account the default value for <literal>$XDG_DATA_HOME</literal> if
+ <literal>$XDG_DATA_HOME</literal> is not set.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lookups of the data file should search for ./subdir/filename relative to
+ all base directories specified by <literal>$XDG_DATA_HOME</literal> and
+ <literal>$XDG_DATA_DIRS</literal> . If an environment
+ variable is either not set or empty, its default value as defined by this specification
+ should be used instead.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Specifications may reference this specification by specifying the
+ location of a configuration file as
+ <literal>$XDG_CONFIG_DIRS</literal>/subdir/filename. This implies that:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Default configuration files should be installed to <literal>$sysconfdir</literal>/xdg/subdir/filename
+ with <literal>$sysconfdir</literal> defaulting to /etc.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A user specific version of the configuration file may be created in
+ <literal>$XDG_CONFIG_HOME</literal>/subdir/filename, taking into
+ account the default value for <literal>$XDG_CONFIG_HOME</literal> if
+ <literal>$XDG_CONFIG_HOME</literal> is not set.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Lookups of the configuration file should search for ./subdir/filename relative to
+ all base directories indicated by <literal>$XDG_CONFIG_HOME</literal> and
+ <literal>$XDG_CONFIG_DIRS</literal> . If an environment
+ variable is either not set or empty, its default value as defined by this specification
+ should be used instead.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ If, when attempting to write a file, the destination
+ directory is non-existant an attempt should be made to create it
+ with permission <literal>0700</literal>. If the destination directory
+ exists already the permissions should not be changed.
+ The application should be prepared to handle the case where the file
+ could not be written, either because the directory was non-existant
+ and could not be created, or for any other reason. In such case it
+ may chose to present an error message to the user.
+ </para>
+ <para>
+ When attempting to read a file, if for any reason a file in a certain
+ directory is unaccessible, e.g. because the directory is non-existant,
+ the file is non-existant or the user is not authorized to open the file,
+ then the processing of the file in that directory should be skipped.
+ If due to this a required file could not be found at all, the
+ application may chose to present an error message to the user.
+ </para>
+ <para>
+ A specification that refers to <literal>$XDG_DATA_DIRS</literal> or
+ <literal>$XDG_CONFIG_DIRS</literal> should define what the behaviour
+ must be when a file is located under multiple base directories.
+ It could, for example, define that only the file under the most
+ important base directory should be used or, as another example,
+ it could define rules for merging the information from the different
+ files.
+ </para>
+ </sect1>
+
+</article>
diff --git a/desktop-entry/.cvsignore b/desktop-entry/.cvsignore
new file mode 100644
index 0000000..e2bf8b5
--- /dev/null
+++ b/desktop-entry/.cvsignore
@@ -0,0 +1,2 @@
+*.ps
+*.html
diff --git a/desktop-entry/ChangeLog b/desktop-entry/ChangeLog
new file mode 100644
index 0000000..7113101
--- /dev/null
+++ b/desktop-entry/ChangeLog
@@ -0,0 +1,182 @@
+2008-09-12 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: reword sentence about characters allowed in
+ key names to avoid some ambiguity.
+ Patch by Simon McVittie <simon.mcvittie@collabora.co.uk>
+
+2008-03-04 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: bump version to 1.1-draft, so we can publish
+ it. See http://bugs.freedesktop.org/show_bug.cgi?id=14097
+
+2007-09-10 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: fix Icon key in example: it was using the
+ .png extension for an icon name. Which is wrong :-)
+
+2007-07-27 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: don't say that the files are encoded as
+ lines of 8-bit characters, which is wrong for UTF-8. Use something a
+ bit better proposed by Matthias Clasen <mclasen@redhat.com>
+ Bug #11417.
+
+2007-05-30 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: s/DESKTOP_LAUNCH_ID/DESKTOP_STARTUP_ID/
+ Thanks to Dan Winship <danw@gnome.org> for catching this.
+ Bug #11081.
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: scanf behavior is locale-dependent, and the
+ numeric type shouldn't be. Specify that the value is a floating point
+ number as recognized by scanf in the C locale.
+ Thanks to Dan Winship <danw@gnome.org> for catching this.
+ http://lists.freedesktop.org/archives/xdg/2007-April/009732.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: clarify description for the TryExec key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009556.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: change "the $PATH" to "the $PATH environment
+ variable".
+ http://lists.freedesktop.org/archives/xdg/2007-March/009552.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: improve the description of the Comment key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009440.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: a desktop file of type Application should
+ always have an Exec key. Same for Link Type and URL key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: desktop files of type Directory have a
+ .directory extension right now (at least in GNOME), but according to
+ the spec, this is wrong. The patch fixes this. Also, it removes a
+ small part of text explaining the name of a desktop file describing a
+ directory.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml:clarify a bit the text about keys that only
+ makes sense in a specific context. Also, state that the keys should
+ not be used outside of this context. For example, the URL key should
+ not be present if the type is not Link.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: small typo
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: this changes the type of the version from
+ numeric to string. It's needed if we consider the possibility that
+ there are some desktop entry files out there using 0.9.x as a value
+ for this key. The change doesn't break anything.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: UnmountIcon is a localestring key.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-04-28 Vincent Untz <vuntz@gnome.org>
+
+ * desktop-entry-spec.xml: clarify that lists should always end with a
+ semicolon, even if there's only one value.
+ http://lists.freedesktop.org/archives/xdg/2007-March/009437.html
+
+2007-02-06 Waldo Bastian <waldo.bastian@intel.com>
+ * Clearify that %U may result in either local paths or file: URLs
+ * Deprecate %d, %n, %D and %N. They aren't usable on Gnome and
+ you can do the same with dirname and basename in a script.
+ * Clearify that %f, %u, %F or %U should only be used once.
+ * Clearify that these codes should be ignored if there is no file
+ to open.
+ * Forbid the use of field codes in quoted arguments.
+ * Clearify that %F and %U should only be used as a stand alone argument.
+ * Bumped version to 1.0
+
+2006-10-30 Waldo Bastian <waldo.bastian@intel.com>
+ * Remove equal sign from reserved characters in Exec args
+ * Add backslash to list of reserved characters in Exec args
+
+2006-10-04 Waldo Bastian <waldo.bastian@intel.com>
+ * Deprecate %v, it is FSDevice specific
+
+2006-08-30 Waldo Bastian <waldo.bastian@intel.com>
+ * list FSDevice type as KDE specific
+ * refer to Icon Theme Specification
+
+2006-08-22 Waldo Bastian <waldo.bastian@intel.com>
+ * clarification of Exec key
+ * correction of StartupWMClass, clarify StartupNotify (Lubos Lunak)
+ * deprecate SortOrder and FilePattern key (Vincent Untz)
+ * remove section on "MIME Type caching" from spec (Vincent Untz)
+ * groups and keys must be unique (Vincent Untz)
+ See http://lists.freedesktop.org/archives/xdg/2006-August/008446.html
+
+2006-05-28 Vincent Untz <vuntz@gnome.org>
+ * cleanup of text and some reorg for clearity
+ * move all of the legacy-mixed encoding stuff to the appendix
+ * deprecate the Swallow* keys
+ * improve definition of group headers
+ * reorder the list of standard keys so they are sorted by type
+ See http://lists.freedesktop.org/archives/xdg/2006-May/008131.html
+
+Tue Jul 13 18:04:11 2004 Jonathan Blandford <jrb@gnome.org>
+
+ * desktop-entry-spec.xml: Update the MIME description to make it
+ more relevent.
+
+2004-04-19 Mark McLoughlin <mark@skynet.ie>
+
+ Pointed out by Ville Skyttä <ville.skytta@iki.fi>
+
+ * desktop-entry-spec.xml: fix minor typo.
+
+2004-04-18 Mark McLoughlin <mark@skynet.ie>
+
+ Patch from Ville Skyttä <ville.skytta@iki.fi>
+
+ * desktop-entry-spec.xml: editorial changes, fixes for
+ typos and general cleanups.
+
+Mon Jul 7 18:26:14 2003 Owen Taylor <otaylor@redhat.com>
+
+ * destop-entry-spec.sgml: Merge changes by Havoc
+ Pennington that had been made in web module:
+
+ 2002/04/20: add changes posted to xdg-list a while back
+
+Sat Aug 24 17:00:48 2002 Owen Taylor <otaylor@redhat.com>
+
+ * desktop-entry-spec.sgml: In legacy-mixed table, fix duplicate
+ pt entry, language code for Armenian. Make explicit that
+ language-country locale tags can match a single language in
+ the table. (Pablo Saratxaga)
+
+Tue Mar 13 12:37:03 2001 Owen Taylor <otaylor@redhat.com>
+
+ * 0.9.3
+
+ * Reworked the description of encoding handling for
+ Legacy-Mixed for clarity and added a bit more details
+ on how encoding-matching should be done.
+
+Thu Mar 8 17:42:05 2001 Owen Taylor <otaylor@redhat.com>
+
+ * Import into CVS.
+
diff --git a/desktop-entry/desktop-entry-spec.xml b/desktop-entry/desktop-entry-spec.xml
new file mode 100644
index 0000000..34bc154
--- /dev/null
+++ b/desktop-entry/desktop-entry-spec.xml
@@ -0,0 +1,1232 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ ]>
+<article id="index">
+ <articleinfo>
+ <title>Desktop Entry Specification</title>
+ <releaseinfo>Version 1.1-draft</releaseinfo>
+ <date>4 Mar 2008</date>
+ <authorgroup>
+ <author>
+ <firstname>Preston</firstname>
+ <surname>Brown</surname>
+ <affiliation>
+ <address>
+ <email>pbrown@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Jonathan</firstname>
+ <surname>Blandford</surname>
+ <affiliation>
+ <address>
+ <email>jrb@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Owen</firstname>
+ <surname>Taylor</surname>
+ <affiliation>
+ <address>
+ <email>otaylor@gtk.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Vincent</firstname>
+ <surname>Untz</surname>
+ <affiliation>
+ <address>
+ <email>vuntz@gnome.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>waldo.bastian@intel.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ Both the KDE and GNOME desktop environments have adopted a similar
+ format for "desktop entries", or configuration files describing how a
+ particular program is to be launched, how it appears in menus, etc.
+ It is to the larger community's benefit that a unified standard be
+ agreed upon by all parties such that interoperation between the two
+ environments, and indeed any additional environments that implement
+ the specification, becomes simpler.
+ </para>
+ </sect1>
+ <sect1 id="basic-format">
+ <title>Basic format of the file</title>
+ <para>
+ Desktop entry files should have the <filename>.desktop</filename>
+ extension, except for files of <varname>Type</varname>
+ <constant>Directory</constant> which should have the
+ <filename>.directory</filename> extension. Determining file type on basis
+ of extension makes determining the file type very easy and quick.
+ When no file extension is present, the desktop system should
+ fall back to recognition via "magic detection".
+ </para>
+ <para>
+ Desktop entry files are encoded in UTF-8. A file is interpreted as a
+ series of lines that are separated by linefeed characters. Case is
+ significant everywhere in the file.
+ </para>
+ <para>
+ Compliant implementations MUST not remove any fields from the file,
+ even if they don't support them. Such fields must be maintained in a
+ list somewhere, and if the file is "rewritten", they will be included.
+ This ensures that any desktop-specific extensions will be preserved
+ even if another system accesses and changes the file.
+ </para>
+ <sect2 id="comments">
+ <title>Comments</title>
+ <para>
+ Lines beginning with a <literal>#</literal> and blank lines are
+ considered comments and will be ignored, however they should be
+ preserved across reads and writes of the desktop entry file.
+ </para>
+ <para>
+ Comment lines are uninterpreted and may contain any character
+ (except for LF). However, using UTF-8 for comment lines that
+ contain characters not in ASCII is encouraged.
+ </para>
+ </sect2>
+ <sect2 id="group-header">
+ <title>Group headers</title>
+ <para>
+ A group header with name <literal>groupname</literal> is a line in the
+ format:
+ </para>
+ <programlisting>[groupname]</programlisting>
+ <para>
+ Group names may contain all ASCII characters except for
+ <literal>[</literal> and <literal>]</literal> and control characters.
+ </para>
+ <para>
+ Multiple groups may not have the same name.
+ </para>
+ <para>
+ All <literal>{key,value}</literal> pairs following a group header until
+ a new group header belong to the group.
+ </para>
+ <para>
+ The basic format of the desktop entry file requires that there be
+ a group header named <literal>Desktop Entry</literal>. There may
+ be other groups present in the file, but this is the most
+ important group which explicitly needs to be supported. This
+ group should also be used as the "magic key" for automatic MIME
+ type detection. There should be nothing preceding this group in
+ the desktop entry file but possibly one or more comments.
+ </para>
+ </sect2>
+ <sect2 id="entries">
+ <title>Entries</title>
+ <para>
+ Entries in the file are <literal>{key,value}</literal> pairs in the
+ format:
+ </para>
+ <programlisting>Key=Value</programlisting>
+ <para>
+ Space before and after the equals sign should be ignored; the
+ <literal>=</literal> sign is the actual delimiter.
+ </para>
+ <para>
+ Only the characters <literal>A-Za-z0-9-</literal> may be used in
+ key names.
+ </para>
+ <para>
+ As the case is significant, the keys <varname>Name</varname> and
+ <varname>NAME</varname> are not equivalent.
+ </para>
+ <para>
+ Multiple keys in the same group may not have the same name. Keys in
+ different groups may have the same name.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="value-types">
+ <title>Possible value types</title>
+ <para>
+ The value types recognized are <literal>string</literal>,
+ <literal>localestring</literal>,
+ <literal>boolean</literal>, and
+ <literal>numeric</literal>.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Values of type <literal>string</literal> may contain all ASCII
+ characters except for control characters.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>localestring</literal> are user displayable,
+ and are encoded in UTF-8.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>boolean</literal> must either be the string
+ <literal>true</literal> or <literal>false</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Values of type <literal>numeric</literal> must be a valid floating
+ point number as recognized by the <literal>%f</literal> specifier for
+ <function>scanf</function> in the <literal>C</literal> locale.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ The escape sequences <literal>\s</literal>, <literal>\n</literal>,
+ <literal>\t</literal>, <literal>\r</literal>, and
+ <literal>\\</literal> are supported for values of type
+ <literal>string</literal> and <literal>localestring</literal>, meaning
+ ASCII space, newline, tab, carriage return, and backslash, respectively.
+ </para>
+ <para>
+ Some keys can have multiple values. In such a case, the value of the key
+ is specified as a plural: for example, <literal>string(s)</literal>. The
+ multiple values should be separated by a semicolon, and the value of the
+ key should have a semicolon as trailing character. Semicolons in these
+ values need to be escaped using <literal>\;</literal>.
+ </para>
+ </sect1>
+ <sect1 id="localized-keys">
+ <title>Localized values for keys</title>
+ <para>
+ Keys with type <literal>localestring</literal> may be postfixed by
+ [<replaceable>LOCALE</replaceable>],
+ where <replaceable>LOCALE</replaceable> is the locale type of the
+ entry. <replaceable>LOCALE</replaceable> must be of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>.<replaceable>ENCODING</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ where
+ <literal>_<replaceable>COUNTRY</replaceable></literal>,
+ <literal>.<replaceable>ENCODING</replaceable></literal>,
+ and <literal>@<replaceable>MODIFIER</replaceable></literal>
+ may be omitted. If a postfixed key occurs, the same
+ key must be also present without the postfix.
+ </para>
+ <para>
+ When reading in the desktop entry file, the value of the key is
+ selected by matching the current POSIX locale for the
+ <varname>LC_MESSAGES</varname> category against the
+ <replaceable>LOCALE</replaceable> postfixes of all occurrences
+ of the key, with the
+ <literal>.<replaceable>ENCODING</replaceable></literal> part
+ stripped.
+ </para>
+ <para>
+ The matching is done as follows. If
+ <varname>LC_MESSAGES</varname> is of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>.<replaceable>ENCODING</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ then it will match a key of the form
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal>.
+ If such a key does not exist, it will attempt to match
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>
+ followed by
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>.
+ Then, a match against <replaceable>lang</replaceable> by itself
+ will be attempted. Finally, if no matching key is found the
+ required key without a locale specified is used. The encoding
+ from the <varname>LC_MESSAGES</varname> value is ignored
+ when matching.
+ </para>
+ <para>
+ If <varname>LC_MESSAGES</varname> does not have a <replaceable>MODIFIER</replaceable>
+ field, then no key with a modifier will be matched. Similarly, if
+ <varname>LC_MESSAGES</varname> does not have a <replaceable>COUNTRY</replaceable>
+ field, then no key with a country specified will be matched. If
+ <varname>LC_MESSAGES</varname> just has a <replaceable>lang</replaceable> field, then
+ it will do a straight match to a key with a similar value. The
+ following table lists possible matches of various <varname>LC_MESSAGES</varname> values in
+ the order in which they are matched. Note that the
+ <replaceable>ENCODING</replaceable> field isn't shown.
+ </para>
+ <table>
+ <title>Locale Matching</title>
+ <tgroup cols="2">
+<thead>
+ <row>
+ <entry><varname>LC_MESSAGES</varname> value</entry>
+ <entry>Possible keys in order of matching</entry>
+ </row>
+</thead>
+<tbody>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>,
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <literal><replaceable>lang</replaceable></literal>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>,
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal></entry>
+ <entry>
+ <literal><replaceable>lang</replaceable>@<replaceable>MODIFIER</replaceable></literal>,
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+ <row>
+ <entry><replaceable>lang</replaceable></entry>
+ <entry>
+ <replaceable>lang</replaceable>,
+ default value
+ </entry>
+ </row>
+</tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ For example, if the current value of the <varname>LC_MESSAGES</varname> category
+ is <literal>sr_YU@Latn</literal> and the desktop file includes:
+ </para>
+ <programlisting>
+ Name=Foo
+ Name[sr_YU]=...
+ Name[sr@Latn]=...
+ Name[sr]=...</programlisting>
+ <para>
+ then the value of the <varname>Name</varname> keyed by <literal>sr_YU</literal> is used.
+ </para>
+ </sect1>
+ <sect1 id="recognized-keys">
+ <title>Recognized desktop entry keys</title>
+ <para>
+ Keys are either OPTIONAL or REQUIRED. If a key is OPTIONAL it may or
+ may not be present in the file. However, if it isn't, the
+ implementation of the standard should not blow up, it must provide
+ some sane defaults.
+ </para>
+ <para>
+ Some keys only make sense in the context when another particular key
+ is also present and set to a specific value. Those keys should not be
+ used if the particular key is not present or not set to the specific
+ value. For example, the <varname>Terminal</varname> key can only be used
+ when the value of the <varname>Type</varname> key is
+ <constant>Application</constant>.
+ </para>
+ <para>
+ If a REQUIRED key is only valid in the context of another key set to a
+ specific value, then it has to be present only if the other key is set to
+ the specific value. For example, the <varname>URL</varname> key has to be
+ present when and only when when the value of the <varname>Type</varname>
+ key is <constant>Link</constant>.
+ </para>
+ <para>
+ Some example keys: <varname>Name[C]</varname>, <varname>Comment[it]</varname>.
+ </para>
+ <table>
+ <title>Standard Keys</title>
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Description</entry>
+ <entry>Value Type</entry>
+ <entry>REQ?</entry>
+ <entry>Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry id="key-type"><varname>Type</varname></entry>
+ <entry>
+ This specification defines 3 types of desktop entries:
+ <constant>Application</constant> (type 1),
+ <constant>Link</constant> (type 2)
+ and <constant>Directory</constant> (type 3).
+ To allow the addition of new types in the future,
+ implementations should ignore desktop entries with an
+ unknown type.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ </row>
+ <row>
+ <entry id="key-version"><varname>Version</varname></entry>
+ <entry>
+ Version of the Desktop Entry Specification that the
+ desktop entry conforms with. Entries that confirm with this
+ version of the specification should use <literal>1.0</literal>.
+ Note that the version field is not required to be present.
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-name"><varname>Name</varname></entry>
+ <entry>
+ Specific name of the application, for example "Mozilla".
+ </entry>
+ <entry>localestring</entry>
+ <entry>YES</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-genericname"><varname>GenericName</varname></entry>
+ <entry>
+ Generic name of the application, for example "Web Browser".
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-nodisplay"><varname>NoDisplay</varname></entry>
+ <entry>
+ <varname>NoDisplay</varname> means "this application exists, but don't display it in the menus".
+ This can be useful to e.g. associate this application with MIME types, so that
+ it gets launched from a file manager (or other apps), without having a menu
+ entry for it (there are tons of good reasons for this, including e.g. the
+ <literal>netscape -remote</literal>, or <literal>kfmclient openURL</literal> kind of stuff).
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-comment"><varname>Comment</varname></entry>
+ <entry>
+ Tooltip for the entry, for example "View sites on the
+ Internet". The value should not be redundant with the values of
+ <varname>Name</varname> and <varname>GenericName</varname>.
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-icon"><varname>Icon</varname></entry>
+ <entry>
+ Icon to display in file manager, menus, etc. If the
+ name is an absolute path, the given file will be
+ used. If the name is not an absolute path, the algorithm described
+ in the <ulink
+ url="http://freedesktop.org/wiki/Standards/icon-theme-spec">Icon
+ Theme Specification</ulink> will be used to locate the icon.
+ </entry>
+ <entry>localestring</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-hidden"><varname>Hidden</varname></entry>
+ <entry>
+ <varname>Hidden</varname> should have been called <varname>Deleted</varname>.
+ It means the user deleted (at his level)
+ something that was present (at an upper level, e.g. in the system dirs). It's
+ strictly equivalent to the <filename>.desktop</filename> file not existing at all, as far as that user is
+ concerned. This can also be used to "uninstall" existing files (e.g. due to a renaming)
+ - by letting <literal>make install</literal> install a file with <literal>Hidden=true</literal> in it.
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-onlyshowin"><varname>OnlyShowIn</varname>, <varname>NotShowIn</varname></entry>
+ <entry>
+ A list of strings identifying the environments that should
+ display/not display a given desktop entry. Only one of
+ these keys, either <varname>OnlyShowIn</varname> or
+ <varname>NotShowIn</varname>, may appear in a group (for
+ possible values see the <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink>).
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1-3</entry>
+ </row>
+ <row>
+ <entry id="key-tryexec"><varname>TryExec</varname></entry>
+ <entry>
+ Path to an executable file on disk used to determine if the program
+ is actually installed. If the path is not an absolute path, the file
+ is looked up in the $PATH environment variable. If the file is not
+ present or if it is not executable, the entry may be ignored (not be
+ used in menus, for example).
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-exec"><varname>Exec</varname></entry>
+ <entry>
+ Program to execute, possibly with arguments.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-path"><varname>Path</varname></entry>
+ <entry>
+ If entry is of type <constant>Application</constant>, the working directory to run the program in.
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-terminal"><varname>Terminal</varname></entry>
+ <entry>
+ Whether the program runs in a terminal window.
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-mimetype"><varname>MimeType</varname></entry>
+ <entry>
+ The MIME type(s) supported by this application.
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-categories"><varname>Categories</varname></entry>
+ <entry>
+ Categories in which the entry should be shown in a menu (for
+ possible values see the <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink>).
+ </entry>
+ <entry>string(s)</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-startupnotify"><varname>StartupNotify</varname></entry>
+ <entry>
+ If true, it is KNOWN that the application will send a "remove"
+ message when started with the DESKTOP_STARTUP_ID environment variable set.
+ If false, it is KNOWN that the application does not work
+ with startup notification at all (does not shown any window, breaks
+ even when using StartupWMClass, etc.).
+ If absent, a reasonable handling is up to implementations (assuming false,
+ using StartupWMClass, etc.). (See the <ulink url="http://www.freedesktop.org/Standards/startup-notification-spec">Startup Notification Protocol Specification</ulink> for more details).
+ </entry>
+ <entry>boolean</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-startupwmclass"><varname>StartupWMClass</varname></entry>
+ <entry>
+ If specified, it is known that the application will map at least one
+ window with the given string as its WM class or WM name hint (see the <ulink url="http://www.freedesktop.org/Standards/startup-notification-spec">Startup Notification Protocol Specification</ulink> for more details).
+ </entry>
+ <entry>string</entry>
+ <entry>NO</entry>
+ <entry>1</entry>
+ </row>
+ <row>
+ <entry id="key-url"><varname>URL</varname></entry>
+ <entry>
+ If entry is Link type, the URL to access.
+ </entry>
+ <entry>string</entry>
+ <entry>YES</entry>
+ <entry>2</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+ <sect1 id="exec-variables">
+ <title>The <varname>Exec</varname> key</title>
+ <para>
+ The <varname>Exec</varname> key must contain a command line.
+ A command line consists of an executable program optionally followed
+ by one or more arguments.
+ The executable program can either be specified with its full path or
+ with the name of the executable only. If no full path is provided the
+ executable is looked up in the $PATH environment variable used by the
+ desktop environment.
+ The name or path of the executable program may not contain the equal
+ sign ("="). Arguments are separated by a space.
+ </para>
+ <para>
+ Arguments may be quoted in whole. If an argument contains a reserved
+ character the argument must be quoted. The rules for quoting of
+ arguments is also applicable to the executable name or path of the
+ executable program as provided.
+ </para>
+ <para>
+ Quoting must be done by enclosing the argument between double quotes
+ and escaping the double quote character, backtick character ("`"),
+ dollar sign ("$") and backslash character ("\") by preceding it with an
+ additional backslash character. Implementations must undo quoting before
+ expanding field codes and before passing the argument to the executable
+ program. Reserved characters are space (" "), tab, newline, double
+ quote, single quote ("'"), backslash character ("\"),
+ greater-than sign ("&gt;"), less-than sign ("&lt;"),
+ tilde ("~"), vertical bar ("|"), ampersand ("&amp;"), semicolon (";"),
+ dollar sign ("$"), asterisk ("*"), question mark ("?"), hash mark ("#"),
+ parenthesis ("(") and (")") and backtick character ("`").
+ </para>
+ <para>
+ Note that the general escape rule for values of type string states that
+ the backslash character can be escaped as ("\\") as well and that this
+ escape rule is applied before the quoting rule. As such, to
+ unambiguously represent a literal backslash character in a quoted
+ argument in a desktop entry file requires the use of four successive
+ backslash characters ("\\\\"). Likewise, a literal dollar sign in a
+ quoted argument in a desktop entry file is unambiguously represented
+ with ("\\$").
+ </para>
+ <para>
+ A number of special field codes have been defined which will be
+ expanded by the file manager or program launcher when encountered
+ in the command line.
+ Field codes consist of the percentage character ("%") followed by
+ an alpha character. Literal percentage characters must be escaped
+ as <literal>%%</literal>.
+ Deprecated field codes should be removed from the command line and
+ ignored.
+ Field codes are expanded only once, the string that is used to
+ replace the field code should not be checked for field codes itself.
+ </para>
+ <para>
+ Command lines that contain a field code that is not listed in this
+ specification are invalid and must not be processed, in particular
+ implementations may not introduce support for field codes not listed
+ in this specification. Extensions, if any, should be introduced by
+ means of a new key.
+ </para>
+ <para>
+ Implementations must take care not to expand field codes into multiple
+ arguments unless explicitly instructed by this specification.
+ This means that name fields, filenames and other replacements that
+ can contain spaces must be passed as a single argument
+ to the executable program after expansion.
+ </para>
+ <para>
+ Although the <varname>Exec</varname> key is defined to have a value
+ of the type string, which is limited to ASCII characters, field code
+ expansion may introduce non-ASCII characters in arguments.
+ Implementations must take care that all characters in arguments
+ passed to the executable program are properly encoded according to
+ the applicable locale setting.
+ </para>
+ <para>
+ Recognized field codes are as follows:
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Code</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>%f</literal></entry>
+ <entry>
+ A single file name, even if multiple files are selected. The system
+ reading the desktop entry should recognize that the program in
+ question cannot handle multiple file arguments, and it should
+ should probably spawn and execute multiple copies of a program
+ for each selected file if the program is not able to handle
+ additional file arguments. If files are not on the local file system
+ (i.e. are on HTTP or FTP locations), the files will be copied to the local
+ file system and <literal>%f</literal> will be expanded to point at the temporary
+ file. Used for programs that do not understand the URL syntax.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%F</literal></entry>
+ <entry>
+ A list of files. Use for apps that can open several local
+ files at once.
+ Each file is passed as a separate argument to
+ the executable program.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%u</literal></entry>
+ <entry>
+ A single URL. Local files may either be passed as
+ file: URLs or as file path.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%U</literal></entry>
+ <entry>
+ A list of URLs.
+ Each URL is passed as a separate argument to
+ the executable program. Local files may either be passed as
+ file: URLs or as file path.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%d</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%D</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%n</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%N</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%i</literal></entry>
+ <entry>
+ The <varname>Icon</varname> key of the desktop entry
+ expanded as two arguments, first
+ <literal>--icon</literal> and then the value of the
+ <varname>Icon</varname> key. Should not expand to any
+ arguments if the <varname>Icon</varname> key is empty
+ or missing.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%c</literal></entry>
+ <entry>
+ The translated name of the application as listed in
+ the appropriate <varname>Name</varname> key in the
+ desktop entry.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%k</literal></entry>
+ <entry>
+ The location of the desktop file as either a URI (if for
+ example gotten from the vfolder system) or a local
+ filename or empty if no location is known.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%v</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>%m</literal></entry>
+ <entry>
+ Deprecated.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <para>
+ A command line may contain at most one %f, %u, %F or %U field code.
+ If the application should not open any
+ file the %f, %u, %F and %U field codes must be removed from the
+ command line and ignored.
+ </para>
+ <para>
+ Field codes must not be used inside a quoted argument, the result of
+ field code expansion inside a quoted argument is undefined. The %F and
+ %U field codes may only be used as an argument on their own.
+ </para>
+ </sect1>
+ <sect1 id="mime-types">
+ <title>Registering MIME Types</title>
+ <para>
+ The <varname>MimeType</varname> key is used to indicate the MIME
+ Types that an application knows how to handle. It is expected that
+ for some applications this list could become long. An application
+ is expected to be able to reasonably open files of these types
+ using the command listed in the <varname>Exec</varname> key.
+ </para>
+ <para>
+ There should be no priority for MIME Types in this field, or any
+ form of priority in the desktop file. Priority for applications
+ is handled external to the <filename>.desktop</filename> files.
+ </para>
+<!--
+ <sect2 id="mime-caching">
+ <title>Caching MIME Types</title>
+ <para>
+ To make parsing of all the desktop files less costly, a
+ <command>update-desktop-database</command> program is provided
+ that will generate a cache file. The concept is identical to
+ that of the 'update-mime-database' program in that it lets
+ applications avoid reading in (potentially) hundreds of files.
+ It will need to be run after every desktop file is installed.
+ One cache file is created for every directory in
+ $XDG_DATA_DIRS/applications/, and will create a file called
+ $XDG_DATA_DIRS/applications/mimeinfo.cache.
+ </para>
+ <para>
+ The format of the cache is similar to that of the desktop file,
+ and is just a list mapping mime-types to desktop files. Here's
+ a quick example of what it would look like:
+ </para>
+ <programlisting>application/x-foo=foo.desktop;bar.desktop;
+application/x-bar=bar.desktop;</programlisting>
+ <para>
+ Each MIME Type is listed only once per cache file, and the
+ desktop-id is expected to exist in that particular directory.
+ That is to say, if the cache file is located at
+ <filename>/usr/share/applications/mimeinfo.cache</filename>,
+ bar.desktop refers to the file
+ <filename>/usr/share/applications/bar.desktop</filename>.
+ </para>
+ </sect2>
+ <sect2 id="mime-priority">
+ <title>Priority of MIME Types and desktop files</title>
+ <para>
+ There is also a preference list to determine preferred
+ application of a given MIME Type. It defines the 'default'
+ application to handle a given MIME Type. It has the same format
+ as the cache list.
+ </para>
+ <programlisting>mime/type=desktop-id.desktop;</programlisting>
+ <para>
+ If a mime type is listed multiple times (either in the same
+ file, or in another file further down the search path), the
+ latter mention wins. If a listed file doesn't exist, or is
+ precluded through the <varname>OnlyShowIn</varname> or
+ <varname>NotShowIn</varname> keys, they should be ignored.
+ This means that applications will have to keep a history of the
+ preferred applications that they run into, so that if the top
+ desktop file for a given MIME Type isn't available, the second
+ one can be tested, etc.
+ </para>
+ <para>
+ It is also worth noting who this mechanism is defined for. It
+ is primarily intended for use by distributors/sysadmins to
+ provide a sane set of defaults for their users. Additionally,
+ users themselves can use this mechanism to override the user
+ defaults. We intentionally don't provide a way for application
+ authors themselves to list themselves as the default for a given
+ type, as we felt that that cannot work.
+ </para>
+ </sect2>
+-->
+ </sect1>
+ <sect1 id="extending">
+ <title>Extending the format</title>
+ <para>
+ If the standard is to be amended with a new <literal>{key,value}</literal> pair which
+ should be applicable to all supporting parties, a group discussion
+ will take place. This is the preferred method for introducing
+ changes. If one particular party wishes to add a field for personal
+ use, they should prefix the key with the string <varname>X-<replaceable>PRODUCT</replaceable></varname>,
+ e.g. <varname>X-NewDesktop-Foo</varname>, following the precedent set by other IETF and RFC
+ standards.
+ </para>
+ <para>
+ Alternatively, fields can be placed in their own group, where they may
+ then have arbitrary key names. If this is the case, the group should
+ follow the scheme outlined above,
+ i.e. <literal>[X-<replaceable>PRODUCT</replaceable>
+ <replaceable>GROUPNAME</replaceable>]</literal> or
+ something similar. These steps will avoid namespace clashes between
+ different yet similar environments.
+ </para>
+ </sect1>
+ <appendix id="example">
+ <title>Example Desktop Entry File</title>
+ <programlisting>
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=Foo Viewer
+Comment=The best viewer for Foo objects available!
+TryExec=fooview
+Exec=fooview %F
+Icon=fooview
+MimeType=image/x-foo;
+X-KDE-Library=libfooview
+X-KDE-FactoryName=fooviewfactory
+X-KDE-ServiceType=FooService</programlisting>
+ </appendix>
+ <appendix id="kde-items">
+ <title>Currently reserved for use within KDE</title>
+ <para>
+ For historical reasons KDE is using some KDE-specific extensions
+ that are currently not prefixed by a <literal>X-KDE-</literal> prefix.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ KDE specific keys: <varname>ServiceTypes</varname>,
+ <varname>DocPath</varname>, <varname>Keywords</varname>,
+ <varname>InitialPreference</varname>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ KDE specific types: <constant>ServiceType</constant>,
+ <constant>Service</constant> and <constant>FSDevice</constant>
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ KDE uses the following additional keys for desktop entries of the
+ <constant>FSDevice</constant> type.
+ </para>
+ <table>
+ <title>FSDevice Specific Keys</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Key</entry>
+ <entry>Description</entry>
+ <entry>Value Type</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry id="key-dev"><varname>Dev</varname></entry>
+ <entry>
+ The device to mount.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-fstype"><varname>FSType</varname></entry>
+ <entry>
+ The type of file system to try to mount.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-mountpoint"><varname>MountPoint</varname></entry>
+ <entry>
+ The mount point of the device in question.
+ </entry>
+ <entry>string</entry>
+ </row>
+ <row>
+ <entry id="key-readonly"><varname>ReadOnly</varname></entry>
+ <entry>
+ Specifies whether or not the device is read only.
+ </entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry id="key-unmounticon"><varname>UnmountIcon</varname></entry>
+ <entry>
+ Icon to display when device is not mounted. Mounted devices display icon from the <varname>Icon</varname> key.
+ </entry>
+ <entry>localestring</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </appendix>
+ <appendix id="deprecated-items">
+ <title>Deprecated Items</title>
+ <para>
+ As this standard is quite old there are some deprecated items that
+ may or may not be used by several implementations.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>Type=MimeType</literal> is deprecated as there is a
+ new standard for this now, see the <ulink
+ url="http://www.freedesktop.org/Standards/shared-mime-info-spec">Shared
+ MIME-info Database specification</ulink> for more
+ information. In consequence the Keys
+ <varname>Patterns</varname> (various file name extensions
+ associated with the MIME type) and
+ <varname>DefaultApp</varname> (the default application
+ associated with this MIME type) are also deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Using <filename>.kdelnk</filename> instead of
+ <filename>.desktop</filename> as the file extension is
+ deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Using <literal>[KDE Desktop Entry]</literal> instead of
+ <literal>[Desktop Entry]</literal> as header is deprecated.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>Encoding</literal> key is deprecated. It was used to
+ specify whether keys of type <literal>localestring</literal> were
+ encoded in UTF-8 or in the specified locale. Possible values are
+ <literal>UTF-8</literal> and <literal>Legacy-Mixed</literal>. See
+ <xref linkend="legacy-mixed"/> for more details.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Deprecated <varname>Exec</varname> field codes:
+ <literal>%m</literal> (the mini-icon associated with the
+ desktop entry, this should be expanded as two arguments,
+ <literal>--miniicon</literal> and the content of the
+ <varname>MiniIcon</varname> key, it can also be ignored by
+ expanding it to no arguments), %v (the device as listed
+ in the <varname>Dev</varname> key in the desktop file),
+ %d (the directory of a file), %D (the directories of
+ files), %n (the base name of a file) and %N (the base names
+ of files).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Deprecated keys: <varname>MiniIcon</varname> (small icon for
+ menus, etc.), <varname>TerminalOptions</varname> (if the
+ program runs in a terminal, any options that should be
+ passed to the terminal emulator before actually executing
+ the program), <varname>Protocols</varname>,
+ <varname>Extensions</varname>,
+ <varname>BinaryPattern</varname>,
+ <varname>MapNotify</varname>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>SwallowTitle</literal> and
+ <literal>SwallowExec</literal> keys are deprecated.
+ The <literal>SwallowTitle</literal> key is of type
+ <literal>localestring</literal> and specifies the title of the window
+ if is swallowed onto the panel. The <literal>SwallowExec</literal>
+ key is of type <literal>string</literal> and specifies the
+ program to exec if swallowed app is clicked.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>SortOrder</literal> key is deprecated. It is of type
+ <literal>string(s)</literal> and may be used to specify the order in
+ which to display files. The <ulink
+ url="http://www.freedesktop.org/Standards/menu-spec">Desktop
+ Menu Specification</ulink> defines another mechanism for defining the
+ order of menu items.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>FilePattern</literal> key is deprecated.
+ The value is a list of regular
+ expressions to match against for a file manager to determine if this
+ entry's icon should be displayed. Usually simply the name of the main
+ executable and friends.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Historically some booleans have been represented by the numeric
+ entries <constant>0</constant> or <constant>1</constant>. With
+ this version of the standard they are now to be represented as a
+ boolean string. However, if an implementation is reading a pre-1.0
+ desktop entry, it should interpret <constant>0</constant> and
+ <constant>1</constant> as <constant>false</constant> and
+ <constant>true</constant>, respectively.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Historically lists have been comma separated. This is inconsistent with other lists which are separated by a semicolon. When reading a pre-1.0 desktop entry, comma separated lists should continue to be supported.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </appendix>
+ <appendix id="legacy-mixed">
+ <title>The <constant>Legacy-Mixed</constant> Encoding (Deprecated)</title>
+ <para>
+ The <constant>Legacy-Mixed</constant> encoding corresponds to the
+ traditional encoding of desktop files in older versions of the GNOME and
+ KDE desktop files. In this encoding, the encoding of each
+ <literal>localestring</literal> key is determined by the locale tag for
+ that key, if any, instead of being UTF-8. For keys without a locale tag,
+ the value must contain only ASCII characters.
+ </para>
+ <para>
+ If the file specifies an unsupported encoding, the implementation
+ should either ignore the file, or, if the user has requested a direct
+ operation on the file (such as opening it for editing), display an
+ appropriate error indication to the user.
+ </para>
+ <para>
+ In the absence of an <varname>Encoding</varname> key, the implementation may choose
+ to autodetect the encoding of the file by using such factors
+ as:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ The location of the file on the file system
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Whether the contents of the file are valid UTF-8
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If the implementation does not perform such auto-detection, it should
+ treat a file without an <varname>Encoding</varname> key in the same way as a file with an
+ unsupported <varname>Encoding</varname> key.
+ </para>
+ <para>
+ If the locale tag includes an <literal>.<replaceable>ENCODING</replaceable></literal> part, then that determines
+ the encoding for the line. Otherwise, the encoding is determined
+ by the language, or
+ <literal><replaceable>lang</replaceable>_<replaceable>COUNTRY</replaceable></literal>
+ pair from the locale tag, according to the following table.
+ </para>
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Encoding</entry>
+ <entry>Aliases</entry>
+ <entry>Tags</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>ARMSCII-8 (*)</entry><entry></entry><entry>hy</entry>
+ </row><row>
+ <entry>BIG5</entry><entry></entry><entry>zh_TW</entry>
+ </row><row>
+ <entry>CP1251</entry><entry></entry><entry>be bg</entry>
+ </row><row>
+ <entry>EUC-CN</entry><entry>GB2312</entry><entry>zh_CN</entry>
+ </row><row>
+ <entry>EUC-JP</entry><entry></entry><entry>ja</entry>
+ </row><row>
+ <entry>EUC-KR</entry><entry></entry><entry>ko</entry>
+ </row><row>
+ <entry>GEORGIAN-ACADEMY (*)</entry><entry></entry><entry></entry>
+ </row><row>
+ <entry>GEORGIAN-PS (*)</entry><entry></entry><entry>ka</entry>
+ </row><row>
+ <entry>ISO-8859-1</entry><entry></entry><entry>br ca da de en es eu fi fr gl it nl no pt sv wa</entry>
+ </row><row>
+ <entry>ISO-8859-2</entry><entry></entry><entry>cs hr hu pl ro sk sl sq sr</entry>
+ </row><row>
+ <entry>ISO-8859-3 </entry><entry></entry><entry>eo</entry>
+ </row><row>
+ <entry>ISO-8859-5</entry><entry></entry><entry>mk sp</entry>
+ </row><row>
+ <entry>ISO-8859-7</entry><entry></entry><entry>el</entry>
+ </row><row>
+ <entry>ISO-8859-9</entry><entry></entry><entry>tr</entry>
+ </row><row>
+ <entry>ISO-8859-13</entry><entry></entry><entry>lt lv mi</entry>
+ </row><row>
+ <entry>ISO-8859-14</entry><entry></entry><entry>cy ga</entry>
+ </row><row>
+ <entry>ISO-8859-15</entry><entry></entry><entry>et</entry>
+ </row><row>
+ <entry>KOI8-R</entry><entry></entry><entry>ru</entry>
+ </row><row>
+ <entry>KOI8-U</entry><entry></entry><entry>uk</entry>
+ </row><row>
+ <entry>TCVN-5712 (*)</entry><entry>TCVN</entry><entry>vi</entry>
+ </row><row>
+ <entry>TIS-620</entry><entry></entry><entry>th</entry>
+ </row><row>
+ <entry>VISCII</entry><entry></entry><entry></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ <variablelist>
+ <varlistentry>
+ <term>Encoding</term>
+ <listitem>
+ <para>
+ The name given here is listed here is typically the
+ canonical name for the encoding in the GNU C Library's
+ <function>iconv</function> facility. Encodings marked with (*) are not
+ currently supported by the GNU C Library; for this reason,
+ implementations may choose to ignore lines in desktop
+ files that resolve to this encoding. Desktop files with
+ these encodings are currently rare or non-existent.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Aliases</term>
+ <listitem>
+ <para>
+ Other names for the encoding found in existing desktop
+ files.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Tags</term>
+ <listitem>
+ <para>
+ Language tags for which this is the default encoding.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ This table above covers all tags and encodings that are known to
+ be currently in use. Implementors may choose to support
+ encodings not in the above set. For tags without defaults listed
+ in the above table, desktop file creators must specify the
+ <literal>.<replaceable>ENCODING</replaceable></literal> part of the locale tag.
+ </para>
+ <para>
+ Matching the <literal>.<replaceable>ENCODING</replaceable></literal> part of the locale tag against a locale
+ name or alias should be done by stripping all punctuation
+ characters from both the tag and the name or alias, converting
+ both name and alias to lowercase, and comparing the result.
+ This is necessary because, for example, <literal>Big5</literal> is frequently
+ found instead of <literal>BIG5</literal> and <literal>georgianacademy</literal> instead of
+ <literal>GEORGIAN-ACADEMY</literal>. Desktop files creators should, however, use
+ the name as it appears in the "Encoding" column above.
+ </para>
+ </appendix>
+</article>
diff --git a/help-system/help-system-spec.xml b/help-system/help-system-spec.xml
new file mode 100644
index 0000000..ffe1a5d
--- /dev/null
+++ b/help-system/help-system-spec.xml
@@ -0,0 +1,195 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<article id="index">
+ <title>Help System Specification</title>
+
+ <articleinfo>
+ <title>Help System Specification</title>
+ <date>2010-04-30</date>
+ <authorgroup>
+ <author>
+ <firstname>Shaun</firstname>
+ <surname>McCance</surname>
+ <affiliation>
+ <address>
+ <email>shaunm@gnome.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <section id="overview">
+ <title>Overview</title>
+ <para>This specification provides a common directory layout and URI
+ scheme for locally installed help documents. Documents installed and
+ referenced using this system will have better interoperability between
+ different desktop environments and help applications.</para>
+ </section>
+
+ <section id="layout">
+ <title>Directory Layout</title>
+
+ <para>A document is a collection of files, possibly from multiple
+ directories in a path. Documents may be in any format, or even in
+ multiple formats; see <xref linkend="formats"/> for details. A
+ document has a <emphasis>document identifier</emphasis> that
+ identifies it uniquely and specifies where it can be found on
+ the file system. A document identifier consists of one or more
+ characters from the following: digits (U+0030-U+0039), letters
+ (U+0041-U+005A and U+0061-U+007A), hyphen (U+002D), underscore
+ (U+005F), period (U+002E), and percent (U+0025).</para>
+
+ <para>Document identifiers are not explicitly namespaced. To
+ avoid conflicts, document identifiers should start with the name
+ of the application or package that provides the document. In many
+ cases, the name of the application or package alone may be used.
+ Otherwise, the document identifier should start with the name
+ of the package or application followed by a hyphen.</para>
+
+ <para>Documents are installed under the <filename>help</filename>
+ subdirectory in <envar>$XDG_DATA_HOME</envar> or in the
+ <envar>$XDG_DATA_DIRS</envar> path. See the
+ <ulink url="http://standards.freedesktop.org/basedir-spec/latest/">XDG
+ Base Directory Specification</ulink> for details on <envar>$XDG_DATA_HOME</envar>
+ and <envar>$XDG_DATA_DIRS</envar>. Each <filename>help</filename>
+ directory in the path contains subdirectories for languages. Each
+ language subdirectory contains subdirectories for documents, where
+ the name of each subdirectory matches the document identifier of
+ a document.</para>
+
+ <para>The <emphasis>document path</emphasis> for a given document
+ is the list of directories of the following form:</para>
+
+ <programlisting><replaceable>datadir</replaceable>/help/<replaceable>lang</replaceable>/<replaceable>document</replaceable></programlisting>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename><replaceable>datadir</replaceable></filename></term>
+ <listitem><para>Either <envar>$XDG_DATA_HOME</envar> or a directory in
+ the <envar>$XDG_DATA_DIRS</envar> path.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename><replaceable>lang</replaceable></filename></term>
+ <listitem><para>The language code of a language in the user's
+ list of preferred languages.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><filename><replaceable>document</replaceable></filename></term>
+ <listitem><para>The document identifier of the document.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The document path is ordered first according to the position of
+ <filename><replaceable>datadir</replaceable></filename> in the path, then
+ by the position of <filename><replaceable>lang</replaceable></filename>
+ in the user's preferred list of languages. So, for example, if the
+ user's preferred language list is <systemitem>pt_BT:pt:C</systemitem>,
+ then <filename>~/.local/share/help/pt/beanstalk</filename> would take
+ precedence over <filename>/usr/share/help/pt_BR/beanstalk</filename>.</para>
+ </section>
+
+ <section id="uri-scheme">
+ <title>URI Scheme</title>
+
+ <para>Documents are referenced using the <systemitem>help:</systemitem>
+ URI scheme. The <systemitem>help:</systemitem> URI scheme has the
+ following form:</para>
+
+ <programlisting>help:<replaceable>document</replaceable>/<replaceable>page</replaceable>?<replaceable>options</replaceable>#<replaceable>anchor</replaceable></programlisting>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable>document</replaceable></term>
+ <listitem><para>The document identifier.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>page</replaceable></term>
+ <listitem><para>An identifier for a page within the document. Documents
+ often consist of multiple logical pages, which may not be reflected in
+ the actual files on the system. The page identifier is optional. If it
+ is not present, the preceding <systemitem>/</systemitem> character
+ should be omitted.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>options</replaceable></term>
+ <listitem><para>Additional options that can influence how applications
+ present a document. Options are optional. If they are not present, the
+ preceding <systemitem>?</systemitem> character should be omitted. If
+ they are present, they must conform to the
+ <systemitem>application/x-www-form-urlencoded</systemitem> MIME type.
+ Options may be used, for example, to override language settings or to
+ provide keys for conditional processing. This specification makes no
+ specific recommendations for the options.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><replaceable>anchor</replaceable></term>
+ <listitem><para>An anchor point within a page. Applications should
+ scroll to an appropriate point whenever possible. The anchor is
+ optional. If it is not present, the preceding <systemitem>#</systemitem>
+ character should be omitted.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Page identifiers and anchors may contain any character that is
+ valid in a document identifier. Some document types may have further
+ restrictions on page identifiers or anchors.</para>
+ </section>
+
+ <section id="formats">
+ <title>Help Formats</title>
+
+ <para>Documents may be installed in any format. Not all help applications
+ may recognize and handle the same types of documents. Whenever possible,
+ help applications should support HTML. Documents may be installed in
+ multiple formats. Help applications choose which format to use when
+ multiple formats are present.</para>
+
+ <para>For any format, a help application must map the information found
+ in the <systemitem>help:</systemitem> URI scheme to the information in
+ that format. This specification contains recommendations for finding
+ and handling documents in DocBook, Mallard, HTML, and XHTML.</para>
+
+ <section id="docbook">
+ <title>DocBook</title>
+ <para>DocBook documents have a file named <filename>index.docbook</filename>
+ in the document path. Any files referenced from the DocBook file, including
+ XML fragments included through XInclude, should be looked up according to
+ the document path.</para>
+ <para>When a help application displays a DocBook document, it will often
+ display it in multiple pages or chunks, rather than as a single long page.
+ The page identifier specifies which chunk to display. Not all applications
+ will split a DocBook document into chunks in the same way, so the page
+ identifier may not map exactly to the <sgmltag>id</sgmltag> attribute
+ of an element that is chunked. In this case, the applicaiton displays
+ the nearest enclosing chunk and treats the page identifier as an anchor
+ if no anchor was explicitly provided.</para>
+ </section>
+
+ <section id="mallard">
+ <title>Mallard</title>
+ <para>Mallard documents have a file named <filename>index.page</filename>
+ in the document path. Other page files may be different directories in
+ the document path. Any files referenced in any page file, including XML
+ fragments included through XInclude, should be looked up according to
+ the document path.</para>
+ <para>The page identifier specifies the id of a Mallard page file. The
+ anchor specifies the id of a section within a Mallard page file.</para>
+ </section>
+
+ <section id="html">
+ <title>HTML and XHTML</title>
+ <para>HTML documents have a file named <filename>index.html</filename>
+ in the document path. XHTML documents have a file named <filename>index.xhtml</filename>
+ in the document path. Other HTML or XHTML files in the document may
+ be in different directories in the document path. Any files referenced
+ in any HTML or XHTML page, including XML fragments included through
+ XInclude in XHTML, should be looked up according to the document path.</para>
+ <para>The page identifier specifies the base file name, without the
+ <filename>.html</filename> or <filename>.xhtml</filename> extension,
+ of an HTML or XHTML file in the document. The anchor specifies a named
+ anchor within the HTML or XHTML file.</para>
+ </section>
+ </section>
+</article>
diff --git a/menu/.cvsignore b/menu/.cvsignore
new file mode 100644
index 0000000..f90eaaf
--- /dev/null
+++ b/menu/.cvsignore
@@ -0,0 +1,11 @@
+config.log
+config.status
+config.sub
+configure
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h
+config.h.in
diff --git a/menu/AUTHORS b/menu/AUTHORS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/menu/AUTHORS
diff --git a/menu/COPYING b/menu/COPYING
new file mode 100644
index 0000000..73e7995
--- /dev/null
+++ b/menu/COPYING
@@ -0,0 +1,2 @@
+FIXME - we need a license for specs
+
diff --git a/menu/ChangeLog b/menu/ChangeLog
new file mode 100644
index 0000000..025e9e1
--- /dev/null
+++ b/menu/ChangeLog
@@ -0,0 +1,143 @@
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: bump version to 1.1-draft
+
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: add LXDE as registered desktop environment for
+ OnlyShowIn.
+ See http://lists.freedesktop.org/archives/xdg/2008-December/010103.html
+
+2007-08-18 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: accept X-Foo for environments in
+ OnlyShowIn/NotShowIn.
+ Fix bug #11564.
+
+2007-07-27 Vincent Untz <vuntz@gnome.org>
+
+ * menu-spec.xml: change sysconfdir/desktop/menus/ to sysconfdir/menus/
+ in one place.
+ Fix bug #8289.
+
+2007-02-06 Waldo Bastian <waldo.bastian@intel.com>
+ * Added scondary menu categories
+ * Bumped version to 1.0
+
+2007-01-18 Waldo Bastian <waldo.bastian@intel.com>
+
+ * Added support for LSB's tet testing framework
+
+ * Renamed test to more meaningful names:
+ 1 -> Category
+ 2 -> OnlyUnallocated
+ 3 -> Deleted
+ 4 -> Move
+ 5 -> All
+ 6 -> Filename
+ 7 -> And
+ 8 -> Or
+ 9 -> Exclude
+ a -> DesktopFileID
+ b -> menu-multiple-matching
+ c -> Directory
+ d -> submenu-collision
+ e -> LegacyDir-relative
+ f -> Move-collapsing
+ g -> AppDir-relative
+ h -> DirectoryDir-relative
+ i -> DefaultMergeDirs
+ j -> MergeDir-relative
+ k -> MergeFile-relative
+ l -> MergeFile2
+ m -> NoDisplay
+ n -> NoDisplay2
+ o -> boolean-logic
+ p -> MergeFile3
+ q -> MergeFile-parent
+ r -> MergeFile-path
+ s -> desktop-name-collision
+ t -> Move-ordering
+ u -> MergeFile-recursive
+ v -> NotOnlyUnallocated-default
+ w -> LegacyDir-Move
+ x -> Move-submenu
+ y -> Merge-combined
+
+ * Added the following new tests:
+ AppDir
+ DirectoryDir
+ MergeDir-absolute
+ MergeFile-absolute
+ official-categories
+
+2006-03-28 Waldo Bastian <waldo.bastian@intel.com>
+
+ * menu-spec.xml: Added example for installing sub-menu.
+
+2006-03-21 Waldo Bastian <waldo.bastian@intel.com>
+
+ * menu-spec.xml: Recommend to use /usr/share for datadir
+
+2005-03-30 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Change semantics and remove restrictions on <Move>
+ element to facilitate menu editing.
+
+2005-03-23 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Clearify DTD conformance and version compatibility
+
+2005-03-22 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml, menu.dtd: Added type attribute to <MergeFile>
+ element to facilitate menu-editing.
+
+2005-02-24 Waldo Bastian <bastian@kde.org>
+
+ * menu-spec.xml: Fixed links to www.freedesktop.org
+
+2004-12-13 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: elaboration from Waldo.
+
+2004-12-08 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: make <OnlyUnallocated/> be for entries
+ which don't match any <Include> rule rather than entries
+ which don't appear in any menu.
+
+2004-09-03 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: minor typo - "This specification adds two
+ new fields to desktop entries" - s/two/three/
+
+2004-05-07 Mark McLoughlin <mark@skynet.ie>
+
+ * menu-spec.xml: fix typo in <Menuname> description. Its
+ inline_header rather than inline_title according to the DTD.
+
+2004-04-18 Mark McLoughlin <mark@skynet.ie>
+
+ Patch from Ville Skyttä <ville.skytta@iki.fi>
+
+ * menu-spec.xml: editorial changes and typo fixes.
+
+2003-12-12 Waldo Bastian <bastian@kde.org>
+ * menu-spec.xml: Correct example in "Legacy Menu Hierarchies"
+
+2003-11-09 Heinrich Wendel <h_wendel@cojobo.net>
+ * menu-spec.xml: patch for categories based on Suse 9.0
+ * menu-spec.xml: Grahpics -> Graphics
+ * menu-spec.xml: DTD fix
+ * menu.dtd: added dtd
+ * testsuite: fixed errors highlighted by havoc
+
+2003-10-23 Havoc Pennington <hp@redhat.com>
+
+ * menu-spec.xml: clarify how <Move> elements are resolved
+
+2003-10-21 Havoc Pennington <hp@redhat.com>
+
+ * create autotools setup
+
diff --git a/menu/Makefile.am b/menu/Makefile.am
new file mode 100644
index 0000000..92448da
--- /dev/null
+++ b/menu/Makefile.am
@@ -0,0 +1,16 @@
+HTML_FILES= menu-spec.html
+
+XML_FILES= menu-spec.xml
+
+EXTRA_DIST= $(HTML_FILES) $(XML_FILES)
+
+if XML_DOCS_ENABLED
+all-local: $(HTML_FILES)
+endif
+
+%.html: %.xml
+ $(XMLTO) html-nochunks $<
+
+maintainer-clean-local:
+ rm -f $(HTML_FILES)
+
diff --git a/menu/NEWS b/menu/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/menu/NEWS
diff --git a/menu/README b/menu/README
new file mode 100644
index 0000000..0edf632
--- /dev/null
+++ b/menu/README
@@ -0,0 +1 @@
+This is the specification and test suite for the menu system.
diff --git a/menu/autogen.sh b/menu/autogen.sh
new file mode 100755
index 0000000..b1376df
--- /dev/null
+++ b/menu/autogen.sh
@@ -0,0 +1,3 @@
+#! /bin/sh
+autoreconf -v --install || exit 1
+./configure --enable-maintainer-mode "$@"
diff --git a/menu/configure.in b/menu/configure.in
new file mode 100644
index 0000000..7a5a76c
--- /dev/null
+++ b/menu/configure.in
@@ -0,0 +1,61 @@
+dnl -*- mode: m4 -*-
+AC_PREREQ(2.52)
+
+AC_INIT(menu-spec.xml)
+
+AM_INIT_AUTOMAKE(desktop-menu-spec, 0.7)
+
+# Honor aclocal flags
+ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+ ## must come before we use the $USE_MAINTAINER_MODE variable later
+AM_MAINTAINER_MODE
+
+AC_ARG_ENABLE(xml-docs, [ --enable-xml-docs build XML into HTML (requires xmlto)],enable_xml_docs=$enableval,enable_xml_docs=auto)
+
+### XML tools
+
+AC_PATH_PROG(XMLTO, xmlto, no)
+
+AC_MSG_CHECKING([whether to build XML documentation])
+
+if test x$XMLTO = xno ; then
+ have_xmlto=no
+else
+ have_xmlto=yes
+fi
+
+if test x$enable_xml_docs = xauto ; then
+ if test x$have_xmlto = xno ; then
+ enable_xml_docs=no
+ else
+ enable_xml_docs=yes
+ fi
+fi
+
+if test x$enable_xml_docs = xyes; then
+ if test x$have_xmlto = xno; then
+ AC_MSG_ERROR([Building XML docs explicitly required, but xmlto not found])
+ fi
+fi
+
+AM_CONDITIONAL(XML_DOCS_ENABLED, test x$enable_xml_docs = xyes)
+AC_MSG_RESULT(yes)
+
+AC_OUTPUT([
+Makefile
+])
+
+dnl ==========================================================================
+echo "
+ Menu specification $VERSION
+ ==============
+
+ prefix: ${prefix}
+ source code location: ${srcdir}
+ xmlto: ${XMLTO}"
+
+echo "
+ Maintainer mode: ${USE_MAINTAINER_MODE}
+ Building XML docs: ${enable_xml_docs}
+"
diff --git a/menu/menu-spec.xml b/menu/menu-spec.xml
new file mode 100644
index 0000000..28c0097
--- /dev/null
+++ b/menu/menu-spec.xml
@@ -0,0 +1,2383 @@
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+ <!ENTITY version "1.1-draft">
+ <!ENTITY dtd-version "1.0">
+ ]>
+
+<article id="index">
+ <articleinfo>
+ <title>Desktop Menu Specification</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <date>31 March 2011</date>
+ <authorgroup>
+ <author>
+ <firstname>Waldo</firstname>
+ <surname>Bastian</surname>
+ <affiliation>
+ <address>
+ <email>waldo.bastian@intel.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Francois</firstname>
+ <surname>Gouget</surname>
+ <affiliation>
+ <address>
+ <email>fgouget@codeweavers.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Alex</firstname>
+ <surname>Graveley</surname>
+ <affiliation>
+ <address>
+ <email>alex@ximian.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>George</firstname>
+ <surname>Lebl</surname>
+ <affiliation>
+ <address>
+ <email>jirka@5z.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Havoc</firstname>
+ <surname>Pennington</surname>
+ <affiliation>
+ <address>
+ <email>hp@pobox.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Heinrich</firstname>
+ <surname>Wendel</surname>
+ <affiliation>
+ <address>
+ <email>h_wendel@cojobo.net</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="introduction">
+ <title>Introduction</title>
+ <para>
+ This document defines how to construct a user-visible hierarchy of
+ applications, typically displayed as a menu. It allows third-party
+ software to add menu items that work for all desktops, and allows system
+ administrators to edit menus in a way that affects all desktops.
+ </para>
+ <para>
+ The basic scheme is very simple. Information about each application (menu
+ item) is stored in a desktop entry (see <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">Desktop
+ Entry Standard</ulink>). Then an XML configuration file defines the
+ hierarchical arrangement (layout) of menu items, and which menu items are
+ actually displayed.
+ </para>
+ <para>
+ Things are complicated somewhat by the need to support legacy desktop
+ entry hierarchies, and the need to allow third parties to extend the menu
+ layout. Both of these issues are addressed by the idea of
+ <firstterm>merging</firstterm> two menu layouts.
+ </para>
+ <para>
+ In addition to a strict definition of the contents of each menu this
+ specification also foresees in a number of layout / presentation hints.
+ This part of the specification is optional, implementations may chose to
+ ignore these hints.
+ </para>
+ </sect1>
+ <sect1 id="paths">
+ <title>File locations</title>
+ <para>
+ Files involved in this specification are located according to the <ulink
+ url="http://www.freedesktop.org/Standards/basedir-spec">"desktop
+ base directory specification"</ulink>.
+ </para>
+ <para>
+ Here are the files defined by this specification:
+ <variablelist>
+ <varlistentry>
+
+<term><varname>$XDG_CONFIG_DIRS</varname>/menus/<varname>${XDG_MENU_PREFIX}</varname>applications.menu</term>
+ <listitem>
+ <para>
+ This file contains the XML definition of the main application
+ menu layout. The first file found in the search path should be
+ used; other files are ignored. This implies that if the user
+ has their own <varname>${XDG_MENU_PREFIX}</varname>applications.menu,
+ it replaces the system wide one.
+ (Though the user's menu may explicitly merge the system wide
+ one.)
+ </para>
+ <para>
+ Systems that offer multiple desktop environments and that want
+ to use distinct menu layouts in the different environments can
+ use differently prefixed .menu files. In this case the
+ <varname>$XDG_MENU_PREFIX</varname> environment variable must
+ be set by the system to reflect the .menu file that is being
+ used.
+ </para>
+ <para>
+ For example if a system contains both the GNOME and the KDE
+ desktop environments it can decide to use
+ gnome-applications.menu as the menu layout in GNOME sessions
+ and kde-applications.menu as the menu layout in KDE sessions.
+ To correctly reflect this, it should set the
+ <varname>$XDG_MENU_PREFIX</varname> environment variable to
+ "gnome-" respectively "kde-".
+ </para>
+ <para>
+ Implementations may chose to use .menu files with other names
+ for tasks or menus other than the main application menu. Such
+ usage is not covered by this specification.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_CONFIG_DIRS</varname>/menus/applications-merged/</term>
+ <listitem>
+ <para>
+ The default merge directories included in the
+ &lt;DefaultMergeDirs&gt; element. By convention, third parties
+ may add new &lt;Menu&gt; files in this location to create their
+ own sub-menus.
+ </para>
+ <para>
+ Note that a system that uses either gnome-applications.menu or
+ kde-applications.menu depending on the desktop environment in
+ use must still use applications-merged as the default merge
+ directory in both cases.
+ </para>
+ <para>
+ Implementations may chose to use .menu files with names other
+ than application.menu for tasks or menus other than the main
+ application menu. In that case the first part of the name of
+ the default merge directory is derived from the name of the
+ .menu file.
+ </para>
+ <para>
+ For example in a system that uses a preferences.menu file to
+ describe an additional menu, the default merge directories
+ included in the &lt;DefaultMergeDirs&gt; element in the
+ preferences.menu file would become
+ <varname>$XDG_CONFIG_DIRS</varname>/menus/preferences-merged/
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_DATA_DIRS</varname>/applications/</term>
+ <listitem>
+ <para>
+ This directory contains a .desktop file for each possible menu
+ item. Each directory in the <varname>$XDG_DATA_DIRS</varname>
+ search path should be used (i.e. desktop entries are collected
+ from all of them, not just the first one that exists). When two
+ desktop entries have the same name, the one appearing earlier in
+ the path is used.
+ </para>
+ <para>
+ The &lt;DefaultAppDirs&gt; element in a menu file indicates that
+ this default list of desktop entry locations should be scanned at
+ that point. If a menu file does not contain
+ &lt;DefaultAppDirs&gt;, then these locations are not scanned.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>$XDG_DATA_DIRS</varname>/desktop-directories/</term>
+ <listitem>
+ <para>
+ This directory contains directory entries which may be associated
+ with folders in the menu layout. Each directory
+ in the search path should be used. Only files ending in
+ .directory are used; other files are ignored.
+ </para>
+ <para>
+ The &lt;DefaultDirectoryDirs&gt; element in a menu file indicates that
+ this default list of directory entry locations should be scanned at
+ that point. If a menu file does not contain
+ &lt;DefaultDirectoryDirs&gt;, then these locations are not scanned.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect1>
+ <sect1 id="desktop-entry-extensions">
+ <title>Extensions to the desktop entry format</title>
+ <para>
+ This specification adds three new fields to <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entries</ulink>: <varname>Categories</varname>,
+ <varname>OnlyShowIn</varname> and <varname>NotShowIn</varname>.
+ </para>
+ <para>
+ The <varname>Categories</varname> field is a list of strings used to
+ classify menu items. For example, applications in the
+ <literal>AudioVideo</literal> category might end up in the
+ "Sound &amp; Video" submenu. <xref linkend="category-registry"/>
+ enumerates the standard categories. Categories not in this document
+ must be prefixed by the string "X-" indicating that they are extensions.
+ Categories are case-sensitive.
+ </para>
+ <para>
+ Desktop entries should list all categories that clearly apply. They should
+ not list categories that only vaguely or possibly apply, because the user
+ will end up seeing the same desktop entry in a half-dozen places. But
+ it's typical that several categories will apply to a given desktop entry.
+ </para>
+ <para>
+ The <varname>OnlyShowIn</varname> field is a list of strings identifying
+ the environments that should display a given menu item. If an
+ <varname>OnlyShowIn</varname> field is present, a given environment should
+ only display the menu item if the string identifying that environment is
+ present. The strings are case-sensitive. <xref
+ linkend="onlyshowin-registry"/> enumerates the strings to use for
+ some common environments.
+ </para>
+ <para>
+ The <varname>NotShowIn</varname> field is a list of strings identifying
+ the environments that should not display a given menu item. If an
+ <varname>NotShowIn</varname> field is present, a given environment should
+ only display the menu item if the string identifying that environment is
+ not present. The strings are case-sensitive. <xref
+ linkend="onlyshowin-registry"/> enumerates the strings to use for
+ some common environments.
+ </para>
+ <para>
+ Environments not in this document must be prefixed by the string "X-"
+ indicating that they are extensions. Environments are case-sensitive.
+ </para>
+ <sect2 id="desktop-entry-extensions-examples">
+ <title>Examples of using <varname>Categories</varname> and <varname>OnlyShowIn</varname></title>
+ <para>
+ A desktop entry for a Qt-based image viewer might contain
+ this <varname>Categories</varname> line:
+ <informalexample>
+ <programlisting>
+ Categories=Qt;Graphics;RasterGraphics;Viewer;
+ </programlisting>
+ </informalexample>
+ </para>
+ <para>
+ A desktop entry for Octave, a command-line mathematics program (which
+ would also have the field <literal>Terminal=true</literal>), might have:
+ <informalexample>
+ <programlisting>
+ Categories=ConsoleOnly;Math;
+ </programlisting>
+ </informalexample>
+ </para>
+ <para>
+ A desktop entry for a GNOME-specific calculator program
+ that should only appear in GNOME might have:
+ <informalexample>
+ <programlisting>
+ Categories=GNOME;Utility;
+ OnlyShowIn=GNOME;
+ </programlisting>
+ </informalexample>
+ Note that the <varname>OnlyShowIn</varname> field is a
+ <emphasis>list</emphasis> and thus ends in a semicolon.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="menu-file-format">
+ <title>Format of menu files</title>
+ <para>
+ Menu files must be well-formed XML files and end in the extension
+ ".menu". They should also conform to the menu file DTD which implies
+ that implementation-specific extensions to the file format are not
+ allowed. Implementations may stop processing if they encounter a menu
+ file which does not comply with the associated DTD. Note that the
+ associated DTD may differ in version from the one defined in this document.
+ </para>
+ <para>
+ When an implementation updates an existing menu file it may need to
+ update the identifier to a newer version of the DTD. Implementations
+ should never update the identifier of an existing menu file to an
+ older version. In order to remain compatible with newer versions,
+ implementations should ignore and preserve any XML elements,
+ attributes and attribute values that it does not know how to handle.
+ </para>
+ <sect2 id="menu-file-doctype">
+ <title>Document Type Declaration</title>
+ <para>
+ Menu files for this version of the specification must use the following
+ namespace, public and system identifiers:
+ <variablelist>
+ <varlistentry>
+ <term>Namespace</term>
+ <listitem>
+ <para>
+ <literal>http://www.freedesktop.org/standards/menu</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Public Identifier for &version;</term>
+ <listitem>
+ <para>
+ <literal>PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>System Identifier for &version;</term>
+ <listitem>
+ <para>
+<literal>http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Here is a sample document type declaration:
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+ </programlisting>
+ </informalexample>
+
+ All menu files MUST include the document type declaration, so that
+ implementations can adapt to different versions of this specification
+ (and so implementations can validate the menu file against
+ the DTD).
+ </para>
+ </sect2>
+ <sect2 id="menu-file-elements">
+ <title>Elements</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>&lt;Menu&gt;</term>
+ <listitem>
+ <para>
+ The root element is &lt;Menu&gt;. Each &lt;Menu&gt; element may
+ contain any number of nested &lt;Menu&gt; elements, indicating submenus.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;AppDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The
+ content of this element is a directory name. Desktop entries
+ in this directory are scanned and added to the pool of entries
+ which can be included in this &lt;Menu&gt; and its submenus.
+ Only files ending in ".desktop" should be used, other files are
+ ignored.
+ </para>
+ <para>
+ Desktop entries in the pool of available entries are identified
+ by their <firstterm>desktop-file id</firstterm> (see <xref
+ linkend="term-desktop-file-id"/>).
+ The desktop-file id of a desktop entry is equal to its filename,
+ with any path components removed.
+ So given a &lt;AppDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>Hello.desktop</filename>
+ </para>
+ <para>
+ If the directory contains sub-directories then these sub-directories
+ should be (recursively) scanned as well. The name of the subdirectory
+ should be added as prefix to the desktop-file id together with a dash character ("-")
+ So given a &lt;AppDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/booz/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>booz-Hello.desktop</filename>
+ A desktop entry <filename>/foo/bar/bo/oz/Hello.desktop</filename> would result
+ in a desktop-file id of <filename>bo-oz-Hello.desktop</filename>
+ </para>
+ <para>
+ &lt;AppDir&gt; elements appearing later in the menu file have
+ priority in case of collisions between desktop-file ids.
+ </para>
+ <para>
+ If the filename given as an &lt;AppDir&gt; is not an absolute
+ path, it should be located relative to the location of the menu
+ file being parsed.
+ </para>
+ <para>
+ Duplicate &lt;AppDir&gt; elements (that specify the same
+ directory) should be ignored, but the <emphasis>last</emphasis>
+ duplicate in the file should be used when establishing the order
+ in which to scan the directories. This is important when merging
+ (see <xref linkend="merge-algorithm"/>). The order of
+ &lt;AppDir&gt; elements with respect to &lt;Include&gt; and
+ &lt;Exclude&gt; elements is not relevant, also to facilitate
+ merging.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultAppDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;AppDir&gt; elements containing the default app dir
+ locations
+ (<replaceable>datadir</replaceable>/applications/ etc.). When expanding
+ &lt;DefaultAppDirs&gt; to a list of &lt;AppDir&gt;, the default
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DirectoryDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The content of
+ this element is a directory name. Each directory listed in a
+ &lt;DirectoryDir&gt; element will be searched for directory
+ entries to be used when resolving the &lt;Directory&gt; element
+ for this menu and its submenus.
+ If the filename given as a &lt;DirectoryDir&gt; is not an absolute path,
+ it should be located relative to the location
+ of the menu file being parsed.
+ </para>
+ <para>
+ Directory entries in the pool of available entries are identified
+ by their <firstterm>relative path</firstterm> (see <xref
+ linkend="term-relative-path"/>).
+ </para>
+ <para>
+ If two directory entries have duplicate relative paths, the one from
+ the last (furthest down) element in the menu file must be used.
+ Only files ending in the extension ".directory" should be
+ loaded, other files should be ignored.
+ </para>
+ <para>
+ Duplicate &lt;DirectoryDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultDirectoryDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;DirectoryDir&gt; elements containing the default desktop dir
+ locations
+ (<replaceable>datadir</replaceable>/desktop-directories/ etc.). The default
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Name&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; element must have a single &lt;Name&gt;
+ element. The content of the &lt;Name&gt; element is a name to
+ be used when referring to the given menu. Each submenu of a
+ given &lt;Menu&gt; must have a unique name. &lt;Menu&gt;
+ elements can thus be referenced by a menu path, for example
+ "Applications/Graphics." The &lt;Name&gt; field must not contain
+ the slash character ("/"); implementations should discard
+ any name containing a slash. See also <xref linkend="term-menu-path"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Directory&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; element has any number of &lt;Directory&gt;
+ elements. The content of the &lt;Directory&gt; element
+ is the relative path of a directory entry containing meta information
+ about the &lt;Menu&gt;, such as its icon and localized name.
+ If no &lt;Directory&gt; is specified for a &lt;Menu&gt;,
+ its &lt;Name&gt; field should be used as the user-visible
+ name of the menu.
+ </para>
+ <para>
+ Duplicate &lt;Directory&gt; elements are allowed in order
+ to simplify menu merging, and allow user menus to override
+ system menus. The last &lt;Directory&gt; element to appear
+ in the menu file "wins" and other elements are ignored,
+ unless the last element points to a nonexistent directory
+ entry, in which case the previous element should be tried instead,
+ and so on.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;OnlyUnallocated&gt; and &lt;NotOnlyUnallocated&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; may contain any number of
+ &lt;OnlyUnallocated&gt; and &lt;NotOnlyUnallocated&gt;
+ elements. Only the last such element to appear is relevant, as
+ it determines whether the &lt;Menu&gt; can contain any desktop
+ entries, or only those desktop entries that do not match other
+ menus. If neither &lt;OnlyUnallocated&gt; nor
+ &lt;NotOnlyUnallocated&gt; elements are present, the default
+ is &lt;NotOnlyUnallocated&gt;.
+ </para>
+ <para>
+ To handle &lt;OnlyUnallocated&gt;, the menu file must be
+ analyzed in two conceptual passes. The first pass processes
+ &lt;Menu&gt; elements that can match any desktop entry. During
+ this pass, each desktop entry is marked as allocated according
+ to whether it was matched by an &lt;Include&gt; rule in some
+ &lt;Menu&gt;. The second pass processes only &lt;Menu&gt;
+ elements that are restricted to unallocated desktop entries.
+ During the second pass, queries may only match desktop entries
+ that were not marked as allocated during the first pass.
+ See <xref linkend="query-algorithm"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Deleted&gt; and &lt;NotDeleted&gt;</term>
+ <listitem>
+ <para>
+ Each &lt;Menu&gt; may contain any number of &lt;Deleted&gt; and
+ &lt;NotDeleted&gt; elements. Only the last such element to
+ appear is relevant, as it determines whether the &lt;Menu&gt;
+ has been deleted. If neither &lt;Deleted&gt; nor
+ &lt;NotDeleted&gt; elements are present, the default is
+ &lt;NotDeleted&gt;. The purpose of this element is to support
+ menu editing. If a menu contains a &lt;Deleted&gt; element
+ not followed by a &lt;NotDeleted&gt; element, that menu
+ should be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Include&gt;</term>
+ <listitem>
+ <para>
+ An &lt;Include&gt; element is a set of rules attempting to match
+ some of the known desktop entries. The &lt;Include&gt; element
+ contains a list of any number of matching rules. Matching rules
+ are specified using the elements &lt;And&gt;, &lt;Or&gt;,
+ &lt;Not&gt;, &lt;All&gt;, &lt;Filename&gt;, and
+ &lt;Category&gt;. Each rule in a list of rules has a logical OR
+ relationship, that is, desktop entries which match any rule
+ are included in the menu.
+ </para>
+ <para>
+ &lt;Include&gt; elements must appear immediately under
+ &lt;Menu&gt; elements. The desktop entries they match are
+ included in the menu. &lt;Include&gt; and &lt;Exclude&gt;
+ elements for a given &lt;Menu&gt; are processed in order,
+ with queries earlier in the file handled first. This has
+ implications for merging, see <xref linkend="merge-algorithm"/>.
+ See <xref linkend="query-algorithm"/> for full details on
+ how to process &lt;Include&gt; and &lt;Exclude&gt; elements.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Exclude&gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;Exclude&gt; elements may appear below a
+ &lt;Menu&gt; element. The content of an &lt;Exclude&gt; element
+ is a list of matching rules, just as with an
+ &lt;Include&gt;. However, the desktop entries matched are
+ removed from the list of desktop entries included so far. (Thus
+ an &lt;Exclude&gt; element that appears before any
+ &lt;Include&gt; elements will have no effect, for example, as no
+ desktop entries have been included yet.)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Filename&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Filename&gt; element is the most basic matching rule.
+ It matches a desktop entry if the desktop entry has the given
+ desktop-file id. See <xref linkend="term-desktop-file-id"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Category&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Category&gt; element is another basic matching
+ predicate. It matches a desktop entry if the desktop entry has
+ the given category in its <varname>Categories</varname> field.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;All&gt;</term>
+ <listitem>
+ <para>
+ The &lt;All&gt; element is a matching rule that matches
+ all desktop entries.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;And&gt;</term>
+ <listitem>
+ <para>
+ The &lt;And&gt; element contains a list of matching rules.
+ If each of the matching rules inside the &lt;And&gt;
+ element match a desktop entry, then the entire
+ &lt;And&gt; rule matches the desktop entry.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Or&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Or&gt; element contains a list of matching rules.
+ If any of the matching rules inside the &lt;Or&gt;
+ element match a desktop entry, then the entire
+ &lt;Or&gt; rule matches the desktop entry.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Not&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Not&gt; element contains a list of matching rules. If
+ any of the matching rules inside the &lt;Not&gt; element matches
+ a desktop entry, then the entire &lt;Not&gt; rule does
+ <emphasis>not</emphasis> match the desktop entry. That is,
+ matching rules below &lt;Not&gt; have a logical OR relationship.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;MergeFile [type="path"|"parent"] &gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;MergeFile&gt; elements may be listed below a
+ &lt;Menu&gt; element, giving the name of another menu file to
+ be merged into this one. <xref linkend="merge-algorithm"/>
+ specifies how merging is done. The root &lt;Menu&gt; of the
+ merged file will be merged into the immediate parent of the
+ &lt;MergeFile&gt; element. The &lt;Name&gt; element of the
+ root &lt;Menu&gt; of the merged file are ignored.
+ </para>
+ <para>
+ If the type attribute is missing or set to "path" then the
+ contents of the &lt;MergeFile&gt; element indicates the
+ file to be merged. If this is not an absolute path then the
+ file to be merged should be located relative to the location
+ of the menu file that contains this &lt;MergeFile&gt; element.
+ </para>
+ <para>
+ Duplicate &lt;MergeFile&gt; elements (that specify the same
+ file) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ <para>
+ If the type attribute is set to "parent" and the file that
+ contains this &lt;MergeFile&gt; element is located under one
+ of the paths specified by <varname>$XDG_CONFIG_DIRS</varname>,
+ the contents of the element should be ignored and the remaining
+ paths specified by <varname>$XDG_CONFIG_DIRS</varname> are
+ searched for a file with the same relative filename. The first
+ file encountered should be merged. There should be no
+ merging at all if no matching file is found.
+ </para>
+ <para>
+ Compatibility note: The filename specified inside the &lt;MergeFile&gt;
+ element should be ignored if the type attribute is set to "parent",
+ it should however be expected that implementations based on
+ previous versions of this specification will ignore the
+ type attribute and that such implementations will use the
+ filename inside the &lt;MergeFile&gt; element instead.
+ </para>
+ <para>
+ Example 1: If <varname>$XDG_CONFIG_HOME</varname> is "~/.config/" and
+ <varname>$XDG_CONFIG_DIRS</varname> is "/opt/gnome/:/etc/xdg/"
+ and the file ~/.config/menus/applications.menu contains
+ &lt;MergeFile type="parent"&gt;/opt/kde3/etc/xdg/menus/applications.menu&lt;/MergeFile&gt;
+ then the file /opt/gnome/menus/applications.menu
+ should be merged if it exists. If that file does not exists
+ then the file /etc/xdg/menus/applications.menu
+ should be merged instead.
+ </para>
+ <para>
+ Example 2: If <varname>$XDG_CONFIG_HOME</varname> is "~/.config/" and
+ <varname>$XDG_CONFIG_DIRS</varname> is "/opt/gnome/:/etc/xdg/"
+ and the file /opt/gnome/menus/applications.menu contains
+ &lt;MergeFile type="parent"&gt;/opt/kde3/etc/xdg/menus/applications.menu&lt;/MergeFile&gt;
+ then the file /etc/xdg/menus/applications.menu should be merged
+ if it exists.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;MergeDir&gt;</term>
+ <listitem>
+ <para>
+ Any number of &lt;MergeDir&gt; elements may be listed below a
+ &lt;Menu&gt; element. A &lt;MergeDir&gt; contains the name of a
+ directory. Each file in the given directory which ends in the
+ ".menu" extension should be merged in the same way that a
+ &lt;MergeFile&gt; would be. If the filename given as a
+ &lt;MergeDir&gt; is not an absolute path, it should be located
+ relative to the location of the menu file being parsed.
+ The files inside the merged directory are not merged in any
+ specified order.
+ </para>
+ <para>
+ Duplicate &lt;MergeDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultMergeDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;MergeDir&gt; elements containing the default merge
+ directory locations. When expanding &lt;DefaultMergeDirs&gt; to a
+ list of &lt;MergeDir&gt;, the default locations that are earlier
+ in the search path go later in the &lt;Menu&gt; so that they
+ have priority.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;LegacyDir&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The text
+ content of this element is a directory name. Each directory
+ listed in a &lt;LegacyDir&gt; element will be an old-style
+ legacy hierarchy of desktop entries, see <xref
+ linkend="legacy-hierarchies"/> for how to load such a
+ hierarchy. Implementations must not load legacy hierarchies that
+ are not explicitly specified in the menu file (because for
+ example the menu file may not be the main menu). If the
+ filename given as a &lt;LegacyDir&gt; is not an absolute path,
+ it should be located relative to the location of the menu file
+ being parsed.
+ </para>
+ <para>
+ Duplicate &lt;LegacyDir&gt; elements (that specify the same
+ directory) are handled as with duplicate &lt;AppDir&gt;
+ elements (the last duplicate is used).
+ </para>
+ <para>
+ The &lt;LegacyDir&gt; element may have one attribute,
+ <literal>prefix</literal>. Normally, given a &lt;LegacyDir&gt;
+ <filename>/foo/bar</filename> and desktop entry
+ <filename>/foo/bar/baz/Hello.desktop</filename> the desktop
+ entry would get a desktop-file id of <filename>Hello.desktop</filename>.
+ Given a prefix of <literal>boo-</literal>, it would instead be
+ assigned the desktop-file id <filename>boo-Hello.desktop</filename>.
+ The prefix should not contain path separator ('/') characters.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;KDELegacyDirs&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The element has
+ no content. The element should be treated as if it were a list
+ of &lt;LegacyDir&gt; elements containing the traditional desktop
+ file locations supported by KDE with a hard coded prefix of "kde-".
+ When expanding &lt;KDELegacyDirs&gt; to a list of &lt;LegacyDir&gt;, the
+ locations that are earlier in the search path go later in the
+ &lt;Menu&gt; so that they have priority.
+ The search path can be obtained by running <filename>kde-config --path apps</filename>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Move&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Menu&gt;. The
+ &lt;Move&gt; element contains pairs of &lt;Old&gt;/&lt;New&gt;
+ elements indicating how to rename a descendant of the current
+ &lt;Menu&gt;. If the destination path already exists, the moved
+ menu is merged with the destination menu (see <xref
+ linkend="merge-algorithm"/> for details).
+ </para>
+ <para>
+ &lt;Move&gt; is used primarily to fix up legacy directories.
+ For example, say you are merging a &lt;LegacyDir&gt; with folder
+ names that don't match the current hierarchy; the legacy folder
+ names can be moved to the new names, where they will be merged
+ with the new folders.
+ </para>
+ <para>
+ &lt;Move&gt; is also useful for implementing menu
+ editing, see <xref linkend="menu-editing"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Old&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Move&gt;, and
+ must be followed by a &lt;New&gt; element. The content of both
+ &lt;Old&gt; and &lt;New&gt; should be a menu path
+ (slash-separated concatenation of &lt;Name&gt; fields, see
+ <xref linkend="term-menu-path"/>).
+ Paths are interpreted relative to the menu containing
+ the &lt;Move&gt; element.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;New&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear below &lt;Move&gt;, and must
+ be preceded by an &lt;Old&gt; element. The &lt;New&gt; element
+ specifies the new path for the preceding &lt;Old&gt; element.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Layout&gt;</term>
+ <listitem>
+ <para>
+ The &lt;Layout&gt; element is an optional part of this specification.
+ Implementations that do not support the &lt;Layout&gt; element should
+ preserve any &lt;Layout&gt; elements and their contents as far as
+ possible.
+ Each &lt;Menu&gt; may optionally contain a &lt;Layout&gt; element.
+ If multiple elements appear then only the last such element is relevant.
+ The purpose of this element is to offer suggestions for the presentation
+ of the menu.
+ If a menu does not contain a &lt;Layout&gt; element or if it contains
+ an empty &lt;Layout&gt; element then the default layout should be used.
+ The &lt;Layout&gt; element may contain &lt;Filename&gt;, &lt;Menuname&gt;,
+ &lt;Separator&gt; and &lt;Merge&gt; elements. The &lt;Layout&gt; element
+ defines a suggested layout for the menu starting from top to bottom.
+ References to desktop entries that are not contained in this
+ menu as defined by the &lt;Include&gt; and &lt;Exclude&gt; elements should
+ be ignored. References to sub-menus that are not directly contained in this
+ menu as defined by the &lt;Menu&gt; elements should be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;DefaultLayout [show_empty="false"] [inline="false"] [inline_limit="4"] [inline_header="true"] [inline_alias="false"]&gt;</term>
+ <listitem>
+ <para>
+ The &lt;DefaultLayout&gt; element is an optional part of this specification.
+ Implementations that do not support the &lt;DefaultLayout&gt; element should
+ preserve any &lt;DefaultLayout&gt; elements and their contents as far as
+ possible.
+ Each &lt;Menu&gt; may optionally contain a &lt;DefaultLayout&gt; element
+ which defines the default-layout for the current menu and all its sub-menus.
+ If a menu has a &lt;DefaultLayout&gt; element then this will override
+ any default-layout specified by a parent menu.
+ The default-layout defines the suggested layout if a &lt;Menu&gt; element
+ does either not have &lt;Layout&gt; element or if it has an empty &lt;Layout&gt; element.
+ For explanations of the various attributes see the &lt;Menuname&gt; element.
+ If no default-layout has been specified then the layout as specified by the following
+ elements should be assumed:
+ &lt;DefaultLayout show_empty="false" inline="false" inline_limit="4" inline_header="true" inline_alias="false"&gt;&lt;Merge type="menus"/&gt;&lt;Merge type="files"/&gt;&lt;/DefaultLayout&gt;
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Menuname [show_empty="..."] [inline="..."] [inline_limit="..."] [inline_header="..."] [inline_alias="..."]&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. Its contents references an immediate sub-menu of the current menu as defined
+ with the &lt;Menu&gt; element, as such it should never contain a slash.
+ If no such sub-menu exists the element should be ignored.
+ This element may have various attributes, the default values are taken from the DefaultLayout key.
+ The show_empty attribute defines whether a menu that contains no desktop
+ entries and no sub-menus should be shown at all. The show_empty attribute
+ can be "true" or "false".
+ It may have an inline attribute that can be either "true" or "false".
+ If the inline attribute is "true" the menu that is referenced may be copied into the
+ current menu at the current point instead of being inserted as sub-menu of the current menu.
+ The optional inline_limit attribute defines the maximum number of entries that can be inlined.
+ If the sub-menu has more entries than inline_limit, the sub-menu will not be inlined.
+ If the inline_limit is 0 (zero) there is no limit.
+ The optional inline_header attribute defines whether an inlined menu should be preceded with
+ a header entry listing the caption of the sub-menu.
+ The inline_header attribute can be either "true" or "false".
+ The optional inline_alias attribute defines whether a single inlined entry should adopt the
+ caption of the inlined menu. In such case no additional header entry will be added regardless
+ of the value of the inline_header attribute.
+ The inline_alias attribute can be either "true" or "false".
+ Example: if a menu has a sub-menu titled "WordProcessor" with a single entry "OpenOffice 4.2",
+ and both inline="true" and inline_alias="true" are specified then this would result in the
+ "OpenOffice 4.2" entry being inlined in the current menu but the "OpenOffice 4.2" caption
+ of the entry would be replaced with "WordProcessor".
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Separator&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. It indicates a suggestion to draw a visual separator at this point in the menu.
+ &lt;Separator&gt; elements at the start of a menu, at the end of a menu or that directly
+ follow other &lt;Separator&gt; elements may be ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>&lt;Merge type="menus"|"files"|"all"/&gt;</term>
+ <listitem>
+ <para>
+ This element may only appear as a child of a &lt;Layout&gt; or &lt;DefaultLayout&gt;
+ menu. It indicates the point where desktop entries and sub-menus that are not explicitly
+ mentioned within the &lt;Layout&gt; or &lt;DefaultLayout&gt; element are to be inserted.
+ It has a type attribute that indicates which elements should be inserted:
+ type="menus"
+ means that all sub-menus that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ type="files"
+ means that all desktop entries
+ contained in this menu that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ type="all" means that a mix of all sub-menus
+ and all desktop entries that are not explicitly mentioned should be inserted in
+ alphabetical order of their visual caption at this point.
+ Each &lt;Layout&gt; or &lt;DefaultLayout&gt; element shall have exactly one &lt;Merge type="all"&gt;
+ element or it shall have exactly one &lt;Merge type="files"&gt; and exactly one
+ &lt;Merge type="menus"&gt; element. An exception is made for a completely empty &lt;Layout&gt;
+ element which may be used to indicate that the default-layout should be used instead.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="merge-algorithm">
+ <title>Merging</title>
+ <para>
+ Sometimes two menu layouts need to be merged. This is done when folding in
+ legacy menu hierarchies (see <xref linkend="legacy-hierarchies"/>) and also
+ for files specified in &lt;MergeFile&gt; elements. A common case is that
+ per-user menu files might merge the system menu file. Merging is also used
+ to avoid cut-and-paste, for example to include a common submenu in
+ multiple menu files.
+ </para>
+ <para>
+ Merging involves a base &lt;Menu&gt; and a merged &lt;Menu&gt;. The base
+ is the "target" menu and the merged &lt;Menu&gt; is being added to it. The
+ result of the merge is termed the "combined menu."
+ </para>
+ <para>
+ As a preparatory step, the goal is to resolve all files into
+ XML elements. To do so, traverse the entire menu tree. For each
+ &lt;MergeFile&gt;, &lt;MergeDir&gt;, or &lt;LegacyDir&gt; element, replace
+ the &lt;MergeFile&gt;, &lt;MergeDir&gt;, or &lt;LegacyDir&gt; element with
+ the child elements of the root &lt;Menu&gt; of the file(s) being
+ merged. As a special exception, remove the &lt;Name&gt; element from the
+ root element of each file being merged. To generate a
+ &lt;Menu&gt; based on a &lt;LegacyDir&gt;, see
+ <xref linkend="legacy-hierarchies"/>.
+ </para>
+ <para>
+ Continue processing until no &lt;MergeFile&gt;, &lt;MergeDir&gt;, or
+ &lt;LegacyDir&gt; elements remain, taking care to avoid infinite loops
+ caused by files that reference one another.
+ </para>
+ <para>
+ Once all files have been loaded into a single tree, scan the tree
+ recursively performing these steps to remove duplicates:
+ <orderedlist>
+ <listitem>
+ <para>
+ Consolidate child menus. Each group of child &lt;Menu&gt;s with the same
+ name must be consolidated into a single child menu with that name.
+ Concatenate the child elements of all menus with the same name, in
+ the order that they appear, and insert those elements as the
+ children of the <emphasis>last</emphasis> menu with that name.
+ Delete all the newly empty &lt;Menu&gt; elements, keeping the
+ last one.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Expand &lt;DefaultAppDirs&gt; and &lt;DefaultDirectoryDirs&gt;
+ elements to &lt;AppDir&gt; and &lt;DirectoryDir&gt; elements.
+ Consolidate duplicate &lt;AppDir&gt;, &lt;DirectoryDir&gt;,
+ and &lt;Directory&gt; elements by keeping the last one.
+ For &lt;Directory&gt; elements that refer to distinct directory
+ entries, all of them should be kept - if the last one points
+ to a nonexistent file, the one before that can be used instead,
+ and so forth.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Recurse into each child &lt;Menu&gt;, performing this list of
+ steps for each child in order.
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+ <para>
+ After recursing once to remove duplicates, recurse a second time to
+ resolve &lt;Move&gt; elements for each menu starting with any child
+ menu before handling the more top level menus.
+ So the deepest menus have their &lt;Move&gt; operations performed first.
+ Within each &lt;Menu&gt;, execute &lt;Move&gt; operations in the order
+ that they appear. If the destination path does not exist, simply relocate
+ the origin &lt;Menu&gt; element, and change its &lt;Name&gt; field to
+ match the destination path. If the origin path does not exist, do
+ nothing. If both paths exist, take the origin &lt;Menu&gt; element,
+ delete its &lt;Name&gt; element, and prepend its remaining child elements
+ to the destination &lt;Menu&gt; element.
+ </para>
+ <para>
+ If any &lt;Move&gt; operations affect a menu, then re-run the
+ steps to resolve duplicates in case any duplicates have been
+ created.
+ </para>
+ <para>
+ Finally, for each &lt;Menu&gt; containing a &lt;Deleted&gt; element which
+ is not followed by a &lt;NotDeleted&gt; element, remove that menu and all
+ its child menus.
+ </para>
+
+ <para>
+ Merged menu elements are kept in order because &lt;Include&gt; and
+ &lt;Exclude&gt; elements later in the file override &lt;Include&gt; and
+ &lt;Exclude&gt; elements earlier in the file. This means that if the user's
+ menu file merges the system menu file, the user can always override what
+ the system menu specifies by placing elements after the &lt;MergeFile&gt;
+ that incorporates the system file.
+ </para>
+ <para>
+ To prevent that a desktop entry from one party inadvertently cancels out
+ the desktop entry from another party because both happen to get the same
+ desktop-file id it is recommended that providers of desktop-files ensure that
+ all desktop-file ids start with a vendor prefix. A vendor prefix
+ consists of [a-zA-Z] and is terminated with a dash ("-"). Open Source
+ projects and commercial parties are encouraged to use a word or phrase,
+ preferably their name, as prefix for which they hold a trademark. Open Source
+ applications can also ask to make use of the vendor prefix of another open
+ source project (such as GNOME or KDE) they consider themselves affiliated
+ with, at the discretion of these projects.
+ </para>
+ <para>
+ For example, to ensure that GNOME applications start with a vendor prefix of "gnome-",
+ it could either add "gnome-" to all the desktop files it installs in
+ <filename><replaceable>datadir</replaceable>/applications/</filename> or it could
+ install desktop files in a <filename><replaceable>datadir</replaceable>/applications/gnome</filename>
+ subdirectory. When including legacy menu hierarchies the <literal>prefix</literal> argument
+ of the &lt;LegacyDir&gt; element can be used to specify a prefix.
+ </para>
+ </sect1>
+
+ <sect1 id="query-algorithm">
+ <title>Generating the menus</title>
+ <para>
+ After merging the menus, the result should be a single menu layout
+ description. For each &lt;Menu&gt;, we have a list of directories where
+ desktop entries can be found, a list of directories where directory
+ entries can be found, and a series of &lt;Include&gt; and &lt;Exclude&gt;
+ directives.
+ </para>
+ <para>
+ For each &lt;Menu&gt; element, build a pool of desktop entries by
+ collecting entries found in each &lt;AppDir&gt; for the menu element. If
+ two entries have the same desktop-file id, the entry for the earlier (closer
+ to the top of the file) &lt;AppDir&gt; must be discarded. Next, add to the
+ pool the entries for any &lt;AppDir&gt;s specified by ancestor
+ &lt;Menu&gt; elements. If a parent menu has a duplicate entry (same
+ desktop-file id), the entry for the child menu has priority.
+ </para>
+ <para>
+ Next, walk through all &lt;Include&gt; and &lt;Exclude&gt; statements.
+ For each &lt;Include&gt;, match the rules against the pool of all desktop
+ entries. For each desktop entry that matches one of the rules,
+ add it to the menu to be displayed and mark it as having been allocated.
+ For each &lt;Exclude&gt;, match the rules against the currently-included
+ desktop entries. For each desktop entry that matches, remove it again
+ from the menu. Note that an entry that is included in a menu but excluded
+ again by a later &lt;Exclude&gt; is still considered allocated (for the
+ purposes of &lt;OnlyUnallocated&gt;) even though that entry no longer
+ appears in the menu.
+ </para>
+ <para>
+ Two passes are necessary, once for regular menus where any entry may
+ be matched, and once for &lt;OnlyUnallocated&gt; menus where only entries
+ which have not been marked as allocated may be matched.
+ </para>
+ <para>
+ The result is a tree of desktop entries, of course.
+ </para>
+ </sect1>
+
+ <sect1 id="legacy-hierarchies">
+ <title>Legacy Menu Hierarchies</title>
+ <para>
+ Traditionally, menus were defined as a filesystem hierarchy, with each
+ filesystem directory corresponding to a submenu. Implementations of this
+ specification must be able to load these old-style hierarchies
+ as specified in this section.
+ </para>
+ <para>
+ The general approach is: the legacy hierarchy is converted into a
+ &lt;Menu&gt;, and then this menu layout is merged with the menu that
+ specified &lt;LegacyDir&gt;.
+ </para>
+ <para>
+ Desktop entries in the legacy hierarchy should be added to the pool of
+ desktop entries as if the &lt;LegacyDir&gt; were an
+ &lt;AppDir&gt;. Directory entries in the legacy hierarchy should be added
+ to the pool of directory entries as if the &lt;LegacyDir&gt; were a
+ &lt;DirectoryDir&gt;. This can be trivially implemented by adding
+ appropriate &lt;AppDir&gt; and &lt;DirectoryDir&gt; statements to the root
+ legacy &lt;Menu&gt;. There is one slight complexity, namely the
+ "prefix" attribute of &lt;LegacyDir&gt;.
+ </para>
+ <para>
+ The menu layout corresponds conceptually to the following, though actually
+ generating the XML is not necessary:
+ <itemizedlist>
+ <listitem>
+ <para>
+ For each directory in the legacy hierarchy, a
+ &lt;Menu&gt; is created with the same &lt;Name&gt;
+ as the directory on disk.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ This menu then contains an &lt;Include&gt; element that includes
+ each desktop entry in the directory. That is, it should have a
+
+ &lt;Filename&gt;<replaceable>Foo/Bar/foo.desktop</replaceable>&lt;/Filename&gt;
+ for each desktop entry in the directory.
+ </para>
+ <para>
+ As a special exception, if a desktop entry in a directory contains
+ a <varname>Categories</varname> field, that desktop entry should
+ <emphasis>not</emphasis> be included in the legacy menu.
+ That is, no &lt;Include&gt; element should be generated for
+ the entry. This allows a desktop entry to be installed
+ in a legacy location but still work optimally with the
+ menu system specified in this document.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If the legacy directory contains a ".directory" file, then
+ a &lt;Directory&gt; element should be generated that points to said
+ ".directory" file.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Legacy desktop entries should not be assigned any
+ <varname>Categories</varname> fields if they didn't have them
+ already, except that all legacy entries should have the
+ "Legacy" category added to allow menu files to treat them
+ specially. (If the same directory is given as both
+ a &lt;LegacyDir&gt; and an &lt;AppDir&gt;, its desktop
+ entries should be labeled "Legacy" only if the &lt;LegacyDir&gt;
+ appears later in the file than the &lt;AppDir&gt;.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ For example, say we have the following legacy directory hierarchy:
+ <informalexample>
+ <programlisting>
+ /usr/share/applnk
+ /usr/share/applnk/.directory
+ /usr/share/applnk/bar.desktop
+ /usr/share/applnk/System
+ /usr/share/applnk/System/.directory
+ /usr/share/applnk/System/foo.desktop
+ </programlisting>
+ </informalexample>
+ Conceptually that is converted to the following &lt;Menu&gt;:
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;AppDir&gt;/usr/share/applnk&lt;/AppDir&gt;
+ &lt;DirectoryDir&gt;/usr/share/applnk&lt;/DirectoryDir&gt;
+ &lt;Directory&gt;.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;bar.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;System&lt;/Name&gt;
+ &lt;AppDir&gt;/usr/share/applnk/System&lt;/AppDir&gt;
+ &lt;DirectoryDir&gt;/usr/share/applnk/System&lt;/DirectoryDir&gt;
+ &lt;Directory&gt;.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;foo.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;/Menu&gt;
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+ This &lt;Menu&gt; is then merged as if it were in a file
+ and loaded with &lt;MergeFile&gt;.
+ </para>
+ </sect1>
+
+ <sect1 id="example">
+ <title>Example Menu File</title>
+ <para>
+ <informalexample>
+ <programlisting>
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;Directory&gt;Applications.directory&lt;/Directory&gt;
+
+ &lt;-- Search the default locations --&gt;
+ &lt;DefaultAppDirs/&gt;
+ &lt;DefaultDirectoryDirs/&gt;
+
+ &lt;-- Merge third-party submenus --&gt;
+ &lt;MergeDir&gt;applications-merged&lt;/MergeDir&gt;
+
+ &lt;-- Merge legacy hierarchy --&gt;
+ &lt;LegacyDir&gt;/usr/share/applnk&lt;/LegacyDir&gt;
+
+ &lt;-- Define default layout --&gt;
+ &lt;DefaultLayout&gt;
+ &lt;Merge type="menus"/&gt;
+ &lt;Merge type="files"/&gt;
+ &lt;Separator/&gt;
+ &lt;Menuname&gt;More&lt;/Menuname&gt;
+ &lt;/DefaultLayout&gt;
+
+ &lt;-- some random moves, maybe to clean up legacy dirs,
+ maybe from menu editing --&gt;
+ &lt;Move&gt;
+ &lt;Old&gt;Foo&lt;/Old&gt;
+ &lt;New&gt;Bar&lt;/New&gt;
+ &lt;Old&gt;Foo2&lt;/Old&gt;
+ &lt;New&gt;Bar2&lt;/New&gt;
+ &lt;/Move&gt;
+
+ &lt;-- A preferences submenu, kept in a separate file
+ so it can also be used standalone --&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Preferences&lt;/Name&gt;
+ &lt;Directory&gt;Preferences.directory&lt;/Directory&gt;
+ &lt;MergeFile&gt;preferences.menu&lt;/MergeFile&gt;
+ &lt;/Menu&gt;
+
+ &lt;-- An Office submenu, specified inline --&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Office&lt;/Name&gt;
+ &lt;Directory&gt;Office.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Category&gt;Office&lt;/Category&gt;
+ &lt;/Include&gt;
+ &lt;Exclude&gt;
+ &lt;Filename&gt;foo.desktop&lt;/Filename&gt;
+ &lt;/Exclude&gt;
+ &lt;/Menu&gt;
+
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+ </para>
+ </sect1>
+
+ <appendix id="category-registry">
+ <title>Registered Categories</title>
+ <para>
+ This section contains a number of well known categories and
+ suggestions on how to use them. The list of Main Categories consist
+ of those categories that every conforming desktop environment MUST
+ support. By including one of these categories in an application's
+ desktop entry file the application will be ensured that it will
+ show up in a section of the application menu dedicated to this
+ category. The list of Additional Categories provides categories
+ that can be used to provide more fine grained information about
+ the application. Additional Categories should always be used in
+ combination with one of the Main Categories.
+ </para>
+ <para>
+ The table below lists all Main Categories.
+ Note that category names are case-sensitive.
+ <informaltable>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Main Category</entry>
+ <entry>Description</entry>
+ <entry>Notes</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>AudioVideo</entry>
+ <entry>Application for presenting, creating, or processing multimedia (audio/video)</entry>
+ </row><row>
+ <entry>Audio</entry>
+ <entry>An audio application</entry>
+ <entry>Desktop entry must include AudioVideo as well</entry>
+ </row><row>
+ <entry>Video</entry>
+ <entry>A video application</entry>
+ <entry>Desktop entry must include AudioVideo as well</entry>
+ </row><row>
+ <entry>Development</entry>
+ <entry>An application for development</entry>
+ </row><row>
+ <entry>Education</entry>
+ <entry>Educational software</entry>
+ </row><row>
+ <entry>Game</entry>
+ <entry>A game</entry>
+ </row><row>
+ <entry>Graphics</entry>
+ <entry>Application for viewing, creating, or processing graphics</entry>
+ </row><row>
+ <entry>Network</entry>
+ <entry>Network application such as a web browser</entry>
+ </row><row>
+ <entry>Office</entry>
+ <entry>An office type application</entry>
+ </row><row>
+ <entry>Settings</entry>
+ <entry>Settings applications</entry>
+ <entry>Entries may appear in a separate menu or as part of a
+ "Control Center"</entry>
+ </row><row>
+ <entry>System</entry>
+ <entry>System application, "System Tools" such as say a log viewer or network monitor</entry>
+ </row><row>
+ <entry>Utility</entry>
+ <entry>Small utility application, "Accessories"</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ <para>
+ The table below describes Additional Categories. The Related
+ Categories column lists one or more categories that are suggested
+ to be used in conjunction with the Additional Category. Note that at
+ least one Main Category must be included in the desktop entry's list
+ of categories. If multiple Main Categories are included in a single
+ desktop entry file, the entry may appear more than once in the menu.
+ If the Related Categories column is blank, the
+ Additional Category can be used with any Main Category.
+ <informaltable>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Additional Category</entry>
+ <entry>Description</entry>
+ <entry>Related Categories</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Building</entry>
+ <entry>A tool to build applications</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Debugger</entry>
+ <entry>A tool to debug applications</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>IDE</entry>
+ <entry>IDE application</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>GUIDesigner</entry>
+ <entry>A GUI designer application</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Profiling</entry>
+ <entry>A profiling tool</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>RevisionControl</entry>
+ <entry>Applications like cvs or subversion</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Translation</entry>
+ <entry>A translation tool</entry>
+ <entry>Development</entry>
+
+ </row><row>
+ <entry>Calendar</entry>
+ <entry>Calendar application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>ContactManagement</entry>
+ <entry>E.g. an address book</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Database</entry>
+ <entry>Application to manage a database</entry>
+ <entry>Office or Development or AudioVideo</entry>
+
+ </row><row>
+ <entry>Dictionary</entry>
+ <entry>A dictionary</entry>
+ <entry>Office;TextTools</entry>
+
+ </row><row>
+ <entry>Chart</entry>
+ <entry>Chart application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Email</entry>
+ <entry>Email application</entry>
+ <entry>Office;Network</entry>
+
+ </row><row>
+ <entry>Finance</entry>
+ <entry>Application to manage your finance</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>FlowChart</entry>
+ <entry>A flowchart application</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>PDA</entry>
+ <entry>Tool to manage your PDA</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>ProjectManagement</entry>
+ <entry>Project management application</entry>
+ <entry>Office;Development</entry>
+
+ </row><row>
+ <entry>Presentation</entry>
+ <entry>Presentation software</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>Spreadsheet</entry>
+ <entry>A spreadsheet</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>WordProcessor</entry>
+ <entry>A word processor</entry>
+ <entry>Office</entry>
+
+ </row><row>
+ <entry>2DGraphics</entry>
+ <entry>2D based graphical application</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>VectorGraphics</entry>
+ <entry>Application for viewing, creating, or processing vector graphics</entry>
+ <entry>Graphics;2DGraphics</entry>
+
+ </row><row>
+ <entry>RasterGraphics</entry>
+ <entry>Application for viewing, creating, or processing raster (bitmap) graphics</entry>
+ <entry>Graphics;2DGraphics</entry>
+
+ </row><row>
+ <entry>3DGraphics</entry>
+ <entry>Application for viewing, creating, or processing 3-D graphics</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>Scanning</entry>
+ <entry>Tool to scan a file/text</entry>
+ <entry>Graphics</entry>
+
+ </row><row>
+ <entry>OCR</entry>
+ <entry>Optical character recognition application</entry>
+ <entry>Graphics;Scanning</entry>
+
+ </row><row>
+ <entry>Photography</entry>
+ <entry>Camera tools, etc.</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>Publishing</entry>
+ <entry>Desktop Publishing applications and Color Management tools</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>Viewer</entry>
+ <entry>Tool to view e.g. a graphic or pdf file</entry>
+ <entry>Graphics or Office</entry>
+
+ </row><row>
+ <entry>TextTools</entry>
+ <entry>A text tool utiliy</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>DesktopSettings</entry>
+ <entry>Configuration tool for the GUI</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>HardwareSettings</entry>
+ <entry>A tool to manage hardware components, like sound cards, video cards or printers</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>Printing</entry>
+ <entry>A tool to manage printers</entry>
+ <entry>HardwareSettings;Settings</entry>
+
+ </row><row>
+ <entry>PackageManager</entry>
+ <entry>A package manager application</entry>
+ <entry>Settings</entry>
+
+ </row><row>
+ <entry>Dialup</entry>
+ <entry>A dial-up program</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>InstantMessaging</entry>
+ <entry>An instant messaging client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>Chat</entry>
+ <entry>A chat client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>IRCClient</entry>
+ <entry>An IRC client</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>FileTransfer</entry>
+ <entry>Tools like FTP or P2P programs</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>HamRadio</entry>
+ <entry>HAM radio software</entry>
+ <entry>Network or Audio</entry>
+
+ </row><row>
+ <entry>News</entry>
+ <entry>A news reader or a news ticker</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>P2P</entry>
+ <entry>A P2P program</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>RemoteAccess</entry>
+ <entry>A tool to remotely manage your PC</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>Telephony</entry>
+ <entry>Telephony via PC</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>TelephonyTools</entry>
+ <entry>Telephony tools, to dial a number, manage PBX, ...</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>VideoConference</entry>
+ <entry>Video Conference software</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>WebBrowser</entry>
+ <entry>A web browser</entry>
+ <entry>Network</entry>
+
+ </row><row>
+ <entry>WebDevelopment</entry>
+ <entry>A tool for web developers</entry>
+ <entry>Network or Development</entry>
+
+ </row><row>
+ <entry>Midi</entry>
+ <entry>An app related to MIDI</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Mixer</entry>
+ <entry>Just a mixer</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Sequencer</entry>
+ <entry>A sequencer</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>Tuner</entry>
+ <entry>A tuner</entry>
+ <entry>AudioVideo;Audio</entry>
+
+ </row><row>
+ <entry>TV</entry>
+ <entry>A TV application</entry>
+ <entry>AudioVideo;Video</entry>
+
+ </row><row>
+ <entry>AudioVideoEditing</entry>
+ <entry>Application to edit audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>Player</entry>
+ <entry>Application to play audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>Recorder</entry>
+ <entry>Application to record audio/video files</entry>
+ <entry>Audio or Video or AudioVideo</entry>
+
+ </row><row>
+ <entry>DiscBurning</entry>
+ <entry>Application to burn a disc</entry>
+ <entry>AudioVideo</entry>
+
+ </row><row>
+ <entry>ActionGame</entry>
+ <entry>An action game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>AdventureGame</entry>
+ <entry>Adventure style game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>ArcadeGame</entry>
+ <entry>Arcade style game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>BoardGame</entry>
+ <entry>A board game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>BlocksGame</entry>
+ <entry>Falling blocks game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>CardGame</entry>
+ <entry>A card game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>KidsGame</entry>
+ <entry>A game for kids</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>LogicGame</entry>
+ <entry>Logic games like puzzles, etc</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>RolePlaying</entry>
+ <entry>A role playing game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>Simulation</entry>
+ <entry>A simulation game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>SportsGame</entry>
+ <entry>A sports game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>StrategyGame</entry>
+ <entry>A strategy game</entry>
+ <entry>Game</entry>
+
+ </row><row>
+ <entry>Art</entry>
+ <entry>Software to teach arts</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Construction</entry>
+ <entry></entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Music</entry>
+ <entry>Musical software</entry>
+ <entry>AudioVideo;Education</entry>
+
+ </row><row>
+ <entry>Languages</entry>
+ <entry>Software to learn foreign languages</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Science</entry>
+ <entry>Scientific software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ArtificialIntelligence</entry>
+ <entry>Artificial Intelligence software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Astronomy</entry>
+ <entry>Astronomy software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Biology</entry>
+ <entry>Biology software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Chemistry</entry>
+ <entry>Chemistry software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>ComputerScience</entry>
+ <entry>ComputerSience software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>DataVisualization</entry>
+ <entry>Data visualization software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Economy</entry>
+ <entry>Economy software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Electricity</entry>
+ <entry>Electricity software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Geography</entry>
+ <entry>Geography software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Geology</entry>
+ <entry>Geology software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Geoscience</entry>
+ <entry>Geoscience software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>History</entry>
+ <entry>History software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ImageProcessing</entry>
+ <entry>Image Processing software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Literature</entry>
+ <entry>Literature software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>Math</entry>
+ <entry>Math software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>NumericalAnalysis</entry>
+ <entry>Numerical analysis software</entry>
+ <entry>Education;Science;Math</entry>
+
+ </row><row>
+ <entry>MedicalSoftware</entry>
+ <entry>Medical software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Physics</entry>
+ <entry>Physics software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Robotics</entry>
+ <entry>Robotics software</entry>
+ <entry>Education;Science</entry>
+
+ </row><row>
+ <entry>Sports</entry>
+ <entry>Sports software</entry>
+ <entry>Education</entry>
+
+ </row><row>
+ <entry>ParallelComputing</entry>
+ <entry>Parallel computing software</entry>
+ <entry>Education;Science;ComputerScience</entry>
+
+ </row><row>
+ <entry>Amusement</entry>
+ <entry>A simple amusement</entry>
+ <entry></entry>
+
+
+ </row><row>
+ <entry>Archiving</entry>
+ <entry>A tool to archive/backup data</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Compression</entry>
+ <entry>A tool to manage compressed data/archives</entry>
+ <entry>Utility;Archiving</entry>
+
+ </row><row>
+ <entry>Electronics</entry>
+ <entry>Electronics software, e.g. a circuit designer</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Emulator</entry>
+ <entry>Emulator of another platform, such as a DOS emulator</entry>
+ <entry>System or Game</entry>
+
+ </row><row>
+ <entry>Engineering</entry>
+ <entry>Engineering software, e.g. CAD programs</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>FileTools</entry>
+ <entry>A file tool utility</entry>
+ <entry>Utility or System</entry>
+
+ </row><row>
+ <entry>FileManager</entry>
+ <entry>A file manager</entry>
+ <entry>System;FileTools</entry>
+
+ </row><row>
+ <entry>TerminalEmulator</entry>
+ <entry>A terminal emulator application</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Filesystem</entry>
+ <entry>A file system tool</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Monitor</entry>
+ <entry>Monitor application/applet that monitors some resource or activity</entry>
+ <entry>System</entry>
+
+ </row><row>
+ <entry>Security</entry>
+ <entry>A security tool</entry>
+ <entry>Settings or System</entry>
+
+ </row><row>
+ <entry>Accessibility</entry>
+ <entry>Accessibility</entry>
+ <entry>Settings or Utility</entry>
+
+ </row><row>
+ <entry>Calculator</entry>
+ <entry>A calculator</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Clock</entry>
+ <entry>A clock application/applet</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>TextEditor</entry>
+ <entry>A text editor</entry>
+ <entry>Utility</entry>
+
+ </row><row>
+ <entry>Documentation</entry>
+ <entry>Help or documentation</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Adult</entry>
+ <entry>Application handles adult or explicit material</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Core</entry>
+ <entry>Important application, core to the desktop such as a file manager or a help browser</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>KDE</entry>
+ <entry>Application based on KDE libraries</entry>
+ <entry>QT</entry>
+
+ </row><row>
+ <entry>GNOME</entry>
+ <entry>Application based on GNOME libraries</entry>
+ <entry>GTK</entry>
+
+ </row><row>
+ <entry>GTK</entry>
+ <entry>Application based on GTK+ libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Qt</entry>
+ <entry>Application based on Qt libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Motif</entry>
+ <entry>Application based on Motif libraries</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>Java</entry>
+ <entry>Application based on Java GUI libraries, such as AWT or Swing</entry>
+ <entry></entry>
+
+ </row><row>
+ <entry>ConsoleOnly</entry>
+ <entry>Application that only works inside a terminal (text-based or command line application)</entry>
+ <entry></entry>
+
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </para>
+ <para>
+ The table below describes Reserved Categories.
+ Reserved Categories have a specific desktop specific meaning
+ that has not been standardized (yet). Desktop entry files that use
+ a reserved category MUST also include an appropriate OnlyShowIn= entry
+ to restrict themselves to those environments that properly support the
+ reserved category as used.
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Reserved Category</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Screensaver</entry>
+ <entry>A screen saver (launching this desktop entry should activate the screen saver)</entry>
+ </row><row>
+ <entry>TrayIcon</entry>
+ <entry>An application that is primarily an icon for the "system tray" or "notification area" (apps that open a normal window and just happen to have a tray icon as well should not list this category)</entry>
+ </row><row>
+ <entry>Applet</entry>
+ <entry>An applet that will run inside a panel or another such application, likely desktop specific</entry>
+ </row><row>
+ <entry>Shell</entry>
+ <entry>A shell (an actual specific shell such as
+ <filename>bash</filename> or <filename>tcsh</filename>, not a TerminalEmulator)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+
+ </para>
+ </appendix>
+ <appendix id="onlyshowin-registry">
+ <title>Registered OnlyShowIn Environments</title>
+ <para>
+ Remember, these are case-sensitive. "KDE" not "kde" should be
+ used.
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>OnlyShowIn Value</entry>
+ <entry>Environment</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>GNOME</entry><entry>GNOME Desktop</entry>
+ </row><row>
+ <entry>KDE</entry><entry>KDE Desktop</entry>
+ </row><row>
+ <entry>LXDE</entry><entry>LXDE Desktop</entry>
+ </row><row>
+ <entry>ROX</entry><entry>ROX Desktop</entry>
+ </row><row>
+ <entry>Unity</entry><entry>Unity Shell</entry>
+ </row><row>
+ <entry>XFCE</entry><entry>XFCE Desktop</entry>
+ </row><row>
+ <entry>Old</entry><entry>Legacy menu systems</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </para>
+ </appendix>
+ <appendix id="third-party-howto">
+ <title>Integrating your application in the menus</title>
+ <sect1 id="adding-items">
+ <title>Adding menu items</title>
+ <para>
+ The following steps describe how a third party application can add
+ menu items to the menu system:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Install desktop entries to
+ <replaceable>datadir</replaceable>/applications/ for each menu
+ item. Please namespace the filename, as in "vendor-foo.desktop", or
+ use a subdirectory of
+ <replaceable>datadir</replaceable>/applications/ so you have
+ "vendor/foo.desktop." Please be sure all desktop entries are valid
+ (see the <ulink
+ url="http://www.freedesktop.org/software/desktop-file-utils/">
+ desktop-file-utils</ulink> package for a validation utility).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Install an XML menu file to <replaceable>sysconfdir</replaceable>/menus/applications-merged/ to add any submenus, if your desktop entries aren't already
+ included in some common categories.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Install any directory entries needed for your submenus to <replaceable>datadir</replaceable>/desktop-directories/, taking care to namespace and validate
+ the directory entries.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect1>
+ <sect1 id="locations">
+ <title>Install Locations</title>
+ <para>
+ If an application is intended to be installed by root on a system wide
+ basis then /usr/share is recommended to be used as value for
+ <replaceable>datadir</replaceable> and /etc/xdg is recommended to be
+ used as value for <replaceable>sysconfdir</replaceable>.
+ In case the /usr/share hierarchy is not writable it is recommended to
+ use /usr/local/share as value for <replaceable>datadir</replaceable>
+ instead.
+ </para>
+ <para>
+ If an application is intended to be installed by an unprivileged user
+ for exclusive use by that user only then
+ <varname>$XDG_DATA_HOME</varname> should be used as value
+ for <replaceable>datadir</replaceable> and
+ <varname>$XDG_CONFIG_HOME</varname> should be used as value
+ for <replaceable>sysconfdir</replaceable>.
+ If <varname>$XDG_DATA_HOME</varname> is not set, the default value of
+ $HOME/.local/share should be used for it.
+ If <varname>$XDG_CONFIG_HOME</varname> is not set, the default value of
+ $HOME/.config should be used for it.
+ </para>
+ </sect1>
+ <sect1 id="menu-add-example">
+ <title>Example</title>
+ <para>
+ The company ShinyThings Inc. has developed an application named
+ <emphasis>WebMirror 1.0</emphasis> and would like to add its own
+ submenu to the system menus consisting of a <emphasis>WebMirror</emphasis>
+ menu item and a <emphasis>WebMirror Admin Tool</emphasis>
+ menu item. The company will use "shinythings" as its vendor id.
+ For the purpose of this example all menu items will be available
+ in two languages, English and Dutch.
+ The language code for Dutch is <emphasis>nl</emphasis>.
+ </para>
+ <para>
+ First the company needs to create two .desktop files that describe
+ the two menu items:
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/applications/shinythings-webmirror.desktop:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+ Type=Application
+
+ Exec=webmirror
+ Icon=webmirror
+
+ Name=WebMirror
+ Name[nl]=WebSpiegel
+ </programlisting>
+ </informalexample>
+ and
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/applications/shinythings-webmirror-admin.desktop:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+ Type=Application
+
+ Exec=webmirror-admintool
+ Icon=webmirror-admintool
+
+ Name=WebMirror Admin Tool
+ Name[nl]=WebSpiegel Administratie Tool
+ </programlisting>
+ </informalexample>
+ A .directory file needs to be installed to provide a title and icon
+ for the sub-menu itself:
+ <informalexample>
+ <programlisting>
+ <replaceable>datadir</replaceable>/desktop-directories/shinythings-webmirror.directory:
+
+ [Desktop Entry]
+ Encoding=UTF-8
+
+ Icon=webmirror
+
+ Name=WebMirror
+ Name[nl]=WebSpiegel
+ </programlisting>
+ </informalexample>
+ And finally, a .menu file needs to be provided that links it all
+ togther:
+ <informalexample>
+ <programlisting>
+ <replaceable>sysconfdir</replaceable>/menus/application-merged/shinythings-webmirror.menu:
+
+ &lt;!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu &dtd-version;//EN"
+ "http://www.freedesktop.org/standards/menu-spec/menu-&dtd-version;.dtd"&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;Applications&lt;/Name&gt;
+ &lt;Menu&gt;
+ &lt;Name&gt;WebMirror&lt;/Name&gt;
+ &lt;Directory&gt;shinythings-webmirror.directory&lt;/Directory&gt;
+ &lt;Include&gt;
+ &lt;Filename&gt;shinythings-webmirror.desktop&lt;/Filename&gt;
+ &lt;Filename&gt;shinythings-webmirror-admin.desktop&lt;/Filename&gt;
+ &lt;/Include&gt;
+ &lt;/Menu&gt;
+ </programlisting>
+ </informalexample>
+
+ </para>
+ </sect1>
+ <sect1 id="compatibility">
+ <title>Backward Compatibility</title>
+ <para>
+ For a limited time, installing a directory hierarchy to
+ the old GNOME/KDE specific locations such as /usr/share/applnk and
+ /usr/share/gnome/apps will continue to work as way to add your
+ application to the menu system as well. There are two ways to support
+ both the old and new menu systems at the same time:
+ <itemizedlist>
+ <listitem>
+ <para>
+ If you add a <varname>Categories</varname> line to the desktop
+ entries in the legacy hierarchy, implementations of this
+ specification will ignore their location in the legacy hierarchy,
+ and arrange them according to <varname>Categories</varname> instead.
+ This allows you to install a single desktop file that works in all
+ cases, though on the down side it's in a legacy location.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ If you add the line <literal>OnlyShowIn=Old;</literal> to a desktop
+ entry, then old legacy implementations that ignore
+ <varname>OnlyShowIn</varname> will still show the desktop entry, but
+ implementations of this specification will not. Thus you can
+ add an "<literal>OnlyShowIn=Old;</literal>" entry to the legacy
+ hierarchy, and a new-style desktop entry to
+ <replaceable>datadir</replaceable>/applications/, and still get
+ only one entry in the menus.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect1>
+ </appendix>
+ <appendix id="implementation-notes">
+ <title>Implementation notes</title>
+ <sect1 id="menu-editing">
+ <title>Menu editing</title>
+ <para>
+ To implement menu editing, the intent is that a per-user file is
+ created. The per-user file should specify a &lt;MergeFile&gt; with the
+ system wide file, so that system changes are inherited. When the user
+ deletes a menu item, you add
+ <literal>&lt;Exclude&gt;&lt;Filename&gt;foo.desktop&lt;/Filename&gt;&lt;/Exclude&gt;</literal>. If
+ the user adds a menu item, you use
+ <literal>&lt;Include&gt;&lt;Filename&gt;foo.desktop&lt;/Filename&gt;&lt;/Include&gt;</literal>.
+ </para>
+ <para>
+ If the user moves a folder you can use &lt;Move&gt; elements
+ to represent the move. &lt;Move&gt; elements used for menu-editing
+ should always be added to the most top-level menu to ensure that moves
+ are performed in the order in which they are specified; moves specified
+ in child menus are always performed before moves specified in a more
+ top level menu regardless of their location in the menu file.
+ </para>
+ <para>
+ To delete a folder, simply append the &lt;Deleted&gt; element.
+ </para>
+ <para>
+ When adding a new folder or moving an existing folder, menu editing
+ implementations are advised not to re-use the menu path of a previously
+ deleted folder.
+ </para>
+ <para>
+ Menu editors probably need to do some kind of consolidation/compression
+ to avoid an XML tree that grows infinitely over time.
+ </para>
+ </sect1>
+ </appendix>
+ <glossary><title>Glossary</title>
+ <para>
+ This glossary defines some of the terms used in this specification.
+ </para>
+
+ <glossentry id="term-desktop-entry"><glossterm>Desktop entry</glossterm>
+ <glossdef>
+ <para>
+ A desktop entry is a file with a name ending in the ".desktop"
+ extension which conforms to the <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entry specification</ulink> with <literal>Type=Application</literal>.
+ It describes a menu item, including a name, an icon, and what to do when the item is selected.
+ Desktop entries are also known as ".desktop files."
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-desktop-file-id"><glossterm>Desktop-File Id</glossterm>
+ <glossdef>
+ <para>
+ The id to identify a desktop entry with.
+ For example, if <filename>/usr/share/applications</filename> is
+ specified as an &lt;AppDir&gt;, and <filename>/opt/ude</filename>
+ as &lt;LegacyDir prefix="foo-"&gt;
+ then
+ <filename>/usr/share/applications/foo/bar.desktop</filename>,
+ <filename>/usr/share/applications/foo-bar.desktop</filename>
+ and
+ <filename>/opt/ude/Settings/bar.desktop</filename> all have
+ the same desktop-file id <literal>foo-bar.desktop</literal>
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-directory-entry"><glossterm>Directory entry</glossterm>
+ <glossdef>
+ <para>
+ A directory entry is a file with a name ending in the ".directory"
+ extension which conforms to the <ulink
+ url="http://www.freedesktop.org/Standards/desktop-entry-spec">desktop
+ entry specification</ulink> with <literal>Type=Directory</literal>.
+ It provides a localized name and an icon for a submenu.
+ Directory entries are also known as ".directory files."
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-menu-path"><glossterm>Menu path</glossterm>
+ <glossdef>
+ <para>
+ A "menu path" is the path to a particular menu. Menu paths are
+ always "relative" so never start with a slash character.
+ The path to a menu is simply the &lt;Name&gt; of each parent
+ of the menu, followed by the &lt;Name&gt; of the menu itself.
+ For example, "Foo/Bar/Baz" is a valid menu path.
+ </para>
+ </glossdef>
+ </glossentry>
+
+ <glossentry id="term-relative-path"><glossterm>Relative path</glossterm>
+ <glossdef>
+ <para>
+ The canonical path to a directory entry, relative to the
+ &lt;DirectoryDir&gt; containing the
+ entry. For example, if <filename>/usr/share/desktop-directories</filename> is
+ specified as an &lt;DirectoryDir&gt;, the relative path to
+ <filename>/usr/share/desktop-directories/foo/bar.directory</filename> is
+ <filename>foo/bar.directory</filename>.
+ </para>
+ </glossdef>
+ </glossentry>
+ </glossary>
+</article>
+
diff --git a/menu/menu.dtd b/menu/menu.dtd
new file mode 100644
index 0000000..1bca3ed
--- /dev/null
+++ b/menu/menu.dtd
@@ -0,0 +1,84 @@
+<!-- For explanations see http://www.freedesktop.org/standards/menu-spec -->
+<!ELEMENT Menu (
+ Name, (
+ Directory
+ | DefaultAppDirs
+ | AppDir
+ | DefaultDirectoryDirs
+ | DirectoryDir
+ | LegacyDir
+ | KDELegacyDirs
+ | MergeFile
+ | DefaultMergeDirs
+ | MergeDir
+ | OnlyUnallocated
+ | NotOnlyUnallocated
+ | Deleted
+ | NotDeleted
+ | Include
+ | Exclude
+ | Move
+ | Menu
+ | Layout
+ | DefaultLayout
+ )*
+)>
+
+<!ELEMENT Name (#PCDATA)>
+
+<!ELEMENT Directory (#PCDATA)>
+
+<!ELEMENT DefaultAppDirs EMPTY>
+<!ELEMENT AppDir (#PCDATA)>
+
+<!ELEMENT DefaultDirectoryDirs EMPTY>
+<!ELEMENT DirectoryDir (#PCDATA)>
+
+<!ELEMENT LegacyDir (#PCDATA)>
+<!ATTLIST LegacyDir prefix CDATA #IMPLIED>
+<!ELEMENT KDELegacyDirs EMPTY>
+
+<!ELEMENT MergeFile (#PCDATA)>
+<!ATTLIST MergeFile type (path|parent) #IMPLIED>
+
+<!ELEMENT DefaultMergeDirs EMPTY>
+<!ELEMENT MergeDir (#PCDATA)>
+
+<!ELEMENT OnlyUnallocated EMPTY>
+<!ELEMENT NotOnlyUnallocated EMPTY>
+
+<!ELEMENT Deleted EMPTY>
+<!ELEMENT NotDeleted EMPTY>
+
+<!ELEMENT Exclude ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Include ((Category|Filename|And|Or|Not|All)*)>
+
+<!ELEMENT And ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Or ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Not ((Category|Filename|And|Or|Not|All)*)>
+<!ELEMENT Filename (#PCDATA)>
+<!ELEMENT Category (#PCDATA)>
+<!ELEMENT All EMPTY>
+
+<!ELEMENT Move ((Old,New)*)>
+<!ELEMENT Old (#PCDATA)>
+<!ELEMENT New (#PCDATA)>
+
+<!ELEMENT Layout ((Filename|Menuname|Separator|Merge)*)>
+<!ELEMENT DefaultLayout ((Filename|Menuname|Separator|Merge)*)>
+<!ATTLIST DefaultLayout show_empty (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline_limit CDATA #IMPLIED>
+<!ATTLIST DefaultLayout inline_header (true|false) #IMPLIED>
+<!ATTLIST DefaultLayout inline_alias (true|false) #IMPLIED>
+
+<!ELEMENT Menuname (#PCDATA)>
+<!ATTLIST Menuname inline (true|false) #IMPLIED>
+<!ATTLIST Menuname inline_limit CDATA #IMPLIED>
+<!ATTLIST Menuname inline_header (true|false) #IMPLIED>
+<!ATTLIST Menuname inline_alias (true|false) #IMPLIED>
+
+<!ELEMENT Separator EMPTY>
+
+<!ELEMENT Merge EMPTY>
+<!ATTLIST Merge type (menus|files|all) #REQUIRED>
diff --git a/menu/tests/ChangeLog b/menu/tests/ChangeLog
new file mode 100644
index 0000000..ac55e4f
--- /dev/null
+++ b/menu/tests/ChangeLog
@@ -0,0 +1,70 @@
+2005-04-25 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/u: add test for recursive inclusion of .menu files.
+ Also know as the "get really hosed and eat all the RAM
+ you can find" test.
+
+2005-04-20 Waldo Bastian <bastian@kde.org>
+
+ * tests/s/result,
+ tests/s/test: Test that .desktop files under $HOME correctly
+ override .desktop files at system level
+
+ * tests/t/result,
+ tests/t/test: Test order in which <Move> elements are processed
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ Make it a bit more difficult in order to catch:
+ http://bugzilla.gnome.org/show_bug.cgi?id=167758
+ Thanks to Chris Lahey for the test case.
+
+ * tests/o/test: put freecell.desktop in a subdirectory.
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/o/result,
+ tests/o/test: test that
+ <And><Category>foo</Category><Not><Category>foo</Category></Not></And>
+ doesn't match anything.
+
+ * README: add bit about how to test the GNOME impl.
+
+2005-02-18 Mark McLoughlin <mark@skynet.ie>
+
+ Problem pointed out by Waldo.
+
+ * menutest: don't try and run the "CVS" test :)
+
+ * tests/m/result,
+ tests/n/result: items in a hidden or deleted menu
+ should be considered allocated.
+
+2004-12-08 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/2/result,
+ tests/2/test: test the new <OnlyUnallocated> behaviour.
+
+2004-08-29 Mark McLoughlin <mark@skynet.ie>
+
+ * tests/m: test for NoDisplay=true behavious in .desktop and
+ .directory files.
+
+ * data/hidden.desktop,
+ data/hidden.directory: add.
+
+ * data/apps.directory: add Type=Directory.
+
+ * menutest: actually generate a test result if one doesn't
+ exist - makes it easier to write new tests.
+
+2003-10-23 Havoc Pennington <hp@redhat.com>
+
+ * tests/f/test: remove <OnlyUnallocated/> from inside <Include>
+ statement, not allowed.
+
+2003-10-16 Havoc Pennington <hp@redhat.com>
+
+ * menutest: cat log file on failure; print list of failed tests
+ when we're done
+
diff --git a/menu/tests/README b/menu/tests/README
new file mode 100644
index 0000000..b003dc0
--- /dev/null
+++ b/menu/tests/README
@@ -0,0 +1,44 @@
+This directory contains regression tests for the menu-spec.
+
+To run these tests your menu-spec implementation should be
+able to generate a menu in the following text format:
+
+<Full Menu Name><tab><Desktop File Id><tab><Full Path to Desktop File>
+
+Example:
+
+Editors/ kde-kwrite.desktop /home/bastian/.local/share/applications/kde-kwrite.desktop
+Editors/ kde-kate.desktop /home/bastian/.local/share/applications/kde-kate.desktop
+Editors/ kde-KEdit.desktop /home/bastian/.local/share/applications/kde-KEdit.desktop
+Development/ kde-gideon.desktop /opt/kde3/share/applnk/Development/gideon.desktop
+Development/ kde-kbabel.desktop /opt/kde3/share/applnk/Development/kbabel.desktop
+Development/ kde-quanta.desktop /opt/kde3/share/applnk/Development/quanta.desktop
+/ kde-Kfind.desktop /opt/kde3/share/applnk/Kfind.desktop
+/ kde-Home.desktop /opt/kde3/share/applnk/Home.desktop
+/ kde-Help.desktop /opt/kde3/share/applnk/Help.desktop
+
+
+
+The environment variable $MENUTEST should point to a command that is
+able to generate the menu in the above format.
+
+To test KDE one can use:
+ MENUTEST="kbuildsycoca --menutest"
+
+With GNOME, you can use:
+ MENUTEST=gnome-menu-spec-test
+
+The menutest script should be used to run the tests. The following commands can
+be used:
+
+ MENUTEST="foo -bar" ./menutest
+
+to run all tests
+
+ MENUTEST="foo -bar" TESTS="Deleted Directory" ./menutest
+
+to run the tests named "Deleted" and "Directory" only
+
+All tests contain of a test setup script named "test" and a file describing
+the expected menu named "result".
+
diff --git a/menu/tests/TODO b/menu/tests/TODO
new file mode 100644
index 0000000..6e178bf
--- /dev/null
+++ b/menu/tests/TODO
@@ -0,0 +1,5 @@
+I hope you understand what i mean ;)
+
+desktop-file-id stuff & overwrite stuff absolute path
+
+onlyshowin - notshowin
diff --git a/menu/tests/data/Help.desktop b/menu/tests/data/Help.desktop
new file mode 100644
index 0000000..dc867cd
--- /dev/null
+++ b/menu/tests/data/Help.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Encoding=UTF-8
+Exec=khelpcenter
+Icon=khelpcenter
+DocPath=khelpcenter/index.html
+Type=Application
+Terminal=0
+
+Name=Help
+Name[af]=Hulp
+Name[az]=Yardım
+Name[be]=Дапамога
+Name[bg]=Помощ
+Name[br]=Skoazell
+Name[bs]=Pomoć
+Name[ca]=Ajuda
+Name[cs]=Nápověda
+Name[cy]=Cymorth
+Name[da]=Hjælp
+Name[de]=Hilfe
+Name[el]=Βοήθεια
+Name[eo]=Helpo
+Name[es]=Ayuda
+Name[et]=Abiinfo
+Name[eu]=Laguntza
+Name[fa]=راهنما
+Name[fi]=Ohje
+Name[fr]=Aide
+Name[gl]=Axuda
+Name[he]=עזרה
+Name[hr]=Pomoć
+Name[hu]=Segítség
+Name[id]=Keterangan bantu
+Name[is]=Hjálp
+Name[it]=Aiuto
+Name[ja]=ヘルプ
+Name[ko]=도움말
+Name[lo]=ລະບົບຊ່ວຍເຫືລອ
+Name[lt]=Pagalba
+Name[lv]=Palīdzība
+Name[mn]=Тусламж
+Name[mt]=Għajnuna
+Name[nb]=Hjelp
+Name[nn]=Hjelp
+Name[nso]=Thuso
+Name[oc]=Ajuda
+Name[pl]=Pomoc
+Name[pt]=Ajuda
+Name[pt_BR]=Ajuda
+Name[ro]=Ajutor
+Name[ru]=Справка
+Name[sk]=Pomocník
+Name[sl]=Pomoč
+Name[sr]=Pomoć
+Name[ss]=Sita
+Name[sv]=Hjälp
+Name[ta]=¯¾Å¢
+Name[th]=ระบบช่วยเหลือ
+Name[tr]=Yardım
+Name[uk]=Довідка
+Name[ven]=Thuso
+Name[vi]=Trợ giúp
+Name[wa]=Aidance
+Name[xh]=Uncedo
+Name[zh_CN]=帮助
+Name[zh_TW]=求助
+Name[zu]=Usizo
+
diff --git a/menu/tests/data/Home.desktop b/menu/tests/data/Home.desktop
new file mode 100644
index 0000000..2bf8178
--- /dev/null
+++ b/menu/tests/data/Home.desktop
@@ -0,0 +1,127 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Application
+Exec=kfmclient openProfile filemanagement
+Icon=folder_home
+Terminal=0
+
+Name=Home
+Name[af]=Huis
+Name[az]=Başlanğıc
+Name[be]=Хатні
+Name[bg]=Домашна директория
+Name[br]=Er-gêr
+Name[bs]=Početak
+Name[ca]=Inici
+Name[cs]=Můj adresář
+Name[cy]=Cartref
+Name[da]=Hjem
+Name[de]=Persönliches Verzeichnis
+Name[el]=Σπίτι
+Name[eo]=Hejmo
+Name[es]=Personal
+Name[et]=Kodukataloog
+Name[eu]=Etxea
+Name[fa]=خانه
+Name[fi]=Koti
+Name[fr]=Dossier personnel
+Name[gl]=Persoal
+Name[he]=בית
+Name[hr]=Početak
+Name[hu]=Saját könyvtár
+Name[id]=Rumah
+Name[is]=Heimasvæðið þitt
+Name[ja]=ホーム
+Name[ko]=홈
+Name[lo]=ພື້ນທີ່ສ່ວນຕົວ
+Name[lt]=Pradžia
+Name[lv]=Mājas
+Name[mn]=Хувийн лавлах
+Name[mt]=Direttorju Personali
+Name[nb]=Hjem
+Name[nl]=Persoonlijke map
+Name[nn]=Heim
+Name[nso]=Gae
+Name[oc]=Inici
+Name[pl]=Katalog domowy
+Name[pt]=Casa
+Name[ro]=Acasă
+Name[ru]=Домой
+Name[sk]=Domov
+Name[sl]=Domov
+Name[sr]=Кориснички директоријум
+Name[ss]=Ekhaya
+Name[sv]=Hem
+Name[ta]=¦¾¡¼ì¸õ
+Name[th]=พื้นที่ส่วนตัว
+Name[tr]=Başlangıç
+Name[uk]=Домівка
+Name[ven]=Haya
+Name[wa]=Måjhon
+Name[xh]=Ikhaya
+Name[xx]=xxHomexx
+Name[zh_CN]=起点
+Name[zh_TW]=家目錄
+Name[zu]=Ikhaya
+
+GenericName=Personal Files
+GenericName[af]=Persoonlike Lêers
+GenericName[az]=Şəxsi Fayllar
+GenericName[be]=Пэрсанальныя файлы
+GenericName[bg]=Лични файлове
+GenericName[br]=Restroù deoc'h
+GenericName[bs]=Osobne datoteke
+GenericName[ca]=Fitxers personals
+GenericName[cs]=Osobní soubory
+GenericName[cy]=Ffeiliau Personol
+GenericName[da]=Personlige filer
+GenericName[de]=Eigene Dateien
+GenericName[el]=Προσωπικά Αρχεία
+GenericName[eo]=Personaj dosieroj
+GenericName[es]=Archivos personales
+GenericName[et]=Isiklikud failid
+GenericName[eu]=Fitxategi Pertsonalak
+GenericName[fa]=پرونده‌های شخصی
+GenericName[fi]=Omat tiedostot
+GenericName[fr]=Fichiers personnels
+GenericName[gl]=Ficheiros Persoais
+GenericName[he]=קבצים אישיים
+GenericName[hr]=Osobne datoteke
+GenericName[hu]=személyes fájlok
+GenericName[id]=File Pribadi
+GenericName[is]=Skrárnar þínar
+GenericName[it]=File personali
+GenericName[ja]=個人のファイル
+GenericName[ko]=혼자만 쓰는 파일
+GenericName[lo]=ທີ່ເກັບແຟ້ມແລະເອກະສານສວ່ນຕົວຫລືອື່ນຯ
+GenericName[lt]=Asmeninės Bylos
+GenericName[lv]=Personālie Faili
+GenericName[mn]=Өөрийн файлууд
+GenericName[mt]=Fajls Personali
+GenericName[nb]=Personlige filer
+GenericName[nl]=persoonlijke bestanden
+GenericName[nn]=Personlege filer
+GenericName[nso]=Difaele tsa Botho
+GenericName[oc]=FiquièRs personals
+GenericName[pl]=Pliki osobiste
+GenericName[pt]=Ficheiros Pessoais
+GenericName[pt_BR]=Arquivos Pessoais
+GenericName[ro]=Fişiere personale
+GenericName[ru]=Личные файлы
+GenericName[sk]=Osobné súbory
+GenericName[sl]=Osebne datoteke
+GenericName[sr]=Овај директоријум садржи ваше личне фајлове
+GenericName[sv]=Personliga filer
+GenericName[ta]=¦º¡ó¾ì §¸¡ôÒì¸û
+GenericName[th]=ที่เก็บแฟ้มและเอกสารส่วนตัว หรืออื่น ๆ
+GenericName[tr]=Kişisel Dosyalar
+GenericName[uk]=Особисті файли
+GenericName[ven]=Dzifaela dza vhune
+GenericName[vi]=File cá nhân
+GenericName[wa]=Fitchîs da vosse
+GenericName[xh]=Iifayile Zobuqu
+GenericName[xx]=xxPersonal Filesxx
+GenericName[zh_CN]=个人文件
+GenericName[zh_TW]=個人檔案
+GenericName[zu]=Amafayela Omuntu siqu
+
diff --git a/menu/tests/data/KEdit.desktop b/menu/tests/data/KEdit.desktop
new file mode 100644
index 0000000..4274a2b
--- /dev/null
+++ b/menu/tests/data/KEdit.desktop
@@ -0,0 +1,34 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kedit;
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+GenericName=Simple Text Editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Jednoduchý textový editor
+GenericName[da]=Simpel teksteditor
+GenericName[es]=Editor de texto sencillo
+GenericName[fr]=Éditeur de texte élémentaire
+GenericName[pt_BR]=Editor de Texto Simples
+GenericName[sv]=Enkel texteditor
+GenericName[wa]=Simpe aspougneu di tecse
+Exec=kedit -caption "%c" %i %m %u
+Icon=kedit
+TerminalOptions=
+Path=
+DocPath=kedit/index.html
+Type=Application
+Terminal=0
+Name=KEdit
+Name[af]=Kredigeer
+Name[eo]=Redaktilo
+Name[hr]=Uređivač
+Name[lv]=KRediģēt
+Name[pl]=Edytor
+Name[sv]=Kedit
+Name[th]=แก้ไขข้อความ
+Name[ven]=U sengulusa ha K
+Name[xh]=Abahleli Be K
+Name[zh_TW]=KDE 編輯器
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/Kfind.desktop b/menu/tests/data/Kfind.desktop
new file mode 100644
index 0000000..8984f18
--- /dev/null
+++ b/menu/tests/data/Kfind.desktop
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Encoding=UTF-8
+Exec=kfind %f
+Icon=kfind
+DocPath=kfind/index.html
+TerminalOptions=
+Path=
+Type=Application
+Terminal=0
+Name=Find Files
+Name[af]=Soek Lêers
+Name[be]=Шукаць файлы
+Name[bg]=Търсене на файлове
+Name[br]=Klask restroù
+Name[bs]=Pronađi datoteke
+Name[ca]=Cerca fitxers
+Name[cs]=Najít soubory
+Name[cy]=Canfod Ffeiliau
+Name[da]=Find filer
+Name[de]=Dateien suchen
+Name[el]=Εύρεση αρχείων
+Name[eo]=Trovu dosierojn
+Name[es]=KFind
+Name[et]=Failide otsimine
+Name[eu]=Fitxategiak Bilatu
+Name[fa]=یافتن پرونده‌ها
+Name[fi]=Etsi tiedostoja
+Name[fr]=Recherche de fichiers
+Name[gl]=Buscar Ficheiros
+Name[he]=חפש קבצים
+Name[hr]=Nađi datoteke
+Name[hu]=Fájlkeresés
+Name[id]=Cari Berkas
+Name[it]=Trova file
+Name[ja]=ファイルを検索
+Name[ko]=파일 찾기
+Name[lo]=ຄົ້ນຫາແຟ້ມ
+Name[lt]=Rasti bylas
+Name[lv]=Meklēt Failus
+Name[mn]=Файл хайх
+Name[mt]=Sib Fajls
+Name[nb]=Finn filer
+Name[nl]=Bestanden zoeken
+Name[nn]=Finn filer
+Name[nso]=Hwetsa Difaele
+Name[oc]=Cerca fiquièrs
+Name[pl]=Znajdź pliki
+Name[pt]=Procurar Ficheiros
+Name[pt_BR]=Encontrar arquivos
+Name[ro]=Caută fişiere
+Name[ru]=Поиск файлов
+Name[sk]=Hľadať súbory
+Name[sl]=Poišči datoteke
+Name[sr]=Pretraga fajlova
+Name[sv]=Hitta filer
+Name[ta]=§¸¡ôÒì ¸ñÎÀ¢Ê
+Name[th]=ค้นหาแฟ้ม
+Name[tr]=Dosyalarda Bul
+Name[uk]=Знайти файли
+Name[ven]=Todani faela
+Name[vi]=Tìm file
+Name[xh]=Fumana Iifayile
+Name[zh_CN]=查找文件
+Name[zh_TW]=尋找檔案
+Name[zu]=Thola Amafayela
+X-KDE-StartupNotify=true
diff --git a/menu/tests/data/apps.directory b/menu/tests/data/apps.directory
new file mode 100644
index 0000000..d54b4bd
--- /dev/null
+++ b/menu/tests/data/apps.directory
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Apps
+Name[af]=Programme
+Name[ar]=تطبيقات
+Name[az]=Proqram Tə'minatları
+Name[be]=Дастасаваньні
+Name[bg]=Приложения
+Name[br]=Arloadoù
+Name[bs]=Aplikacije
+Name[ca]=Aplicacions
+Name[cs]=Aplikace
+Name[da]=Øvrige programmer
+Name[de]=Programme
+Name[el]=Εφαρμογές
+Name[eo]=Aplikaĵoj
+Name[es]=Aplicaciones
+Name[et]=Rakendused
+Name[eu]=Aplikazioak
+Name[fa]=برنامه‌های کاربردی
+Name[fi]=Sovellukset
+Name[fo]=Nýtsluskipanir
+Name[gl]=Aplicacións
+Name[he]=יישומים
+Name[hr]=Programi
+Name[hu]=Alkalmazások
+Name[id]=Aplikasi
+Name[is]=Forrit
+Name[it]=Applicazioni
+Name[ja]=アプリケーション
+Name[ko]=응용 프로그램
+Name[lo]=ອັບພລິກເຄເຊິນ
+Name[lt]=Programos
+Name[lv]=Aplikācijas
+Name[mk]=Апликации
+Name[mt]=Applikazzjonijiet
+Name[nb]=Programmer
+Name[nl]=Programma's
+Name[nn]=Program
+Name[nso]=Ditshomiso
+Name[oc]=Aplicacions
+Name[pl]=Aplikacje
+Name[pt]=Aplicações
+Name[pt_BR]=Aplicativos
+Name[ro]=Aplicaţii
+Name[ru]=Приложения
+Name[se]=Prográmmat
+Name[sk]=Aplikácie
+Name[sl]=Uporabniški programi
+Name[sr]=Aplikacije
+Name[ss]=Ticelo
+Name[sv]=Program
+Name[ta]=ÀÂýÀ¡Î¸û
+Name[th]=แอพพลิเคชัน
+Name[tr]=Uygulamalar
+Name[uk]=Програми
+Name[ven]=Apulikhesheni
+Name[vi]=Chương trình
+Name[wa]=Programes
+Name[xh]=Izicelo
+Name[zh_CN]=应用程序
+Name[zh_TW]=應用程式
+Name[zu]=Abayaleli
+Type=Directory
+Icon=package_applications
+
diff --git a/menu/tests/data/freecell.desktop b/menu/tests/data/freecell.desktop
new file mode 100644
index 0000000..da6d251
--- /dev/null
+++ b/menu/tests/data/freecell.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=FreeCell
+Name[be]=Вольная ячэя
+Name[bg]=Свободна Клетка
+Name[bn]=ফ্রীসেল
+Name[ca]=FreeCell
+Name[cs]=FreeCell
+Name[da]=Napoleon
+Name[de]=FreeCell
+Name[el]=FreeCell
+Name[es]=FreeCell
+Name[et]=Freecell
+Name[fi]=Vapaakenttä
+Name[fr]=Freecell
+Name[ga]=FreeCell
+Name[gl]=FreeCell
+Name[he]=פריסל
+Name[hu]=FreeCell
+Name[it]=FreeCell
+Name[ja]=フリーセル
+Name[ko]=프리셀
+Name[lt]=FreeCell
+Name[lv]=FreeCell
+Name[mn]=Фриселл
+Name[ms]=FreeCell
+Name[nl]=FreeCell
+Name[no]=Freecell
+Name[pl]=FreeCell
+Name[pt]=Freecell
+Name[pt_BR]=FreeCell
+Name[ru]=Фриселл
+Name[sk]=FreeCell
+Name[sl]=FreeCell
+Name[sv]=Napoleon på S:t Helena
+Name[tr]=İskambil falı
+Name[uk]=Вільна комірка
+Name[vi]=FreeCell
+Name[wa]=Freecell
+Name[zh_CN]=空当接龙
+Name[zh_TW]=Freecell
+Comment=FreeCell game
+Comment[be]=Гульня ў Вольную ячэю
+Comment[bg]=игра Свободна Клетка
+Comment[bn]=ফ্রীসেল খেলা
+Comment[ca]=Joc del FreeCell
+Comment[cs]=Hra FreeCell
+Comment[da]=Kortspillet Napoleon
+Comment[de]=FreeCell-Spiel
+Comment[el]=Το παιχνίδι FreeCell
+Comment[es]=Juego FreeCell
+Comment[et]=Kaardimäng FreeCell
+Comment[fi]=Vapaakenttä-peli
+Comment[fr]=Jeu de cartes Freecell
+Comment[he]=משחק פריסל
+Comment[hu]=FreeCell játék
+Comment[it]=FreeCell
+Comment[ja]=フリーセルの GNOME 版
+Comment[lv]=FreeCell spēle
+Comment[mn]=GNOME Фриселл тоглоом
+Comment[ms]=Permainan FreeCell
+Comment[nl]=FreeCell Spel
+Comment[no]=Spillet FreeCell
+Comment[pl]=Gra FreeCell
+Comment[pt]=Jogo FreeCell
+Comment[pt_BR]=Jogo de FreeCell
+Comment[ru]=Пасьянс "Ячейка"
+Comment[sk]=Hra FreeCell
+Comment[sl]=Igra Freecell
+Comment[sv]=Spelet Napoleon på S:t Helena
+Comment[tr]=İskambil falı oyunu
+Comment[uk]=Гра "Вільна комірка"
+Comment[vi]=Trò chơi FreeCell
+Comment[zh_TW]=Freecell 紙牌接龍遊戲
+Exec=freecell
+Icon=gnome-cardgame.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;CardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=freecell
+StartupNotify=true
diff --git a/menu/tests/data/gataxx.desktop b/menu/tests/data/gataxx.desktop
new file mode 100644
index 0000000..10dfec6
--- /dev/null
+++ b/menu/tests/data/gataxx.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Encoding=UTF-8
+Icon=gataxx.png
+Name=Gataxx
+Name[am]=Gataxx
+Name[be]=Gataxx
+Name[bg]=Гатаксс
+Name[bn]=জীঅ্যাটাক্স
+Name[ca]=Gataxx
+Name[cs]=Gataxx
+Name[da]=Ataxx
+Name[de]=Gataxx
+Name[el]=Gataxx
+Name[es]=Gataxx
+Name[et]=Gataxx
+Name[fi]=Gataxx
+Name[fr]=Gataxx
+Name[he]=Gataxx
+Name[hu]=Gataxx
+Name[it]=Gataxx
+Name[ja]=Gataxx
+Name[ko]=Gataxx
+Name[lv]=Gataxx
+Name[mn]=Gataxx
+Name[ms]=Gataxx
+Name[nl]=Gataxx
+Name[no]=Gataxx
+Name[pl]=Gataxx
+Name[pt]=Gataxx
+Name[pt_BR]=Gataxx
+Name[ru]=Gataxx
+Name[sk]=Gataxx
+Name[sl]=Gataxx
+Name[sv]=Gataxx
+Name[tr]=Gataxx
+Name[uk]=Атаки
+Name[vi]=Gataxx
+Name[wa]=Gataxx
+Name[zh_CN]=Gataxx
+Name[zh_TW]=Gataxx
+Comment=Ataxx game
+Comment[be]=Гульня Ataxx
+Comment[bg]=Игра атаксс
+Comment[bn]=অ্যাটাক্স খেলা
+Comment[ca]=Joc del Ataxx
+Comment[cs]=Hra Ataxx
+Comment[da]=Spillet Ataxx
+Comment[de]=Ataxx-Spiel
+Comment[el]=Παιχνίδι Ataxx
+Comment[es]=Un juego tipo Ataxx.
+Comment[et]=Mäng nimega ataxx
+Comment[fi]=Ataxx-peli
+Comment[fr]=Jeu Ataxx
+Comment[he]=משחק Ataxx
+Comment[hu]=Ataxx játék
+Comment[it]=Gioco Ataxx
+Comment[ja]=Ataxx ゲーム
+Comment[lv]=Ataxx spēle
+Comment[mn]=Ataxx тоглоом.
+Comment[ms]=Permainan Ataxx
+Comment[nl]=Ataxx
+Comment[no]=Ataxx-spill
+Comment[pl]=Gra Ataxx
+Comment[pt]=Jogo Ataxx
+Comment[pt_BR]=Jogo Ataxx
+Comment[ru]=Игра ataxx
+Comment[sk]=Hra Ataxx
+Comment[sl]=Igra Ataxx
+Comment[sv]=Spelet Ataxx
+Comment[tr]=Ataxx oyunu
+Comment[uk]=Гра "Атаки"
+Comment[vi]=Trò chơi Ataxx.
+Comment[zh_TW]=Ataxx 遊戲
+Exec=gataxx
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;BoardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=gataxx
+StartupNotify=true
diff --git a/menu/tests/data/gideon-legacy.desktop b/menu/tests/data/gideon-legacy.desktop
new file mode 100644
index 0000000..4595087
--- /dev/null
+++ b/menu/tests/data/gideon-legacy.desktop
@@ -0,0 +1,12 @@
+[KDE Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kdevelop;
+Type=Application
+Exec=gideon %u
+MimeType=application/x-kdevelop;
+Icon=gideon
+DocPath=kdevelop/index.html
+Terminal=0
+Name=KDevelop 3.0
+Comment=IDE for C++/Qt/KDE
+X-DCOP-ServiceType=Multi
diff --git a/menu/tests/data/gideon.desktop b/menu/tests/data/gideon.desktop
new file mode 100644
index 0000000..1e11dc1
--- /dev/null
+++ b/menu/tests/data/gideon.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kdevelop;
+Type=Application
+Exec=gideon %u
+MimeType=application/x-kdevelop;
+Icon=gideon
+DocPath=kdevelop/index.html
+Terminal=0
+Name=KDevelop 3.0
+Comment=IDE for C++/Qt/KDE
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Development
diff --git a/menu/tests/data/glines-2.desktop b/menu/tests/data/glines-2.desktop
new file mode 100644
index 0000000..266747d
--- /dev/null
+++ b/menu/tests/data/glines-2.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Glines
+Name[be]=Glines
+Name[bg]=ГЛинии
+Name[bn]=জীলাইন্‌স
+Name[ca]=Glines
+Name[cs]=Glines
+Name[da]=Linjer
+Name[de]=Glines
+Name[el]=Glines
+Name[es]=Glines
+Name[et]=Glines
+Name[eu]=Glines
+Name[fi]=Glines
+Name[fr]=Glines
+Name[he]=Glines
+Name[hu]=Glines
+Name[it]=Glines
+Name[ja]=Glines
+Name[ko]=Glines
+Name[lt]=Glines
+Name[lv]=Glines
+Name[mn]=Glines
+Name[ms]=Glines
+Name[nl]=GLines
+Name[no]=Glines
+Name[pl]=Glines
+Name[pt]=GLinhas
+Name[pt_BR]=Glines
+Name[ro]=Glines
+Name[ru]=Glines
+Name[sk]=GČiary
+Name[sl]=Glines
+Name[sv]=Glinjer
+Name[tr]=Sıradaki toplar
+Name[uk]=Ряди
+Name[vi]=Glines
+Name[wa]=GRoyes
+Name[zh_CN]=Glines
+Name[zh_TW]=Glines
+Comment=Color lines game
+Comment[be]=Гульня "Каляровыя лініі"
+Comment[bg]=Игра Цветни линии
+Comment[bn]=কালার লাইন্‌স খেলা
+Comment[ca]=Joc de línies de color
+Comment[cs]=Hra Barevné linie
+Comment[da]=Farvede linjer-spil
+Comment[de]=Farblinienspiel
+Comment[el]=Παιχνίδι γραμμών χρώματος
+Comment[es]=Juego del tipo líneas de colores.
+Comment[fi]=Väriviivapeli
+Comment[fr]=Jeu de lignes colorées
+Comment[he]=משחק שורות צבעוניות
+Comment[hu]=Color lines játék
+Comment[it]=Gioco Color Lines
+Comment[ja]=カラーラインのゲーム
+Comment[lv]=Krāsu līniju spēle
+Comment[mn]=Өнгөт бөмбөлөг тоглоом
+Comment[ms]=Permainan garisan warna
+Comment[nl]=Kleurlijnen
+Comment[no]=Spill med fargelinjer
+Comment[pl]=Gra w kolorowe linie
+Comment[pt]=Jogo das linhas coloridas
+Comment[pt_BR]=Jogo de linhas coloridas
+Comment[ru]=Игра "Цветные линии"
+Comment[sk]=Hra Farebné čiary
+Comment[sl]=Igra barvnih črt
+Comment[sv]=Färglinjespel
+Comment[tr]=Color lines oyunu
+Comment[uk]=Гра "Кольорові ряди"
+Comment[vi]=Trò chơi ColorLines
+Comment[zh_TW]=Color lines 遊戲
+Exec=glines
+Icon=glines.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;PuzzleGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=glines
+StartupNotify=true
+NoDisplay=true
diff --git a/menu/tests/data/glines.desktop b/menu/tests/data/glines.desktop
new file mode 100644
index 0000000..5e5b465
--- /dev/null
+++ b/menu/tests/data/glines.desktop
@@ -0,0 +1,82 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Glines
+Name[be]=Glines
+Name[bg]=ГЛинии
+Name[bn]=জীলাইন্‌স
+Name[ca]=Glines
+Name[cs]=Glines
+Name[da]=Linjer
+Name[de]=Glines
+Name[el]=Glines
+Name[es]=Glines
+Name[et]=Glines
+Name[eu]=Glines
+Name[fi]=Glines
+Name[fr]=Glines
+Name[he]=Glines
+Name[hu]=Glines
+Name[it]=Glines
+Name[ja]=Glines
+Name[ko]=Glines
+Name[lt]=Glines
+Name[lv]=Glines
+Name[mn]=Glines
+Name[ms]=Glines
+Name[nl]=GLines
+Name[no]=Glines
+Name[pl]=Glines
+Name[pt]=GLinhas
+Name[pt_BR]=Glines
+Name[ro]=Glines
+Name[ru]=Glines
+Name[sk]=GČiary
+Name[sl]=Glines
+Name[sv]=Glinjer
+Name[tr]=Sıradaki toplar
+Name[uk]=Ряди
+Name[vi]=Glines
+Name[wa]=GRoyes
+Name[zh_CN]=Glines
+Name[zh_TW]=Glines
+Comment=Color lines game
+Comment[be]=Гульня "Каляровыя лініі"
+Comment[bg]=Игра Цветни линии
+Comment[bn]=কালার লাইন্‌স খেলা
+Comment[ca]=Joc de línies de color
+Comment[cs]=Hra Barevné linie
+Comment[da]=Farvede linjer-spil
+Comment[de]=Farblinienspiel
+Comment[el]=Παιχνίδι γραμμών χρώματος
+Comment[es]=Juego del tipo líneas de colores.
+Comment[fi]=Väriviivapeli
+Comment[fr]=Jeu de lignes colorées
+Comment[he]=משחק שורות צבעוניות
+Comment[hu]=Color lines játék
+Comment[it]=Gioco Color Lines
+Comment[ja]=カラーラインのゲーム
+Comment[lv]=Krāsu līniju spēle
+Comment[mn]=Өнгөт бөмбөлөг тоглоом
+Comment[ms]=Permainan garisan warna
+Comment[nl]=Kleurlijnen
+Comment[no]=Spill med fargelinjer
+Comment[pl]=Gra w kolorowe linie
+Comment[pt]=Jogo das linhas coloridas
+Comment[pt_BR]=Jogo de linhas coloridas
+Comment[ru]=Игра "Цветные линии"
+Comment[sk]=Hra Farebné čiary
+Comment[sl]=Igra barvnih črt
+Comment[sv]=Färglinjespel
+Comment[tr]=Color lines oyunu
+Comment[uk]=Гра "Кольорові ряди"
+Comment[vi]=Trò chơi ColorLines
+Comment[zh_TW]=Color lines 遊戲
+Exec=glines
+Icon=glines.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;PuzzleGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=glines
+StartupNotify=true
diff --git a/menu/tests/data/hidden.desktop b/menu/tests/data/hidden.desktop
new file mode 100644
index 0000000..dbbbff9
--- /dev/null
+++ b/menu/tests/data/hidden.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Hidden
+Comment=You shouldn't see this
+Exec=hidden
+Type=Application
+Categories=GNOME;Application;TextEditor;
+NoDisplay=true
diff --git a/menu/tests/data/hidden.directory b/menu/tests/data/hidden.directory
new file mode 100644
index 0000000..5f1e776
--- /dev/null
+++ b/menu/tests/data/hidden.directory
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Hidden
+Type=Directory
+NoDisplay=true
diff --git a/menu/tests/data/kate.desktop b/menu/tests/data/kate.desktop
new file mode 100644
index 0000000..3ca1c5d
--- /dev/null
+++ b/menu/tests/data/kate.desktop
@@ -0,0 +1,30 @@
+[Desktop Entry]
+Encoding=UTF-8
+GenericName=Advanced Text Editor
+GenericName[cy]=Golygydd Testun Uwch
+GenericName[da]= Avanceret teksteditor
+GenericName[el]=Προχωρημένος διορθωτής κειμένου
+GenericName[es]=Editor de texto avanzado
+GenericName[fa]=ویرایشگر متن پیشرفته
+GenericName[hu]=szövegszerkesztő
+GenericName[pt_BR]=Editor de Texto Avançado
+GenericName[sr]=Napredni editor teksta
+GenericName[sv]=Avancerad texteditor
+BinaryPattern=
+Name=Kate
+Name[ar]=كيت
+Name[bg]=Редактор Kate
+Name[eo]=Kodredaktilo
+Name[fa]=کِیت
+Name[ko]=카테
+Name[ru]=Редактор Kate
+MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/rdf;
+Exec=kate %u
+TerminalOptions=
+Icon=kate
+Path=
+DocPath=kate/index.html
+Type=Application
+Terminal=0
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/kbabel.desktop b/menu/tests/data/kbabel.desktop
new file mode 100644
index 0000000..2892cb8
--- /dev/null
+++ b/menu/tests/data/kbabel.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=KBabel
+Name[af]=Kbabel
+Name[ar]=المترجم
+Name[eo]=Babelo-tradukilo
+Name[ko]=K바벨
+Name[pt_BR]=Editor de POTFiles
+Name[sv]=Kbabel
+Name[xx]=xxKBabelxx
+Exec=kbabel %i %m -caption "%c" %U
+Icon=kbabel
+MiniIcon=kbabel
+Type=Application
+DocPath=kbabel/index.html
+MimeType=application/x-gettext;
+GenericName=Translation Tool
+GenericName[af]=Vertaling Program
+GenericName[ar]=أداة الترجمة
+GenericName[bg]=Инструменти за Превод
+GenericName[bs]=Alat za prevođenje
+GenericName[ca]=Eina de traducció
+GenericName[cs]=Překladatelský nástroj
+GenericName[cy]=Erfyn Cyfieithu
+GenericName[da]=Oversættelsesværktøj
+GenericName[de]=Übersetzungsprogramm
+GenericName[el]=Εργαλείο μετάφρασης
+GenericName[eo]=Tradukilo por Qt-programoj
+GenericName[es]=Herramienta de traducción
+GenericName[et]=Tõlkimise rakendus
+GenericName[eu]=Itzulpenerako Tresnak
+GenericName[fi]=Käännöstyökalu
+GenericName[fo]=Umsetingaramboð
+GenericName[fr]=Outil de traduction
+GenericName[he]=כלי תרגום
+GenericName[hr]=Uslužni program za prevođenje
+GenericName[hu]=segédprogram fordítóknak
+GenericName[it]=Strumento per le traduzioni
+GenericName[ja]=翻訳ツール
+GenericName[lt]=Vertimo įrankis
+GenericName[lv]=Tulkošanas Rīks
+GenericName[mt]=Għodda tat-traduzzjoni
+GenericName[nb]=Oversettingsverktøy
+GenericName[nl]=vertaalprogramma
+GenericName[nn]=Omsetjingsverktøy
+GenericName[nso]=Sebereka sa Thlathollo
+GenericName[pl]=Narzędzie dla tłumaczy
+GenericName[pt]=Ferramenta de Tradução
+GenericName[pt_BR]=Ferramenta de Tradução
+GenericName[ro]=Utilitar de traducere
+GenericName[ru]=Утилита локализации приложений
+GenericName[sk]=Prekladací nástroj
+GenericName[sl]=Orodje za prevajanje
+GenericName[sv]=Översättningsverktyg
+GenericName[ta]=¦Á¡Æ¢¦ÀÂ÷ôÒì ¸ÕÅ¢
+GenericName[th]=เครื่องมือแปลภาษา
+GenericName[tr]=Çeviri Aracı
+GenericName[uk]=Засіб для перекладів
+GenericName[ven]=Zwishumiswa zwau Dologa
+GenericName[vi]=Công cụ dịch
+GenericName[xh]=Isixhobo Soguqulelo lomsebenzi kolunye ulwimi
+GenericName[xx]=xxTranslation Toolxx
+GenericName[zh_CN]=翻译工具
+GenericName[zh_TW]=翻譯工具
+GenericName[zu]=Ithuluzi Lokuguqulela
+Terminal=0
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;Development
diff --git a/menu/tests/data/kedit-legacy.desktop b/menu/tests/data/kedit-legacy.desktop
new file mode 100644
index 0000000..fa9700e
--- /dev/null
+++ b/menu/tests/data/kedit-legacy.desktop
@@ -0,0 +1,33 @@
+[Desktop Entry]
+Encoding=UTF-8
+BinaryPattern=kedit;
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+GenericName=Simple Text Editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Jednoduchý textový editor
+GenericName[da]=Simpel teksteditor
+GenericName[es]=Editor de texto sencillo
+GenericName[fr]=Éditeur de texte élémentaire
+GenericName[pt_BR]=Editor de Texto Simples
+GenericName[sv]=Enkel texteditor
+GenericName[wa]=Simpe aspougneu di tecse
+Exec=kedit -caption "%c" %i %m %u
+Icon=kedit
+TerminalOptions=
+Path=
+DocPath=kedit/index.html
+Type=Application
+Terminal=0
+Name=KEdit
+Name[af]=Kredigeer
+Name[eo]=Redaktilo
+Name[hr]=Uređivač
+Name[lv]=KRediģēt
+Name[pl]=Edytor
+Name[sv]=Kedit
+Name[th]=แก้ไขข้อความ
+Name[ven]=U sengulusa ha K
+Name[xh]=Abahleli Be K
+Name[zh_TW]=KDE 編輯器
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
diff --git a/menu/tests/data/kwrite.desktop b/menu/tests/data/kwrite.desktop
new file mode 100644
index 0000000..9804fa6
--- /dev/null
+++ b/menu/tests/data/kwrite.desktop
@@ -0,0 +1,84 @@
+[Desktop Entry]
+Encoding=UTF-8
+GenericName=Text Editor
+GenericName[af]=Teks Redigeerder
+GenericName[ar]=محرر نصوص
+GenericName[be]=Тэкставы рэдактар
+GenericName[bg]=Текстов Редактор
+GenericName[bs]=Tekst editor
+GenericName[ca]=Editor de text
+GenericName[cs]=Textový editor
+GenericName[cy]=Golygydd Testun
+GenericName[da]= Teksteditor
+GenericName[de]=Texteditor
+GenericName[el]=Διορθωτής Κειμένου
+GenericName[eo]=Tekstredaktilo
+GenericName[es]=Editor de texto
+GenericName[et]=Tekstiredaktor
+GenericName[eu]=Testu Editorea
+GenericName[fa]=ویرایشگر متن
+GenericName[fi]=Tekstieditori
+GenericName[fo]=Tekstritil
+GenericName[fr]=Éditeur de texte
+GenericName[he]=עורך טקסט
+GenericName[hr]=Uređivač teksta
+GenericName[hu]=szövegszerkesztő
+GenericName[is]=Textaritill
+GenericName[it]=Editor di testi
+GenericName[ja]=テキストエディタ
+GenericName[ko]=글월 편집기
+GenericName[lo]=ເຄື່ອງມືແກ້ໄຂຂໍ້ຄວາມ
+GenericName[lt]=Teksto redaktorius
+GenericName[lv]=Teksta Redaktors
+GenericName[mn]=Текст боловсруулагч
+GenericName[mt]=Editur tat-test
+GenericName[nb]=Skriveprogram
+GenericName[nl]=teksteditor
+GenericName[nn]=Skriveprogram
+GenericName[nso]=Mofetosi wa Sengwalwana
+GenericName[pl]=Edytor tekstowy
+GenericName[pt]=Editor de Texto
+GenericName[pt_BR]=Editor de Texto
+GenericName[ro]=Editor de text
+GenericName[ru]=Текстовый редактор
+GenericName[sk]=Textový editor
+GenericName[sl]=Urejevalnik besedil
+GenericName[sr]=Editor teksta
+GenericName[ss]=Sihleli sembhalo
+GenericName[sv]=Texteditor
+GenericName[ta]=¯¨Ã ¦¾¡ÌôÀ¡Ç÷
+GenericName[th]=เครื่องมือแก้ไขข้อความ
+GenericName[tr]=Metin Düzenleyici
+GenericName[uk]=Редактор текстів
+GenericName[ven]=Musengulusi wa Manwalwa
+GenericName[vi]=Trình soạn văn bản
+GenericName[wa]=Aspougneu di tecse
+GenericName[xh]=Umhleli Wombhalo
+GenericName[zh_CN]=文本编辑器
+GenericName[zh_TW]=文字編輯器
+GenericName[zu]=Umlungisi wombhalo
+BinaryPattern=
+Name=KWrite
+Name[af]=Kskryf
+Name[ar]=كاتب كيدي
+Name[eo]=Simpla kodredaktilo
+Name[fa]=نوشتار K
+Name[fo]=KSkriva
+Name[lo]=Kwrite
+Name[lv]=KRakstīt
+Name[nso]=KNgwala
+Name[ru]=Редактор KWrite
+Name[sv]=Kwrite
+Name[ven]=Nwala ha K
+MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/x-diff;text/rdf;
+Exec=kwrite %u
+X-KDE-StartupNotify=true
+TerminalOptions=
+Icon=kwrite
+Path=
+DocPath=kwrite/index.html
+Type=Application
+Terminal=0
+InitialPreference=8
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;TextEditor
diff --git a/menu/tests/data/mahjongg-2.desktop b/menu/tests/data/mahjongg-2.desktop
new file mode 100644
index 0000000..95cfe27
--- /dev/null
+++ b/menu/tests/data/mahjongg-2.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Mahjongg
+Name[am]=ማህጆንግ
+Name[be]=Маджонг
+Name[bg]=Махджонг
+Name[bn]=মাহজং
+Name[ca]=Mahjongg
+Name[cs]=Mahjongg
+Name[da]=Mahjongg
+Name[de]=Mahjongg
+Name[el]=Mahjongg
+Name[es]=Mahjongg
+Name[et]=Mahjongg
+Name[fa]=ماهجونگ
+Name[fi]=Mahjongg
+Name[fr]=Mahjongg
+Name[gl]=Mahjongg
+Name[he]=Mahjongg
+Name[hu]=Mahjongg
+Name[it]=Mahjongg
+Name[ja]=GNOME 上海
+Name[ko]=그놈 마작
+Name[lt]=Mahjongg
+Name[lv]=Mahjongg
+Name[mn]=Маджонг
+Name[ms]=Mahjongg
+Name[nl]=Mahjongg
+Name[no]=Mahjongg
+Name[pl]=Mahjongg
+Name[pt]=Mahjongg
+Name[pt_BR]=Mahjongg
+Name[ru]=Маджонг
+Name[sk]=Mahjongg
+Name[sl]=Mahjongg
+Name[sv]=Mah Jong
+Name[tr]=Mahjongg
+Name[uk]=Магджонґ
+Name[vi]=Mahjongg
+Name[wa]=Mahjongg
+Name[zh_CN]=堆麻将
+Name[zh_TW]=上海麻將
+Comment=Mahjongg game
+Comment[am]=የማህጆንግ ጨዋታ
+Comment[be]=Гульня Маджонг
+Comment[bg]=Игра на Махджонг
+Comment[bn]=মাহজং খেলা
+Comment[ca]=Joc del Mahjongg
+Comment[cs]=Hra Mahjongg
+Comment[da]=Mahjonggspil
+Comment[de]=Mahjongg-Spiel
+Comment[el]=Παιχνίδι Mahjongg
+Comment[es]=Juego Mahjongg
+Comment[et]=Mäng nimega mahjongg
+Comment[fa]=بازی ماهجونگ
+Comment[fi]=Mahjongg-peli
+Comment[fr]=Jeu de Mahjongg
+Comment[he]=משחק Mahjongg
+Comment[hu]=Mahjongg játék
+Comment[it]=Gioco Mahjongg
+Comment[ja]=麻雀ゲーム
+Comment[lv]=Mahjongg spēle
+Comment[mn]=Mahjongg тоглоом
+Comment[ms]=Permainan Mahjongg
+Comment[nl]=Mahjongg
+Comment[no]=Mahjongg-spill
+Comment[pl]=Gra Mahjongg
+Comment[pt]=Jogo Mahjongg
+Comment[pt_BR]=Jogo Mahjongg
+Comment[ru]=Игра Маджонг
+Comment[sk]=Hra Mahjongg
+Comment[sl]=Igra Mahjongg
+Comment[sv]=Mah Jong-spel
+Comment[tr]=Mahjongg oyunu
+Comment[uk]=Гра "Магджонґ"
+Comment[vi]=Trò chơi Mahjongg
+Comment[zh_TW]=上海麻將遊戲
+Exec=mahjongg
+Icon=gnome-mahjongg.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Development;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=mahjongg
+StartupNotify=true
diff --git a/menu/tests/data/mahjongg.desktop b/menu/tests/data/mahjongg.desktop
new file mode 100644
index 0000000..0d5f5de
--- /dev/null
+++ b/menu/tests/data/mahjongg.desktop
@@ -0,0 +1,86 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Mahjongg
+Name[am]=ማህጆንግ
+Name[be]=Маджонг
+Name[bg]=Махджонг
+Name[bn]=মাহজং
+Name[ca]=Mahjongg
+Name[cs]=Mahjongg
+Name[da]=Mahjongg
+Name[de]=Mahjongg
+Name[el]=Mahjongg
+Name[es]=Mahjongg
+Name[et]=Mahjongg
+Name[fa]=ماهجونگ
+Name[fi]=Mahjongg
+Name[fr]=Mahjongg
+Name[gl]=Mahjongg
+Name[he]=Mahjongg
+Name[hu]=Mahjongg
+Name[it]=Mahjongg
+Name[ja]=GNOME 上海
+Name[ko]=그놈 마작
+Name[lt]=Mahjongg
+Name[lv]=Mahjongg
+Name[mn]=Маджонг
+Name[ms]=Mahjongg
+Name[nl]=Mahjongg
+Name[no]=Mahjongg
+Name[pl]=Mahjongg
+Name[pt]=Mahjongg
+Name[pt_BR]=Mahjongg
+Name[ru]=Маджонг
+Name[sk]=Mahjongg
+Name[sl]=Mahjongg
+Name[sv]=Mah Jong
+Name[tr]=Mahjongg
+Name[uk]=Магджонґ
+Name[vi]=Mahjongg
+Name[wa]=Mahjongg
+Name[zh_CN]=堆麻将
+Name[zh_TW]=上海麻將
+Comment=Mahjongg game
+Comment[am]=የማህጆንግ ጨዋታ
+Comment[be]=Гульня Маджонг
+Comment[bg]=Игра на Махджонг
+Comment[bn]=মাহজং খেলা
+Comment[ca]=Joc del Mahjongg
+Comment[cs]=Hra Mahjongg
+Comment[da]=Mahjonggspil
+Comment[de]=Mahjongg-Spiel
+Comment[el]=Παιχνίδι Mahjongg
+Comment[es]=Juego Mahjongg
+Comment[et]=Mäng nimega mahjongg
+Comment[fa]=بازی ماهجونگ
+Comment[fi]=Mahjongg-peli
+Comment[fr]=Jeu de Mahjongg
+Comment[he]=משחק Mahjongg
+Comment[hu]=Mahjongg játék
+Comment[it]=Gioco Mahjongg
+Comment[ja]=麻雀ゲーム
+Comment[lv]=Mahjongg spēle
+Comment[mn]=Mahjongg тоглоом
+Comment[ms]=Permainan Mahjongg
+Comment[nl]=Mahjongg
+Comment[no]=Mahjongg-spill
+Comment[pl]=Gra Mahjongg
+Comment[pt]=Jogo Mahjongg
+Comment[pt_BR]=Jogo Mahjongg
+Comment[ru]=Игра Маджонг
+Comment[sk]=Hra Mahjongg
+Comment[sl]=Igra Mahjongg
+Comment[sv]=Mah Jong-spel
+Comment[tr]=Mahjongg oyunu
+Comment[uk]=Гра "Магджонґ"
+Comment[vi]=Trò chơi Mahjongg
+Comment[zh_TW]=上海麻將遊戲
+Exec=mahjongg
+Icon=gnome-mahjongg.png
+Terminal=false
+Type=Application
+Categories=GNOME;Application;Game;BoardGame;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-games
+X-GNOME-Bugzilla-Component=mahjongg
+StartupNotify=true
diff --git a/menu/tests/data/quanta.desktop b/menu/tests/data/quanta.desktop
new file mode 100644
index 0000000..4b23aef
--- /dev/null
+++ b/menu/tests/data/quanta.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Quanta Plus
+Exec=quanta
+Icon=quanta
+Type=Application
+MimeType=text/html
+DocPath=quanta/index.html
+Comment=Web Development Environment
+Categories=Qt;KDE;Development
diff --git a/menu/tests/expand b/menu/tests/expand
new file mode 100755
index 0000000..5dbaac8
--- /dev/null
+++ b/menu/tests/expand
@@ -0,0 +1,12 @@
+#!/usr/bin/perl
+
+# This script performs expansion of environment variables of the form ${HOME}
+
+while(<>)
+{
+ while (($a) = ($_ =~ /[^\$]*\$\{([^\}]*)\}.*/))
+ {
+ s/([^\$]*)(\$\{$a\})(.*)/$1$ENV{$a}$3/;
+ }
+ printf $_;
+} \ No newline at end of file
diff --git a/menu/tests/menutest b/menu/tests/menutest
new file mode 100755
index 0000000..9d37246
--- /dev/null
+++ b/menu/tests/menutest
@@ -0,0 +1,200 @@
+#!/bin/bash
+# these vars are usable *only* for debugging purposes; they're not allowed as part of the spec, thus don't use them.
+# set MENU_FAKE_PREFIX to a non empty val to force testing for if things would succeed if XDG_MENU_PREFIX were
+# implemented
+# set MENU_FAKE_APPLICATIONS to force a work around for applications-merged postfixing.
+#
+function installData()
+{
+ local DIR="$1"
+ shift
+ mkdir -p "${DIR}"
+ for file in $*; do
+ cp "data/${file}" "${DIR}"
+ WIPE[$WIPE_IDX]="${DIR}/${file}"
+ WIPE_IDX=$(( $WIPE_IDX + 1 ))
+ done
+}
+
+function installDataAs()
+{
+ local DIR="$1"
+ mkdir -p "${DIR}"
+ cp "data/$2" "${DIR}/$3"
+ WIPE[${WIPE_IDX}]="${DIR}/${3}"
+ WIPE_IDX=$(( $WIPE_IDX + 1 ))
+}
+
+setup_local_xdg_vars() {
+ export XDG_CONFIG_HOME="${MENUTESTDIR}/xdg_config_home"
+ export XDG_DATA_HOME="${MENUTESTDIR}/xdg_data_home"
+ export XDG_CONFIG_DIR="${MENUTESTDIR}/xdg_config_dir"
+ export XDG_CONFIG_DIRS="$XDG_CONFIG_DIR:${XDG_CONFIG_DIRS}2"
+ export XDG_DATA_DIR="${MENUTESTDIR}/xdg_data_dir"
+ export XDG_DATA_DIRS="$XDG_DATA_DIR:${XDG_DATA_DIR}2"
+ export XDG_CACHE_HOME="${MENUTESTDIR}/xdg_cache_home"
+}
+
+setup_xdg_system_data_vars()
+{
+ export XDG_CACHE_HOME="${XDG_DATA_HOME:-${HOME}/.cache}"
+ export XDG_DATA_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}"
+ export XDG_CONFIG_DIR="${XDG_CONFIG_DIR:-/etc/xdg}"
+ if [ -z "${XDG_DATA_DIRS}" ]; then
+ export XDG_DATA_DIRS="/usr/local/share:/usr/share"
+ fi
+ export XDG_DATA_DIRS="${XDG_DATA_DIRS}:${MENUTESTDIR}/xdg_cache_dir"
+ export XDG_DATA_DIR="${XDG_DATA_DIRS//*:}"
+ if [ -z "${XDG_CONFIG_DIRS}" ]; then
+ export XDG_CONFIG_DIRS="/etc/xdg"
+ fi
+ export XDG_CONFIG_DIR="${XDG_CONFIG_DIRS/:*}"
+}
+
+run_test() {
+ if [ -z "$1" ]; then
+ echo "requires name of test directory to run"
+ exit 1
+ fi
+ local TEST="$1"
+ rm -rf "${MENUTESTDIR}" 2> /dev/null
+ mkdir "${MENUTESTDIR}"
+ RESULT="${TEST}/result"
+
+ (
+ unset WIPE WIPE_IDX
+ declare -a WIPE
+ declare -i WIPE_IDX=0
+ unset MODE
+
+ . ${TEST}/test
+ echo ">>> Running test ${TEST}, purpose $TEST_PURPOSE"
+
+ if [ "${MODE:-local}" == "local" ]; then
+ setup_local_xdg_vars
+ elif [ "${MODE}" == "system_data" ]; then
+ setup_xdg_system_data_vars
+ else
+ echo "!!! unknown MODE from $TEST, bailing"
+ exit -1
+ fi
+
+ test_code
+
+ declare -i IDX=0
+ while [ $WIPE_IDX -gt $IDX ]; do
+ echo "${WIPE[$IDX]}" >> "${MENUTESTDIR}/wipe"
+ IDX=$(( $IDX + 1 ))
+ done
+
+ DEBUG_OVERRIDES=''
+ [ -n "$MENU_FAKE_PREFIX" ] && DEBUG_OVERRIDES=.menu
+ [ -n "$MENU_FAKE_APPLICATIONS" ] && DEBUG_OVERIDES="${DEBUG_OVERRIDES} -merged"
+
+ for x in dir home; do
+ for y in ${DEBUG_OVERRIDES}; do
+ if [ -e "${MENUTESTDIR}/xdg_config_${x}/menus/applications${y}" ]; then
+ ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/kde-applications${y}"
+ ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/gnome-applications${y}"
+ fi
+ done
+ unset y
+ done
+ unset x DEBUG_OVERRIDES
+
+ $MENUTEST > ${MENUTESTDIR}/run-result 2> ${MENUTESTDIR}/log
+
+ if [ -e "${RESULT}" ]; then
+ ./expand "${RESULT}" > "${MENUTESTDIR}/required-result"
+ fi
+
+ if [ "$(type -t interpret_results)" == "function" ]; then
+ interpret_results
+ else
+ default_interpret_results
+ fi
+ ret=$?
+ if [ -e "${MENUTESTDIR}/wipe" ]; then
+ cat "${MENUTESTDIR}/wipe" | while read l; do
+ [ -z "$l" ] && continue
+ rm "$l"
+ done
+ fi
+ return $ret
+ )
+}
+
+default_interpret_results() {
+ if [ ! -e "${RESULT}" ]; then
+ echo "!!! Result file (${RESULT}) for ${TEST} missing"
+ echo '>>> Failed'
+ return 1
+ elif diff -q "${MENUTESTDIR}/run-result" "${MENUTESTDIR}/required-result" > /dev/null; then
+ echo '>>> OK'
+ return 0
+ fi
+ sort "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.sorted"
+ sort "${MENUTESTDIR}/required-result" > "${MENUTESTDIR}/required-result.sorted"
+ if diff -u "${MENUTESTDIR}/run-result.sorted" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then
+ echo '>>> OK (different order)'
+ return 0
+ fi
+ grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.filtered" 2> /dev/null
+ if diff -q "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result" > /dev/null; then
+ echo '>>> OK (additional system items)'
+ return 0
+ fi
+ grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result.sorted" > "${MENUTESTDIR}/required-result.filtered" 2> /dev/null
+ if diff -u "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then
+ echo '>>> OK (different order, additional system items)'
+ return 0
+ fi
+ echo '>>> Failed'
+ cat "${MENUTESTDIR}/result.diff"
+ cat "${MENUTESTDIR}/log"
+ return 1
+}
+
+if [ -z "${TESTS}" ]; then
+ export TESTS=`ls tests/*/test | sed -e 's:^\(\./\)\?tests/\+::' -e 's:/\+test$::'`
+fi
+
+if [ -z "$TET_RUN" ]; then
+
+ if [ "x${MENUTEST}" == "x" ]; then
+ echo 'To run the test set $MENUTEST to your menu-spec implementation.'
+ exit 1
+ fi
+
+ if [ "x${MENUTESTDIR}" == "x" ]; then
+ MENUTESTDIR=/tmp/menutestdir
+ echo Using ${MENUTESTDIR} as test directory, override with \$MENUTESTDIR.
+ else
+ echo Using ${MENUTESTDIR} as test directory.
+ fi
+
+ export MENUTESTDIR
+ export USER=${USER:-test}
+
+
+ FAILED=
+ SUCCEEDED=
+
+ for TESTCASE in ${TESTS}; do
+ if [ "${TESTCASE}" == "CVS" ]; then
+ continue
+ fi
+ echo
+ if ! run_test "tests/${TESTCASE}" ${MODE}; then
+ FAILED="${FAILED} ${TESTCASE}"
+ else
+ SUCCEEDED="${SUCCEEDED} ${TESTCASE}"
+ fi
+ done
+
+ echo "OK tests: ${SUCCEEDED}"
+ [ -n "${FAILED}" ] && echo "Failed tests: ${FAILED}"
+ echo "$(echo ${SUCCEEDED} | wc -w) tests passed, $(echo ${FAILED} | wc -w) tests failed"
+ [ -z "${FAILED}" ] && exit 0
+ exit 1
+fi
diff --git a/menu/tests/tests/All/result b/menu/tests/tests/All/result
new file mode 100644
index 0000000..fc75a82
--- /dev/null
+++ b/menu/tests/tests/All/result
@@ -0,0 +1,4 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/All/test b/menu/tests/tests/All/test
new file mode 100644
index 0000000..e97bddc
--- /dev/null
+++ b/menu/tests/tests/All/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="<All> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/And/result b/menu/tests/tests/And/result
new file mode 100644
index 0000000..dc159fa
--- /dev/null
+++ b/menu/tests/tests/And/result
@@ -0,0 +1 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/And/test b/menu/tests/tests/And/test
new file mode 100644
index 0000000..31571db
--- /dev/null
+++ b/menu/tests/tests/And/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<And> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <And>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </And>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/AppDir-relative/result b/menu/tests/tests/AppDir-relative/result
new file mode 100644
index 0000000..9e1d622
--- /dev/null
+++ b/menu/tests/tests/AppDir-relative/result
@@ -0,0 +1,3 @@
+Applications/ KEdit.desktop ${XDG_CONFIG_DIR}/menus/apps/KEdit.desktop
+Applications/ kate.desktop ${XDG_CONFIG_DIR}/menus/apps/kate.desktop
+Applications/ kwrite.desktop ${XDG_CONFIG_DIR}/menus/apps/kwrite.desktop
diff --git a/menu/tests/tests/AppDir-relative/test b/menu/tests/tests/AppDir-relative/test
new file mode 100644
index 0000000..75bd201
--- /dev/null
+++ b/menu/tests/tests/AppDir-relative/test
@@ -0,0 +1,27 @@
+TEST_PURPOSE="<AppDir> relative path tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <AppDir>apps</AppDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installDataAs "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop should_be_ignored.notdesktop
+}
diff --git a/menu/tests/tests/AppDir/test b/menu/tests/tests/AppDir/test
new file mode 100644
index 0000000..8faff91
--- /dev/null
+++ b/menu/tests/tests/AppDir/test
@@ -0,0 +1,4 @@
+. tests/AppDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+TEST_PURPOSE="<AppDir> absolute path"
+
diff --git a/menu/tests/tests/Category/result b/menu/tests/tests/Category/result
new file mode 100644
index 0000000..73b00c9
--- /dev/null
+++ b/menu/tests/tests/Category/result
@@ -0,0 +1,3 @@
+Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Category/test b/menu/tests/tests/Category/test
new file mode 100644
index 0000000..3ee0777
--- /dev/null
+++ b/menu/tests/tests/Category/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Category> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ <!-- note it's lowercase, this is intentional to verify
+ it's a case sensitive implementation -->
+ <Category>application</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+# Install .desktop files, freecell is daft but intentional to verify category support is case sensitive
+installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop
+}
diff --git a/menu/tests/tests/DefaultMergeDirs/result b/menu/tests/tests/DefaultMergeDirs/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/DefaultMergeDirs/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/DefaultMergeDirs/test b/menu/tests/tests/DefaultMergeDirs/test
new file mode 100644
index 0000000..3b570f2
--- /dev/null
+++ b/menu/tests/tests/DefaultMergeDirs/test
@@ -0,0 +1,43 @@
+TEST_PURPOSE="<DefaultMergeDirs> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <DefaultMergeDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/Deleted/result b/menu/tests/tests/Deleted/result
new file mode 100644
index 0000000..c121419
--- /dev/null
+++ b/menu/tests/tests/Deleted/result
@@ -0,0 +1,2 @@
+BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Deleted/test b/menu/tests/tests/Deleted/test
new file mode 100644
index 0000000..db7a413
--- /dev/null
+++ b/menu/tests/tests/Deleted/test
@@ -0,0 +1,35 @@
+TEST_PURPOSE="<Deleted> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <Deleted/>
+ <NotDeleted/>
+ <Deleted/>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/DesktopFileID/result b/menu/tests/tests/DesktopFileID/result
new file mode 100644
index 0000000..9629384
--- /dev/null
+++ b/menu/tests/tests/DesktopFileID/result
@@ -0,0 +1,4 @@
+Applications/ company-games-freecell.desktop ${XDG_DATA_DIR}/applications/company/games/freecell.desktop
+Applications/ company-games-gataxx.desktop ${XDG_DATA_DIR}/applications/company/games/gataxx.desktop
+Applications/ company-games-glines.desktop ${XDG_DATA_DIR}/applications/company/games/glines.desktop
+Applications/ company-games-mahjongg.desktop ${XDG_DATA_DIR}/applications/company/games/mahjongg.desktop
diff --git a/menu/tests/tests/DesktopFileID/test b/menu/tests/tests/DesktopFileID/test
new file mode 100644
index 0000000..ae2c679
--- /dev/null
+++ b/menu/tests/tests/DesktopFileID/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="DesktopFileIDs in submenus"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications/company/games gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Directory/result b/menu/tests/tests/Directory/result
new file mode 100644
index 0000000..2cd1d1f
--- /dev/null
+++ b/menu/tests/tests/Directory/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Directory/test b/menu/tests/tests/Directory/test
new file mode 100644
index 0000000..fe7e126
--- /dev/null
+++ b/menu/tests/tests/Directory/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Directory> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/DirectoryDir-relative/result b/menu/tests/tests/DirectoryDir-relative/result
new file mode 100644
index 0000000..2cd1d1f
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir-relative/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/DirectoryDir-relative/test b/menu/tests/tests/DirectoryDir-relative/test
new file mode 100644
index 0000000..4a5e039
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir-relative/test
@@ -0,0 +1,28 @@
+TEST_PURPOSE="relative <DirectoryDir> tag ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DirectoryDir>${PATH_EXPANSION}desktop-directories</DirectoryDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+ installData ${XDG_CONFIG_DIR}/menus/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/DirectoryDir/test b/menu/tests/tests/DirectoryDir/test
new file mode 100644
index 0000000..dc8bb3b
--- /dev/null
+++ b/menu/tests/tests/DirectoryDir/test
@@ -0,0 +1,4 @@
+. tests/DirectoryDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+TEST_PURPOSE="<DirectoryDir> absolute path"
+
diff --git a/menu/tests/tests/Exclude/result b/menu/tests/tests/Exclude/result
new file mode 100644
index 0000000..dfbb122
--- /dev/null
+++ b/menu/tests/tests/Exclude/result
@@ -0,0 +1,3 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Exclude/test b/menu/tests/tests/Exclude/test
new file mode 100644
index 0000000..728ec99
--- /dev/null
+++ b/menu/tests/tests/Exclude/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="<Exclude> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Or>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </Or>
+ </Include>
+ <Exclude>
+ <Filename>glines.desktop</Filename>
+ </Exclude>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Filename/result b/menu/tests/tests/Filename/result
new file mode 100644
index 0000000..dc159fa
--- /dev/null
+++ b/menu/tests/tests/Filename/result
@@ -0,0 +1 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/Filename/test b/menu/tests/tests/Filename/test
new file mode 100644
index 0000000..b9d021d
--- /dev/null
+++ b/menu/tests/tests/Filename/test
@@ -0,0 +1,26 @@
+TEST_PURPOSE="<Filename> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Filename>freecell.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/LegacyDir-Move/result b/menu/tests/tests/LegacyDir-Move/result
new file mode 100644
index 0000000..9a2b9db
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-Move/result
@@ -0,0 +1,2 @@
+Editors/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop
+/ Home.desktop ${LEGACY_DIR}/Home.desktop
diff --git a/menu/tests/tests/LegacyDir-Move/test b/menu/tests/tests/LegacyDir-Move/test
new file mode 100644
index 0000000..7e6e85e
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-Move/test
@@ -0,0 +1,39 @@
+TEST_PURPOSE="move entries from <LegacyDir>"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Development</Name>
+ <Exclude>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Exclude>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${LEGACY_DIR} Home.desktop
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop
+}
diff --git a/menu/tests/tests/LegacyDir-relative/result b/menu/tests/tests/LegacyDir-relative/result
new file mode 100644
index 0000000..0c2af9b
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-relative/result
@@ -0,0 +1,9 @@
+Development/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop
+Development/ kbabel.desktop ${LEGACY_DIR}/Development/kbabel.desktop
+Development/ quanta.desktop ${LEGACY_DIR}/Development/quanta.desktop
+Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+/ Help.desktop ${LEGACY_DIR}/Help.desktop
+/ Home.desktop ${LEGACY_DIR}/Home.desktop
+/ Kfind.desktop ${LEGACY_DIR}/Kfind.desktop
diff --git a/menu/tests/tests/LegacyDir-relative/test b/menu/tests/tests/LegacyDir-relative/test
new file mode 100644
index 0000000..5082590
--- /dev/null
+++ b/menu/tests/tests/LegacyDir-relative/test
@@ -0,0 +1,42 @@
+TEST_PURPOSE="Simple <LegacyDir> test"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Editors</Name>
+ <Directory>kde-editors.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Directory>kde-development.directory</Directory>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop kbabel.desktop quanta.desktop
+ installData ${LEGACY_DIR} Kfind.desktop Home.desktop Help.desktop
+}
+
diff --git a/menu/tests/tests/Merge-combined/result b/menu/tests/tests/Merge-combined/result
new file mode 100644
index 0000000..999bd1e
--- /dev/null
+++ b/menu/tests/tests/Merge-combined/result
@@ -0,0 +1 @@
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/Merge-combined/test b/menu/tests/tests/Merge-combined/test
new file mode 100644
index 0000000..07cfab3
--- /dev/null
+++ b/menu/tests/tests/Merge-combined/test
@@ -0,0 +1,46 @@
+TEST_PURPOSE="Merge Two Menus and a Legacy Menu"
+
+test_code() {
+ LEGACY_DIR=${MENUTESTDIR}/legacy_applnk
+ export LEGACY_DIR
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+ <LegacyDir>${LEGACY_DIR}</LegacyDir>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>kate.desktop</Filename>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>KEdit.desktop</Filename>
+ </Include>
+ <Deleted/>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${LEGACY_DIR}/Development gideon-legacy.desktop
+ installData ${XDG_DATA_DIR}/applications kate.desktop kwrite.desktop KEdit.desktop
+}
diff --git a/menu/tests/tests/MergeDir-absolute/test b/menu/tests/tests/MergeDir-absolute/test
new file mode 100644
index 0000000..db6c895
--- /dev/null
+++ b/menu/tests/tests/MergeDir-absolute/test
@@ -0,0 +1,3 @@
+. tests/MergeDir-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+export TEST_PURPOSE="<MergeDir> absolute path test"
diff --git a/menu/tests/tests/MergeDir-relative/result b/menu/tests/tests/MergeDir-relative/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeDir-relative/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeDir-relative/test b/menu/tests/tests/MergeDir-relative/test
new file mode 100644
index 0000000..ba96b1a
--- /dev/null
+++ b/menu/tests/tests/MergeDir-relative/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeDir> relative path ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p "${XDG_CONFIG_DIR}/menus"
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications.menu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeDir>${PATH_EXPANSION}applications-merged</MergeDir>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir "${XDG_CONFIG_DIR}/menus/applications-merged/"
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/test.menu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # intentional crap entry to verify it does _not_ get picked up
+ ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/dar.notmenu" <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>testing</Name>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+
+ # Install .desktop files
+ installData "${XDG_DATA_DIR}/applications" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile-absolute/test b/menu/tests/tests/MergeFile-absolute/test
new file mode 100644
index 0000000..24cf669
--- /dev/null
+++ b/menu/tests/tests/MergeFile-absolute/test
@@ -0,0 +1,3 @@
+. tests/MergeFile-relative/test
+export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/'
+export TEST_PURPOSE="<MergeFile> absolute path"
diff --git a/menu/tests/tests/MergeFile-parent/result b/menu/tests/tests/MergeFile-parent/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-parent/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-parent/test b/menu/tests/tests/MergeFile-parent/test
new file mode 100644
index 0000000..00ed006
--- /dev/null
+++ b/menu/tests/tests/MergeFile-parent/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_HOME}/menus
+ ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile type="parent">${XDG_CONFIG_DIR}/menus/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/MergeFile-path/result b/menu/tests/tests/MergeFile-path/result
new file mode 100644
index 0000000..fbe635e
--- /dev/null
+++ b/menu/tests/tests/MergeFile-path/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/MergeFile-path/test b/menu/tests/tests/MergeFile-path/test
new file mode 100644
index 0000000..97daa5c
--- /dev/null
+++ b/menu/tests/tests/MergeFile-path/test
@@ -0,0 +1,61 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_HOME}/menus
+ ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile type="path">${XDG_CONFIG_DIR}/menus/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/MergeFile-recursive/result b/menu/tests/tests/MergeFile-recursive/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-recursive/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-recursive/test b/menu/tests/tests/MergeFile-recursive/test
new file mode 100644
index 0000000..4dbab21
--- /dev/null
+++ b/menu/tests/tests/MergeFile-recursive/test
@@ -0,0 +1,58 @@
+TEST_PURPOSE="test elaborate recursive look in <MergeFile>s"
+
+test_code(){
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+
+ <MergeFile>../test.menu</MergeFile>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile-relative/result b/menu/tests/tests/MergeFile-relative/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile-relative/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile-relative/test b/menu/tests/tests/MergeFile-relative/test
new file mode 100644
index 0000000..c3478db
--- /dev/null
+++ b/menu/tests/tests/MergeFile-relative/test
@@ -0,0 +1,44 @@
+TEST_PURPOSE="<MergeFile> tag relative path"
+
+test_code(){
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>${PATH_EXPANSION}applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+<!-- test -->
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile2/result b/menu/tests/tests/MergeFile2/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile2/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile2/test b/menu/tests/tests/MergeFile2/test
new file mode 100644
index 0000000..e5b1591
--- /dev/null
+++ b/menu/tests/tests/MergeFile2/test
@@ -0,0 +1,57 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the use of relative paths in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>applications-merged/test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/MergeFile3/result b/menu/tests/tests/MergeFile3/result
new file mode 100644
index 0000000..9e17189
--- /dev/null
+++ b/menu/tests/tests/MergeFile3/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/MergeFile3/test b/menu/tests/tests/MergeFile3/test
new file mode 100644
index 0000000..8781888
--- /dev/null
+++ b/menu/tests/tests/MergeFile3/test
@@ -0,0 +1,56 @@
+TEST_PURPOSE="<MergeFile> tag ..."
+
+test_code() {
+ # Tests the use of relative paths in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <MergeFile>test.menu</MergeFile>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ ./expand > ${XDG_CONFIG_DIR}/menus/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <MergeFile>extra/test.menu</MergeFile>
+</Menu>
+EOF
+
+ mkdir ${XDG_CONFIG_DIR}/menus/extra/
+ ./expand > ${XDG_CONFIG_DIR}/menus/extra/test.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/Move-collapsing/result b/menu/tests/tests/Move-collapsing/result
new file mode 100644
index 0000000..10ccec6
--- /dev/null
+++ b/menu/tests/tests/Move-collapsing/result
@@ -0,0 +1,4 @@
+Games/BoardGame/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Games/BoardGame/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/Move-collapsing/test b/menu/tests/tests/Move-collapsing/test
new file mode 100644
index 0000000..b842b0a
--- /dev/null
+++ b/menu/tests/tests/Move-collapsing/test
@@ -0,0 +1,40 @@
+TEST_PURPOSE="complicated <Move> operation"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <Move>
+ <Old>Games1</Old>
+ <New>Games</New>
+ </Move>
+
+ <Menu>
+ <Name>Games1</Name>
+ <Menu>
+ <Name>BoardGame</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <OnlyUnallocated/>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Move-ordering/result b/menu/tests/tests/Move-ordering/result
new file mode 100644
index 0000000..b941f58
--- /dev/null
+++ b/menu/tests/tests/Move-ordering/result
@@ -0,0 +1,3 @@
+Development/ gideon.desktop ${XDG_DATA_DIR}/applications/gideon.desktop
+Games-Correct/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Games-Correct/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
diff --git a/menu/tests/tests/Move-ordering/test b/menu/tests/tests/Move-ordering/test
new file mode 100644
index 0000000..f8883b9
--- /dev/null
+++ b/menu/tests/tests/Move-ordering/test
@@ -0,0 +1,49 @@
+TEST_PURPOSE="Order of <Move> operations ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <Menu>
+ <Name>Development</Name>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+ <Move>
+ <Old>Development/Games-New</Old>
+ <New>Games-Correct</New>
+ </Move>
+ <Move>
+ <Old>Development/Games</Old>
+ <New>Games-Wrong</New>
+ </Move>
+ <Menu>
+ <Name>Development</Name>
+ <Move>
+ <Old>Games</Old>
+ <New>Games-New</New>
+ </Move>
+ </Menu>
+</Menu>
+EOF
+
+ # Move operations in sub-menus should be performed first
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop gideon.desktop
+}
diff --git a/menu/tests/tests/Move-submenu/result b/menu/tests/tests/Move-submenu/result
new file mode 100644
index 0000000..f1c44ef
--- /dev/null
+++ b/menu/tests/tests/Move-submenu/result
@@ -0,0 +1 @@
+A/B/Development/ gideon-legacy.desktop ${XDG_DATA_DIR}/applications/gideon-legacy.desktop
diff --git a/menu/tests/tests/Move-submenu/test b/menu/tests/tests/Move-submenu/test
new file mode 100644
index 0000000..2751983
--- /dev/null
+++ b/menu/tests/tests/Move-submenu/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="Move into a new Submenu"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Filename>gideon-legacy.desktop</Filename>
+ </Include>
+ </Menu>
+ <Move>
+ <Old>Development</Old>
+ <New>A/B/Development</New>
+ </Move>
+</Menu>
+EOF
+
+ # Install .desktop files
+
+ installData ${XDG_DATA_DIR}/applications gideon-legacy.desktop
+}
diff --git a/menu/tests/tests/Move/result b/menu/tests/tests/Move/result
new file mode 100644
index 0000000..d9a802d
--- /dev/null
+++ b/menu/tests/tests/Move/result
@@ -0,0 +1,2 @@
+Games/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Games/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Move/test b/menu/tests/tests/Move/test
new file mode 100644
index 0000000..9f2d162
--- /dev/null
+++ b/menu/tests/tests/Move/test
@@ -0,0 +1,34 @@
+TEST_PURPOSE="simple <Move> operation"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+ <Move>
+ <Old>Foo</Old>
+ <New>Bar</New>
+ <Old>BoardGames</Old>
+ <New>Apps</New>
+ <Old>BoardGames</Old>
+ <New>Games</New>
+ </Move>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <Category>BoardGame</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/NoDisplay/result b/menu/tests/tests/NoDisplay/result
new file mode 100644
index 0000000..7ed7169
--- /dev/null
+++ b/menu/tests/tests/NoDisplay/result
@@ -0,0 +1 @@
+Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/NoDisplay/test b/menu/tests/tests/NoDisplay/test
new file mode 100644
index 0000000..48e94ea
--- /dev/null
+++ b/menu/tests/tests/NoDisplay/test
@@ -0,0 +1,37 @@
+TEST_PURPOSE="NoDisplay desktop entry values"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Shouldn't see this</Name>
+ <Directory>hidden.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+
+ <Menu>
+ <Name>Other</Name>
+ <OnlyUnallocated/>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories hidden.directory
+}
diff --git a/menu/tests/tests/NoDisplay2/result b/menu/tests/tests/NoDisplay2/result
new file mode 100644
index 0000000..7ed7169
--- /dev/null
+++ b/menu/tests/tests/NoDisplay2/result
@@ -0,0 +1 @@
+Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/NoDisplay2/test b/menu/tests/tests/NoDisplay2/test
new file mode 100644
index 0000000..62ba5a7
--- /dev/null
+++ b/menu/tests/tests/NoDisplay2/test
@@ -0,0 +1,38 @@
+TEST_PURPOSE="Allocation of desktop entry values from deleted menus"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Shouldn't see this</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ <Deleted/>
+ </Menu>
+
+ <Menu>
+ <Name>Other</Name>
+ <OnlyUnallocated/>
+ <Include>
+ <All/>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/NotOnlyUnallocated-default/result b/menu/tests/tests/NotOnlyUnallocated-default/result
new file mode 100644
index 0000000..0c398b7
--- /dev/null
+++ b/menu/tests/tests/NotOnlyUnallocated-default/result
@@ -0,0 +1,2 @@
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
diff --git a/menu/tests/tests/NotOnlyUnallocated-default/test b/menu/tests/tests/NotOnlyUnallocated-default/test
new file mode 100644
index 0000000..2f4956e
--- /dev/null
+++ b/menu/tests/tests/NotOnlyUnallocated-default/test
@@ -0,0 +1,33 @@
+TEST_PURPOSE="Another <OnlyUnallocated> test"
+
+test_code() {
+ # Tests <OnlyUnallocated>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Editors</Name>
+ <Include>
+ <Filename>kwrite.desktop</Filename>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop
+}
diff --git a/menu/tests/tests/OnlyUnallocated/result b/menu/tests/tests/OnlyUnallocated/result
new file mode 100644
index 0000000..8be4bc7
--- /dev/null
+++ b/menu/tests/tests/OnlyUnallocated/result
@@ -0,0 +1,3 @@
+BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
diff --git a/menu/tests/tests/OnlyUnallocated/test b/menu/tests/tests/OnlyUnallocated/test
new file mode 100644
index 0000000..efda0ff
--- /dev/null
+++ b/menu/tests/tests/OnlyUnallocated/test
@@ -0,0 +1,44 @@
+TEST_PURPOSE="<OnlyAllocated> tag"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>BoardGames</Name>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Not>
+ <Category>CardGame</Category>
+ </Not>
+ </And>
+ </Include>
+ <Exclude>
+ <Category>PuzzleGame</Category>
+ </Exclude>
+ </Menu>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ <OnlyUnallocated/>
+ <NotOnlyUnallocated/>
+ <OnlyUnallocated/>
+ </Menu>
+</Menu>
+EOF
+
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/Or/result b/menu/tests/tests/Or/result
new file mode 100644
index 0000000..fc75a82
--- /dev/null
+++ b/menu/tests/tests/Or/result
@@ -0,0 +1,4 @@
+Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop
+Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop
diff --git a/menu/tests/tests/Or/test b/menu/tests/tests/Or/test
new file mode 100644
index 0000000..cd10cfd
--- /dev/null
+++ b/menu/tests/tests/Or/test
@@ -0,0 +1,29 @@
+TEST_PURPOSE="<Or> Keyword"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Or>
+ <Filename>freecell.desktop</Filename>
+ <Category>Game</Category>
+ </Or>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop
+}
diff --git a/menu/tests/tests/boolean-logic/result b/menu/tests/tests/boolean-logic/result
new file mode 100644
index 0000000..ea47f01
--- /dev/null
+++ b/menu/tests/tests/boolean-logic/result
@@ -0,0 +1,3 @@
+Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
diff --git a/menu/tests/tests/boolean-logic/test b/menu/tests/tests/boolean-logic/test
new file mode 100644
index 0000000..dc045c4
--- /dev/null
+++ b/menu/tests/tests/boolean-logic/test
@@ -0,0 +1,36 @@
+TEST_PURPOSE="<And><Category>foo</Category><Not><Category>foo</Category></Not></And> shouldn't match anything"
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+
+ <DefaultAppDirs/>
+ <DefaultDirectoryDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Directory>apps.directory</Directory>
+ <Include>
+ <Or>
+ <Category>TextEditor</Category>
+ <And>
+ <Category>Game</Category>
+ <Not><Category>Game</Category></Not>
+ </And>
+ </Or>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop
+ installData ${XDG_DATA_DIR}/applications/test freecell.desktop
+ installData ${XDG_DATA_DIR}/desktop-directories apps.directory
+}
diff --git a/menu/tests/tests/desktop-name-collision/result b/menu/tests/tests/desktop-name-collision/result
new file mode 100644
index 0000000..e1e9221
--- /dev/null
+++ b/menu/tests/tests/desktop-name-collision/result
@@ -0,0 +1,3 @@
+Development/ kde-gideon.desktop ${XDG_DATA_HOME}/applications/kde-gideon.desktop
+Development/ mahjongg.desktop ${XDG_DATA_HOME}/applications/mahjongg.desktop
+Games/ freecell.desktop ${XDG_DATA_HOME}/applications/freecell.desktop
diff --git a/menu/tests/tests/desktop-name-collision/test b/menu/tests/tests/desktop-name-collision/test
new file mode 100644
index 0000000..46b5ded
--- /dev/null
+++ b/menu/tests/tests/desktop-name-collision/test
@@ -0,0 +1,53 @@
+TEST_PURPOSE=".desktop files with same name ..."
+
+test_code() {
+ # Tests the type attribute in <MergeFile>
+
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <DefaultAppDirs/>
+ <Menu>
+ <Name>Games</Name>
+ <Include>
+ <Category>Game</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Development</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop mahjongg.desktop
+
+ # freecell.desktop is installed twice, only the version under ${XDG_DATA_HOME}/applications
+ # should show up in the menu
+ # freecell.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installData ${XDG_DATA_HOME}/applications freecell.desktop
+
+ # ${XDG_DATA_HOME}/applications/glines.desktop has NoDisplay=true
+ # glines.desktop should not be shown
+ # glines.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installDataAs ${XDG_DATA_HOME}/applications glines-2.desktop glines.desktop
+
+ # ${XDG_DATA_HOME}/applications/mahjongg.desktop has Categories=Development
+ # mahjongg.desktop should be shown under the Development menu
+ # mahjongg.desktop in ${XDG_DATA_DIR}/applications should be ignored.
+ installDataAs ${XDG_DATA_HOME}/applications mahjongg-2.desktop mahjongg.desktop
+
+ # kde/gideon.desktop and kde-gideon.desktop are equivalent
+ # only the version under ${XDG_DATA_HOME}/applications should show up in the menu
+ # gideon.desktop in ${XDG_DATA_DIR}/applications/kde should be ignored.
+ installDataAs ${XDG_DATA_DIR}/applications/kde gideon.desktop
+ installDataAs ${XDG_DATA_HOME}/applications gideon.desktop kde-gideon.desktop
+}
diff --git a/menu/tests/tests/menu-multiple-matching/result b/menu/tests/tests/menu-multiple-matching/result
new file mode 100644
index 0000000..986c873
--- /dev/null
+++ b/menu/tests/tests/menu-multiple-matching/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/menu-multiple-matching/test b/menu/tests/tests/menu-multiple-matching/test
new file mode 100644
index 0000000..d8914ab
--- /dev/null
+++ b/menu/tests/tests/menu-multiple-matching/test
@@ -0,0 +1,36 @@
+TEST_PURPOSE="complicated rule ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <And>
+ <Category>Game</Category>
+ <Category>BoardGame</Category>
+ </And>
+ <Or>
+ <Category>TextEditor</Category>
+ <Filename>quanta.desktop</Filename>
+ </Or>
+ </Include>
+ <Exclude>
+ <Filename>mahjongg.desktop</Filename>
+ </Exclude>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tests/official-categories/categories.list b/menu/tests/tests/official-categories/categories.list
new file mode 100644
index 0000000..6e2198d
--- /dev/null
+++ b/menu/tests/tests/official-categories/categories.list
@@ -0,0 +1,10 @@
+AudioVideo
+Development
+Education
+Game
+Graphics
+Network
+Office
+Settings
+System
+Utility
diff --git a/menu/tests/tests/official-categories/test b/menu/tests/tests/official-categories/test
new file mode 100644
index 0000000..09621cd
--- /dev/null
+++ b/menu/tests/tests/official-categories/test
@@ -0,0 +1,73 @@
+TEST_PURPOSE="verify all required categories are supported"
+
+base_loc="tests/official-categories"
+
+test_code()
+{
+ local category
+ for category in $(< "${base_loc}/categories.list"); do
+ CATEGORY="${category}" ./expand "${base_loc}/unique-entry.desktop" > "data/${category}.desktop"
+ installData "${XDG_DATA_DIR}/applications" "${category}.desktop"
+ rm "data/${category}.desktop"
+ done
+}
+
+query()
+{
+ echo "$@"
+ ret=''
+ while [ -z "$ret" ]; do
+ echo -n "y/n? :"
+ read ret
+ if ! [ "$ret" == "y" -o "$ret" == "n" ]; then
+ echo "invalid response; must be 'y' or 'n'"
+ ret=''
+ fi
+ done
+ [ "$ret" == "y" ] && return 0
+ return 1
+}
+
+interpret_results()
+{
+ # inefficient, but works.
+ local missed=''
+ local correct=''
+ for category in $(< "${base_loc}/categories.list"); do
+ if grep "/${category}\.desktop" "${MENUTESTDIR}/run-result" > /dev/null; then
+ correct="${correct} ${category}"
+ else
+ missed="${missed} ${category}"
+ fi
+ done
+ if [ -z "${missed}" ]; then
+ echo ">>> OK"
+ return 0
+ fi
+ if [ "$(echo $missed)" != "Settings" ]; then
+ # failures.
+ cat "${MENUTESTDIR}/run-result"
+ echo "missed categories $missed"
+ echo "matched ${correct}"
+ echo ">>> Failed (missed $(echo $missed | wc -w) out of $(wc -l "${base_loc}/categories.list")"
+ return 1
+ fi
+ echo ">>> Settings failed; checking interactively"
+ local ret
+ if [ "$(id -u)" != "0" ]; then
+ echo ">>> Cannot go interactive due to test being ran as non-root; re-run as root"
+ return 1;
+ elif ! which xdg-desktop-menu &> /dev/null; then
+ echo ">>> xdg-desktop-menu is not available; cannot do interactive test"
+ return 1;
+ fi
+ xdg-desktop-menu install --mode system --novendor "${XDG_DATA_DIR}/applications/Settings.desktop"
+ (
+ query "Please check for a 'menu-spec-testing' in any gnome/kde system settings panel"
+ )
+ ret=$?
+ xdg-desktop-menu uninstall --mode system "${XDG_DATA_DIR}/applications/Settings.desktop"
+ return $(($ret))
+}
+
+MODE=system_data
diff --git a/menu/tests/tests/official-categories/unique-entry.desktop b/menu/tests/tests/official-categories/unique-entry.desktop
new file mode 100644
index 0000000..6ee3b88
--- /dev/null
+++ b/menu/tests/tests/official-categories/unique-entry.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=menu-spec-testing
+Exec=true
+Icon=quanta
+Type=Application
+MimeType=text/html
+Comment=menu-spec testing
+Categories=${CATEGORY};
diff --git a/menu/tests/tests/submenu-collision/result b/menu/tests/tests/submenu-collision/result
new file mode 100644
index 0000000..6aa47d7
--- /dev/null
+++ b/menu/tests/tests/submenu-collision/result
@@ -0,0 +1,5 @@
+Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop
+Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop
+Applications/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop
+Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop
+Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop
diff --git a/menu/tests/tests/submenu-collision/test b/menu/tests/tests/submenu-collision/test
new file mode 100644
index 0000000..7d29896
--- /dev/null
+++ b/menu/tests/tests/submenu-collision/test
@@ -0,0 +1,32 @@
+TEST_PURPOSE="two submenus with the same name ..."
+
+test_code() {
+ # Generate applications.menu
+ mkdir -p ${XDG_CONFIG_DIR}/menus
+ ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu <<EOF
+ <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+
+<Menu>
+ <Name>KDE</Name>
+ <!-- Search the default locations -->
+ <DefaultAppDirs/>
+
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>TextEditor</Category>
+ </Include>
+ </Menu>
+ <Menu>
+ <Name>Applications</Name>
+ <Include>
+ <Category>Development</Category>
+ </Include>
+ </Menu>
+</Menu>
+EOF
+
+ # Install .desktop files
+ installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop
+}
diff --git a/menu/tests/tet_menutest b/menu/tests/tet_menutest
new file mode 100755
index 0000000..1aae67e
--- /dev/null
+++ b/menu/tests/tet_menutest
@@ -0,0 +1,39 @@
+#!/bin/bash
+TET_RUN="asdf"
+export MENUTESTDIR="${MENUTESTDIR:-/tmp/menutest}"
+. menutest
+# hack, figure out the var to use here
+
+tpstart() {
+ tet_infoline "$*"
+ FAIL=N
+}
+
+tet_startup=''
+tet_cleanup=''
+declare -i count=1
+iclist=''
+echo $TESTS
+for TESTCASE in ${TESTS}; do
+ [ ! -e "tests/${TESTCASE}/test" ] && continue;
+ # this basically curries the arg to run_test.
+ eval "tp${count}() {
+ . tests/${TESTCASE}/test
+ tpstart \"$(. tests/${TESTCASE}/test; echo ${TEST_PURPOSE-none stated})\";
+ if ! run_test tests/\"$TESTCASE\"; then
+ tet_result FAIL;
+ else
+ tet_result PASS;
+ fi
+ set +x
+ }";
+ iclist="${iclist} ic${count}"
+ eval "ic${count}=tp${count}"
+ # force subshelling, so that it doesn't pull a die on us
+ ((count+=1))
+done
+tet_iclist=iclist
+. /opt/lsb-tet3-lite/lib/posix_sh/tcm.sh
+tet_outputline 100 "xdg menu test"
+tet_tcm_main $tet_iclist
+
diff --git a/menu/tet_scen b/menu/tet_scen
new file mode 100644
index 0000000..4fe544e
--- /dev/null
+++ b/menu/tet_scen
@@ -0,0 +1,5 @@
+all
+ "Starting menu tests"
+ /tests/tet_menutest
+ "finished menu tests"
+
diff --git a/menu/tetexec.cfg b/menu/tetexec.cfg
new file mode 100644
index 0000000..ea150c9
--- /dev/null
+++ b/menu/tetexec.cfg
@@ -0,0 +1 @@
+TET_PASS_TC_NAME=True
diff --git a/secret-service/.gitignore b/secret-service/.gitignore
new file mode 100644
index 0000000..551e415
--- /dev/null
+++ b/secret-service/.gitignore
@@ -0,0 +1,3 @@
+
+/html/*.html
+reference.xml
diff --git a/secret-service/Makefile b/secret-service/Makefile
new file mode 100644
index 0000000..00efeb9
--- /dev/null
+++ b/secret-service/Makefile
@@ -0,0 +1,17 @@
+
+SPEC = org.freedesktop.Secrets.xml
+
+all: html/index.html
+
+html/index.html: reference.xml docbook-params.xsl specification.xml
+ xmlto --skip-validation -o html/ -x docbook-params.xsl xhtml specification.xml
+
+reference.xml: tools/spec-to-docbook.xsl $(SPEC)
+ xsltproc tools/spec-to-docbook.xsl $(SPEC) > $@
+
+clean:
+ rm -f reference.xml
+ rm -f html/*.html
+
+upload: all
+ rsync -Hvax html/./ specs.freedesktop.org:/srv/specifications.freedesktop.org/www/secret-service/./
diff --git a/secret-service/README b/secret-service/README
new file mode 100644
index 0000000..72a9997
--- /dev/null
+++ b/secret-service/README
@@ -0,0 +1,20 @@
+Generating the specification consists of separate steps:
+
+1. Use an xslt processor to create a DocBook XML document from the Telepathy
+ Introspection document using the provided stylesheet:
+
+$ xsltproc tools/spec-to-docbook.xsl \
+ org.freedesktop.Secrets.xml >reference.xml
+
+2. Convert specification.xml and reference.xml to a suitable output format
+ (specification.xml includes reference.xml), eg html:
+
+$ xmlto --skip-validation -o html -p params-html.xsl xhtml specification.xml
+
+If you choose a different directory for html generation, be sure to copy
+html/style.css to that location.
+
+Then open the resulting html/index.html in your favourite browser.
+
+
+Michael Leupold <lemma@confuego.org>
diff --git a/secret-service/docbook-params.xsl b/secret-service/docbook-params.xsl
new file mode 100644
index 0000000..5d8591a
--- /dev/null
+++ b/secret-service/docbook-params.xsl
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!--
+ Parameters for DocBook transformation.
+
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
+
+ <xsl:param name="toc.max.depth">3</xsl:param>
+ <xsl:param name="generate.section.toc.level">0</xsl:param>
+ <xsl:param name="generate.toc">
+ book toc
+ part nop
+ chapter toc
+ </xsl:param>
+ <xsl:param name="html.stylesheet">style.css</xsl:param>
+ <xsl:param name="funcsynopsis.style">ansi</xsl:param>
+ <xsl:param name="funcsynopsis.decoration">1</xsl:param>
+ <xsl:param name="refentry.generate.name">0</xsl:param>
+ <xsl:param name="refentry.generate.title">1</xsl:param>
+
+</xsl:stylesheet>
diff --git a/secret-service/html/style.css b/secret-service/html/style.css
new file mode 100644
index 0000000..f8a03b0
--- /dev/null
+++ b/secret-service/html/style.css
@@ -0,0 +1,127 @@
+/* reference.css, a stylesheet for reference documentation
+ * generated by the DocBook XSL Stylesheets */
+/* $Id: reference.css 8234 2009-02-09 12:10:48Z xmldoc $ */
+
+div.legalnotice {
+ font-size: 80%;
+}
+
+div.note, div.tip, div.warning {
+ margin-left: 5%;
+ margin-right: 10%;
+ padding: 5px;
+}
+
+div.note, div.tip {
+ border-left: solid #d5dee3 20px;
+ border-right: solid #d5dee3 20px;
+}
+
+div.note, div.tip {
+ border-left: solid palegreen 20px;
+ border-right: solid palegreen 20px;
+}
+
+div.warning {
+ border-left: solid yellow 20px;
+ border-right: solid yellow 20px;
+}
+
+div.note p, div.tip p, div.warning p {
+ margin-top: 0px;
+ margin-bottom: 4px;
+}
+
+div.note h3, div.tip h3, div.warning h3 {
+ margin-top: 0;
+}
+
+div.informalexample {
+ background-color: #d5dee3;
+ border-top-width: 2px;
+ border-top-style: double;
+ border-top-color: #d3d3d3;
+ border-bottom-width: 2px;
+ border-bottom-style: double;
+ border-bottom-color: #d3d3d3;
+ padding: 4px;
+ margin: 0em;
+ margin-left: 2em;
+}
+
+pre.programlisting, pre.synopsis {
+ whitespace: pre;
+ font-family: monospace;
+ background-color: #d5dee3;
+ border-top-width: 1px;
+ border-top-style: single;
+ border-top-color: #d3d3d3;
+ border-bottom-width: 1px;
+ border-bottom-style: single;
+ border-bottom-color: #d3d3d3;
+ padding: 4px;
+ margin: 0em;
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
+
+div.informalexample pre {
+ whitespace: pre;
+ font-family: monospace;
+ border-top-width: 0px;
+ border-bottom-width: 0px;
+ padding: 0px;
+}
+
+/* Parameter and PI titles */
+ div.refnamediv h2 {
+ font-size: 2em;
+}
+
+div.funcsynopsis, code.fieldsynopsis,
+div.refsect2 div.refsynopsisdiv {
+ padding: 0.5em;
+ background-color: #F4F4F4;
+ border: 1px solid gray;
+ display: block;
+ width: 80%;
+}
+
+div.refsynopsisdiv code.fieldsynopsis {
+ padding: 0;
+ border: 0;
+}
+
+code.fieldsynopsis span.modifier {
+ min-width: 8em;
+ display: inline-block;
+}
+
+code.fieldsynopsis span.type {
+ min-width: 11em;
+ display: inline-block;
+}
+
+code.fieldsynopsis span.varname {
+ min-width: 11em;
+ display: inline-block;
+ font-size: 1.1em;
+ font-weight: bold;
+}
+
+div.funcprototype-spacer {
+ max-height: 0.5em;
+}
+
+table.funcprototype-table * td {
+ font-size: 0.9em;
+}
+
+table.funcprototype-table * td code.funcdef {
+ font-size: 1.1em;
+}
+
+b.fsfunc {
+ display: inline-block;
+ min-width: 11em;
+} \ No newline at end of file
diff --git a/secret-service/org.freedesktop.Secrets.xml b/secret-service/org.freedesktop.Secrets.xml
new file mode 100644
index 0000000..b6fba64
--- /dev/null
+++ b/secret-service/org.freedesktop.Secrets.xml
@@ -0,0 +1,490 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+ * This is the well known dbus service name for controlling the
+ * entire daemon. The service manages collections of secrets. These
+ * are analogous to the gnome-keyring 'keyrings'.
+-->
+<tp:spec xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <tp:title>Secret Service API Specification</tp:title>
+ <tp:version>0.1</tp:version>
+ <tp:copyright>
+ Copyright (C) 2009 Stef Walter &lt;stef@memberwebs.com&gt;
+ </tp:copyright>
+ <tp:copyright>
+ Copyright (C) 2009 Michael Leupold &lt;lemma@confuego.org&gt;
+ </tp:copyright>
+
+ <!--
+ * ======================================================================================
+ * ERROR CODES
+ *
+ -->
+
+ <tp:errors namespace="org.freedesktop.Secret.Error">
+ <tp:docstring>Errors returned by the Secret Service API.</tp:docstring>
+ <tp:error name="IsLocked">
+ <tp:docstring>The object must be unlocked before this action can be carried out.</tp:docstring>
+ </tp:error>
+ <tp:error name="NoSession">
+ <tp:docstring>The session does not exist.</tp:docstring>
+ </tp:error>
+ <tp:error name="NoSuchObject">
+ <tp:docstring>No such item or collection exists.</tp:docstring>
+ </tp:error>
+ </tp:errors>
+
+ <!--
+ * ======================================================================================
+ * SECRET STRUCT
+ * Signature: (oayays)
+ *
+ -->
+
+ <tp:struct name="Secret">
+ <tp:docstring>The Secret type holds a (possibly encoded) secret.</tp:docstring>
+ <tp:member type="o" name="session">
+ <tp:docstring>The session that was used to encode the secret.</tp:docstring>
+ </tp:member>
+ <tp:member type="ay" name="parameters">
+ <tp:docstring>Algorithm dependent parameters for secret value encoding.</tp:docstring>
+ </tp:member>
+ <tp:member type="ay" name="value">
+ <tp:docstring>Possibly encoded secret value</tp:docstring>
+ </tp:member>
+ <tp:member type="s" name="content_type">
+ <tp:docstring>The content type of the secret. For example: 'text/plain; charset=utf8'</tp:docstring>
+ </tp:member>
+ </tp:struct>
+
+ <tp:mapping name="ObjectPath_Secret_Map">
+ <tp:docstring>A mapping from object-paths to Secret structs</tp:docstring>
+ <tp:member type="o" name="Key">
+ <tp:docstring>D-Bus object-path</tp:docstring>
+ </tp:member>
+ <tp:member type="(oayays)" name="Value" tp:type="Secret">
+ <tp:docstring>A secret</tp:docstring>
+ </tp:member>
+ </tp:mapping>
+
+ <!--
+ * ======================================================================================
+ * SERVICE INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secrets">
+
+ <interface name="org.freedesktop.Secret.Service">
+
+ <tp:docstring>
+ The Secret Service manages all the sessions and collections.
+ </tp:docstring>
+
+ <property name="Collections" type="ao" access="read">
+ <tp:docstring>The object paths of all collections (ie: keyrings)</tp:docstring>
+ </property>
+
+ <method name="OpenSession">
+ <tp:docstring>Open a unique session for the caller application.</tp:docstring>
+ <arg name="algorithm" type="s" direction="in">
+ <tp:docstring>The algorithm the caller wishes to use.</tp:docstring>
+ </arg>
+ <arg name="input" type="v" direction="in">
+ <tp:docstring>Input arguments for the algorithm.</tp:docstring>
+ </arg>
+ <arg name="output" type="v" direction="out">
+ <tp:docstring>Output of the session algorithm negotiation.</tp:docstring>
+ </arg>
+ <arg name="result" type="o" direction="out">
+ <tp:docstring>The object path of the session, if session was created.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="CreateCollection">
+ <tp:docstring>Create a new collection with the specified properties.</tp:docstring>
+ <arg name="properties" type="a{sv}" direction="in">
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ Properties for the new collection. This allows setting the new collection's
+ properties upon its creation. All READWRITE properties are useable. Specify
+ the property names in full interface.Property form.
+ <example>
+ <title>Example for properties</title>
+ <programlisting>
+<![CDATA[
+properties = { "org.freedesktop.Secret.Collection.Label": "MyCollection" }
+]]>
+ </programlisting>
+ </example>
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="alias" type="s" direction="in">
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ If creating this connection for a well known alias then a string like
+ <literal>default</literal>. If an collection with this well-known alias already
+ exists, then that collection will be returned instead of creating a new
+ collection. Any readwrite properties provided to this function will be set on
+ the collection.
+ </para>
+ <para>
+ Set this to an empty string if the new collection should not be associated with a
+ well known alias.
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="collection" type="o" direction="out">
+ <tp:docstring>The new collection object, or '/' if prompting is necessary.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object if prompting is necessary, or '/' if no prompt was needed.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SearchItems">
+ <tp:docstring>Find items in any collection.</tp:docstring>
+ <arg name="attributes" type="a{ss}" direction="in">
+ <tp:docstring>Find secrets in any collection.</tp:docstring>
+ </arg>
+ <arg name="unlocked" type="ao" direction="out">
+ <tp:docstring>Items found.</tp:docstring>
+ </arg>
+ <arg name="locked" type="ao" direction="out">
+ <tp:docstring>Items found that require authentication.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Unlock">
+ <tp:docstring>Unlock the specified objects.</tp:docstring>
+ <arg name="objects" type="ao" direction="in">
+ <tp:docstring>Objects to unlock.</tp:docstring>
+ </arg>
+ <arg name="unlocked" type="ao" direction="out">
+ <tp:docstring>Objects that were unlocked without a prompt.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object which can be used to unlock the remaining objects, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Lock">
+ <tp:docstring>Lock the items.</tp:docstring>
+ <arg name="objects" type="ao" direction="in">
+ <tp:docstring>Objects to lock.</tp:docstring>
+ </arg>
+ <arg name="locked" type="ao" direction="out">
+ <tp:docstring>Objects that were locked without a prompt.</tp:docstring>
+ </arg>
+ <arg name="Prompt" type="o" direction="out">
+ <tp:docstring>A prompt to lock the objects, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetSecrets">
+ <tp:docstring>Retrieve multiple secrets from different items.</tp:docstring>
+ <arg name="items" type="ao" direction="in">
+ <tp:docstring>Items to get secrets for.</tp:docstring>
+ </arg>
+ <arg name="session" type="o" direction="in">
+ <tp:docstring>The session to use to encode the secrets.</tp:docstring>
+ </arg>
+ <arg name="secrets" type="a{o(oayays)}" direction="out" tp:type="ObjectPath_Secret_Map">
+ <tp:docstring>Secrets for the items.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="ReadAlias">
+ <tp:docstring>Get the collection with the given alias.</tp:docstring>
+ <arg name="name" type='s' direction='in'>
+ <tp:docstring>An alias, such as 'default'.</tp:docstring>
+ </arg>
+ <arg name="collection" type='o' direction='out'>
+ <tp:docstring>The collection or the the path '/' if no such collection exists.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetAlias">
+ <tp:docstring>Setup a collection alias.</tp:docstring>
+ <arg name="name" type='s' direction='in'>
+ <tp:docstring>An alias, such as 'default'.</tp:docstring>
+ </arg>
+ <arg name="collection" type='o' direction='in'>
+ <tp:docstring>
+ The collection to make the alias point to. To remove an alias use the special value '/'.
+ </tp:docstring>
+ </arg>
+ </method>
+
+ <signal name="CollectionCreated">
+ <tp:docstring>A collection was created.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was created</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="CollectionDeleted">
+ <tp:docstring>A collection was deleted.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was deleted.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="CollectionChanged">
+ <tp:docstring>A collection was changed.</tp:docstring>
+ <arg name="collection" type="o">
+ <tp:docstring>Collection that was changed.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * COLLECTION INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secrets/collection/xxxx">
+
+ <interface name="org.freedesktop.Secret.Collection">
+
+ <tp:docstring>A collection of items containing secrets.</tp:docstring>
+
+ <property name="Items" type="ao" access="read">
+ <tp:docstring>Items in this collection.</tp:docstring>
+ </property>
+
+ <property name="Label" type="s" access="readwrite">
+ <tp:docstring>The displayable label of this collection.</tp:docstring>
+ </property>
+
+ <property name="Locked" type="b" access="read">
+ <tp:docstring>
+ Whether the collection is locked and must be authenticated by the client application.
+ </tp:docstring>
+ </property>
+
+ <property name="Created" type="t" access="read">
+ <tp:docstring>The unix time when the collection was created.</tp:docstring>
+ </property>
+
+ <property name="Modified" type="t" access="read">
+ <tp:docstring>The unix time when the collection was last modified.</tp:docstring>
+ </property>
+
+ <method name="Delete">
+ <tp:docstring>Delete this collection.</tp:docstring>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt to delete the collection, or the special value '/' when no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SearchItems">
+ <tp:docstring>Search for items in this collection matching the lookup attributes.</tp:docstring>
+ <arg name="attributes" type="a{ss}" direction="in">
+ <tp:docstring>Attributes to match.</tp:docstring>
+ </arg>
+ <arg name="results" type="ao" direction="out">
+ <tp:docstring>Items that matched the attributes.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="CreateItem">
+ <tp:docstring>Create an item with the given attributes, secret and label. If replace is set, then it replaces an item already present with the same values for the attributes.</tp:docstring>
+ <arg name="properties" type="a{sv}" direction="in">
+ <tp:docstring>The properties for the new item.</tp:docstring>
+ <tp:docstring xmlns:docbook="http://docbook.org/ns/docbook">
+ <para>
+ Properties for the new item. This allows setting the new item's
+ properties upon its creation. All READWRITE properties are useable.
+ Specify the property names in full interface.Property form.
+ <example>
+ <title>Example for properties</title>
+ <programlisting>
+<![CDATA[
+properties = {
+ "org.freedesktop.Secret.Item.Label": "MyItem",
+ "org.freedesktop.Secret.Item.Attributes": {
+ "Attribute1": "Value1",
+ "Attribute2": "Value2"
+ }
+ }
+]]>
+ </programlisting>
+ </example>
+ <note>
+ <para>
+ Please note that there is a distinction between the terms
+ <emphasis>Property</emphasis>, which refers to a D-Bus properties
+ of an object, and <emphasis>Attribute</emphasis>, which refers to one
+ of a secret item's string-valued attributes.
+ </para>
+ </note>
+ </para>
+ </tp:docstring>
+ </arg>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="in">
+ <tp:docstring>The secret to store in the item, encoded with the included session.</tp:docstring>
+ </arg>
+ <arg name="replace" type="b" direction="in">
+ <tp:docstring>Whether to replace an item with the same attributes or not.</tp:docstring>
+ </arg>
+ <arg name="item" type="o" direction="out">
+ <tp:docstring>The item created, or the special value '/' if a prompt is necessary.</tp:docstring>
+ </arg>
+ <arg name="prompt" type="o" direction="out">
+ <tp:docstring>A prompt object, or the special value '/' if no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <signal name="ItemCreated">
+ <tp:docstring>A new item in this collection was created.</tp:docstring>
+ <arg name="item" type="o">
+ <tp:docstring>The item that was created.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="ItemDeleted">
+ <tp:docstring>An item in this collection was deleted.</tp:docstring>
+
+ <arg name="item" type="o">
+ <tp:docstring>The item that was deleted.</tp:docstring>
+ </arg>
+ </signal>
+
+ <signal name="ItemChanged">
+ <tp:docstring>An item in this collection changed.</tp:docstring>
+
+ <arg name="item" type="o">
+ <tp:docstring>The item that was changed.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * ITEM INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/collection/xxxx/iiii">
+
+ <interface name="org.freedesktop.Secret.Item">
+
+ <tp:docstring>An item contains a secret, lookup attributes and has a label.</tp:docstring>
+
+ <property name="Locked" type="b" access="read">
+ <tp:docstring>Whether the item is locked and requires authentication, or not.</tp:docstring>
+ </property>
+
+ <property name="Attributes" type="a{ss}" access="readwrite">
+ <tp:docstring>The lookup attributes for this item.</tp:docstring>
+ </property>
+
+ <property name="Label" type="s" access="readwrite">
+ <tp:docstring>The displayable label for this item.</tp:docstring>
+ </property>
+
+ <property name="Created" type="t" access="read">
+ <tp:docstring>The unix time when the item was created.</tp:docstring>
+ </property>
+
+ <property name="Modified" type="t" access="read">
+ <tp:docstring>The unix time when the item was last modified.</tp:docstring>
+ </property>
+
+ <method name="Delete">
+ <tp:docstring>Delete this item.</tp:docstring>
+ <arg name="Prompt" type="o" direction="out">
+ <tp:docstring>A prompt object, or the special value '/' if no prompt is necessary.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="GetSecret">
+ <tp:docstring>Retrieve the secret for this item.</tp:docstring>
+ <arg name="session" type="o" direction="in">
+ <tp:docstring>The session to use to encode the secret.</tp:docstring>
+ </arg>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="out">
+ <tp:docstring>The secret retrieved.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="SetSecret">
+ <tp:docstring>Set the secret for this item.</tp:docstring>
+ <arg name="secret" type="(oayays)" tp:type="Secret" direction="in">
+ <tp:docstring>The secret to set, encoded for the included session.</tp:docstring>
+ </arg>
+ </method>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * SESSION INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/session/ssss">
+
+ <interface name="org.freedesktop.Secret.Session">
+
+ <tp:docstring>A session tracks state between the service and a client application.</tp:docstring>
+
+ <method name="Close">
+ <tp:docstring>Close this session.</tp:docstring>
+ </method>
+
+ </interface>
+
+ </node>
+
+ <!--
+ * ======================================================================================
+ * PROMPT INTERFACE
+ *
+ -->
+
+ <node name="/org/freedesktop/Secret/prompts/pppp">
+
+ <interface name="org.freedesktop.Secret.Prompt">
+ <tp:docstring>A prompt necessary to complete an operation.</tp:docstring>
+
+ <method name="Prompt">
+ <tp:docstring>Perform the prompt.</tp:docstring>
+ <arg name="window-id" type="s" direction="in">
+ <tp:docstring>Platform specific window handle to use for
+ showing the prompt.</tp:docstring>
+ </arg>
+ </method>
+
+ <method name="Dismiss">
+ <tp:docstring>Dismiss the prompt.</tp:docstring>
+ </method>
+
+ <signal name="Completed">
+ <tp:docstring>The prompt and operation completed.</tp:docstring>
+ <arg name="dismissed" type="b">
+ <tp:docstring>Whether the prompt and operation were dismissed or not.</tp:docstring>
+ </arg>
+ <arg name="result" type="v">
+ <tp:docstring>The possibly empty, operation specific,
+ result.</tp:docstring>
+ </arg>
+ </signal>
+
+ </interface>
+
+ </node>
+
+</tp:spec>
diff --git a/secret-service/specification.xml b/secret-service/specification.xml
new file mode 100644
index 0000000..507eb78
--- /dev/null
+++ b/secret-service/specification.xml
@@ -0,0 +1,580 @@
+<?xml version="1.0"?>
+<book xml:id="index" xmlns="http://docbook.org/ns/docbook" version="5.0">
+ <bookinfo>
+ <title>Secret Service API Draft</title>
+ <releaseinfo>
+ Secret Service 0.2 DRAFT
+ </releaseinfo>
+
+ <authorgroup>
+ <author>
+ <firstname>Stef</firstname>
+ <surname>Walter</surname>
+ <affiliation>
+ <jobtitle>GNOME Keyring Developer</jobtitle>
+ <address>
+ <email>stefw@collabora.co.uk</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Michael</firstname>
+ <surname>Leupold</surname>
+ <affiliation>
+ <jobtitle>KWallet Developer</jobtitle>
+ <address>
+ <email>lemma@confuego.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2008-2011</year>
+ <holder>The Secret Service API Authors</holder>
+ </copyright>
+
+ </bookinfo>
+
+ <part xml:id="description">
+ <title>API Documentation</title>
+ <chapter>
+ <title>Introduction</title>
+
+ <para>The Secret Service API allows client applications to store secrets securely in a
+ service running in the user's login session. </para>
+
+ <para>The secrets are usually stored in an encrypted manner by the service. The
+ service may need to be unlocked by the user before the secrets become available
+ for retrieval by client applications.</para>
+
+ <para>The Secret Service stores a secret along with a set of lookup attributes.
+ The attributes can be used to look up and retrieve a secret at a later date. The
+ lookup attributes are not treated as secret material, and the service may choose
+ not to encrypt attributes when storing them to disk.</para>
+
+ <para>This API was desigened by GNOME and KDE developers with the goal of having
+ a common way to store secrets. Its predecessors are the desktop-specific APIs
+ used by GNOME Keyring and KWallet.</para>
+ </chapter>
+
+ <chapter>
+ <title>Secrets</title>
+
+ <para>A secret is something an application wishes to store securely. A good example
+ is a password that an application needs to save and use at a later date.</para>
+
+ <para>Within this API a secret value is treated as an array of bytes. It is
+ recommended that a secret consist of user-readable text, although this API has
+ no such requirement.</para>
+
+ <para>Applications wishing to store multiple values as part of a single secret, may
+ choose to use a textual format to combine these values into one. For example, multiple
+ values may be combined using the 'desktop' key file format, or XML.</para>
+
+ <para>Secrets may be <link linkend='transfer-secrets'>encrypted when transferred</link>
+ to or from the client application.</para>
+
+ <para>The <link linkend='type-Secret'><classname>Secret</classname> structure</link> encapsulates
+ a secret value along with its transfer encryption parameters.</para>
+ </chapter>
+
+ <chapter>
+ <title>Collection and Items</title>
+
+ <para>Each secret is stored together with
+ <link linkend='lookup-attributes'>lookup attributes</link> and a label. These together
+ form an <link linkend='org.freedesktop.Secret.Item'>item</link>.</para>
+
+ <para>A group of items together form a
+ <link linkend='org.freedesktop.Secret.Collection'>collection</link>.
+ A collection is similar in concept to the terms 'keyring' or 'wallet'.</para>
+
+ <para>Collections and items are represented as DBus objects, and each has its own
+ object path. Under normal circumstances, the object path of a collection or item
+ should not change for its lifetime.</para>
+
+ <para>It is strongly recommended that client applications use
+ <link linkend='lookup-attributes'>lookup attributes</link> to find items rather than
+ recording the object path of a stored item. This allows maximum interoperability.</para>
+
+ <para>An item or a collection may be initially in a locked state. When in a locked
+ state the item or collection may not be modified in any way, and the secret may not
+ be read. Client applications that require access to the secret of a locked item, or
+ desire to modify a locked item, must <link linkend='unlocking'>unlock it</link> before use.</para>
+
+ <para>The service must prevent modification of locked collections or items. On
+ such an invalid access the
+ <link linkend='org.freedesktop.Secret.Error.IsLocked'><errorname>IsLocked</errorname></link>
+ error should be raised.</para>
+
+ <para>Client applications without special requirements should store in the default
+ collection. The default collection is always accessible through a
+ <link linkend='object-paths'>specific object path</link>.</para>
+
+ <para>A new item can be created with the
+ <link linkend='org.freedesktop.Secret.Collection.CreateItem'>
+ <function>CreateItem()</function></link>
+ method on the Collection interface. When creating an item, the properties of the new
+ item are specified. The service may ignore or change these properties when creating
+ the item.</para>
+
+ <para>When creating an item, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be created. In this case, the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object path of the new item.</para>
+
+ <para>An item can be deleted by calling the
+ <link linkend='org.freedesktop.Secret.Item.Delete'><function>Delete()</function></link>
+ method on the Item interface.</para>
+
+ <para>When deleting an item, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the item to be deleted.</para>
+
+ <para>Client applications with special needs can create a new collection by calling the
+ <link linkend='org.freedesktop.Secret.Service.CreateCollection'>
+ <function>CreateCollection()</function></link>
+ method on the Service interface. When creating a collection, the properties of the new
+ collection are specified. The service may ignore or change these properties when creating
+ the collection.</para>
+
+ <para>When creating a collection, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be created. In this case, the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object path of the new collection.</para>
+
+ <para>A collection can be deleted by calling the
+ <link linkend='org.freedesktop.Secret.Collection.Delete'><function>Delete()</function></link>
+ method on the Collection interface.</para>
+
+ <para>When deleting a collection, the service may need to prompt the user for additional
+ information. In this case, a <link linkend='prompts'>prompt object</link> is returned. It
+ must be <link linkend='org.freedesktop.Secret.Prompt.Prompt'>acted upon</link> in order for
+ the collection to be deleted.</para>
+ </chapter>
+
+ <chapter xml:id="aliases">
+ <title>Aliases</title>
+
+ <para>Collections may be accessed via well known aliases. For example an alias
+ called <literal>default</literal> tells applications which is the default
+ collection to store secrets.</para>
+
+ <para>The aliased collections will be available at a
+ <link linkend='object-paths'>well-known DBus object path</link>.</para>
+
+ <para>If an application needs to create a collection with a given alias, this can
+ be done in a race free fashion by specifying the alias parameter of the
+ <link linkend='org.freedesktop.Secret.Service.CreateCollection'>CreateCollection()</link>
+ method on the service interface. If a collection with that alias already exists,
+ then it will be returned instead of creating a new one.</para>
+
+ <para>For applications like password managers it can be useful to allow the user to
+ configure which collection is associated with which well known alias. To alias or
+ unalias a collection use the
+ <link linkend='org.freedesktop.Secret.Service.SetAlias'>SetAlias()</link> method on the
+ service interface. Use the
+ <link linkend='org.freedesktop.Secret.Service.ReadAlias'>ReadAlias()</link> method on the
+ service interface to discover which collection is associated with a given alias.</para>
+ </chapter>
+
+ <chapter xml:id="lookup-attributes">
+ <title>Lookup Attributes</title>
+
+ <para>Attributes can and should be stored with a secret to facilitate lookup
+ of the secret at a later date.</para>
+
+ <para>An attribute constists of a name, and a value. Both parts are simple
+ strings.</para>
+
+ <para>The service may have additional requirements as to what can be present
+ in an attribute name. It is recommended that attribute names are human
+ readable, and kept simple for the sake of simplicity.</para>
+
+ <para>During a lookup, attribute names and values are matched via case-sensitive
+ string equality.</para>
+
+ <para>It's important to remember that attributes are not part of the secret.
+ Services implementing this API will probably store attributes in an unencrypted
+ manner in order to support simple and effecient lookups.</para>
+
+ <para>In order to search for items, use the
+ <link linkend='org.freedesktop.Secret.Service.SearchItems'><function>SearchItems()</function></link>
+ method of the Service interface. The matched items will be returned in two sets. The
+ <parameter class='function'>unlocked</parameter> return value will contain the object paths
+ of all the items that are not locked. The <parameter class='function'>locked</parameter> return
+ value will contain object paths of items that are locked, which can be
+ <link linkend='unlocking'>unlocked if desired</link>.</para>
+
+ <para>The
+ <link linkend='org.freedesktop.Secret.Collection.SearchItems'><function>SearchItems()</function></link>
+ method of the Collection interface is similar, except for it only searches a single collection.</para>
+
+ </chapter>
+
+ <chapter xml:id="sessions">
+ <title>Sessions</title>
+
+ <para>A session is established between a client application and a service. A session
+ is used to <link linkend='transfer-secrets'>transfer secrets</link> between the client
+ application and the service.</para>
+
+ <para>A session is established by calling the service's
+ <link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ <function>OpenSession()</function></link>
+ method. Once established, a session is bound to calling application's connection to
+ the DBus session bus.</para>
+
+ <para>A session is closed when the client application disconnects from the DBus
+ session bus. Alternatively the client application can call the
+ <link linkend='org.freedesktop.Secret.Session.Close'><function>Close()</function></link>
+ method on the session interface. Once a session is closed all session specific
+ negotiations will be dropped by the service.</para>
+
+ <para>More than one session may opened by a client application, although this is
+ not normally necessary.</para>
+ </chapter>
+
+ <chapter xml:id='transfer-secrets'>
+ <title>Transfer of Secrets</title>
+
+ <para>To access or store secrets, use the
+ <link linkend='org.freedesktop.Secret.Item.GetSecret'><function>GetSecret()</function></link>,
+ <link linkend='org.freedesktop.Secret.Item.SetSecret'><function>SetSecret()</function></link>
+ methods on the item interface, or the
+ <link linkend='org.freedesktop.Secret.Service.GetSecrets'><function>GetSecrets()</function></link>,
+ method on the service interface.</para>
+
+ <para>You must specify a session when retrieving or storing a secret. The session
+ controls how the secret is encoded during transfer. Since this is a D-Bus API, the
+ data in all method calls and other accesses in this API will go through multiple
+ processes, and may be cached arbitrarilyby the OS or elsewhere.</para>
+
+ <para>The Secrets API has provision to encrypt secrets while in transit between
+ the service and the client application. The encryption is not envisioned to withstand
+ man in the middle attacks, or other active attacks. It is envisioned to minimize
+ storage of plain text secrets in memory and prevent storage plain text storage of
+ secrets in a swap file or other caching mechanism.</para>
+
+ <para>Many client applications may choose not to make use of the provisions to
+ encrypt secrets in transit. In fact for applications unable to prevent their own
+ memory from being paged to disk (eg: Java, C# or Python apps), transfering
+ encrypted secrets would be an excersize of questionable value.</para>
+
+ <section>
+ <title>Negotiation of Algorithms</title>
+
+ <para>In order to encrypt secrets in transit, the service and the client
+ application must agree on an algorithm, and some algorithm specific
+ parameters (eg: a key).</para>
+
+ <para>When the client application opens a <link linkend='sessions'>session</link>
+ with the service, it calls the
+ <link linkend='org.freedesktop.Secret.Service.OpenSession'><function>
+ OpenSession()</function></link> method on the service. The algorithms argument to the
+ <function>OpenSession()</function> method specifies a set of algorithms to be used
+ together for key agreement and encryption. The other arguments are algorithm
+ specific.</para>
+
+ <para>If a service does not support a specific set of algorithms, a
+ <errorname>org.freedesktop.DBus.Error.NotSupported</errorname>
+ error is returned, and the client is free to try another set of algorithms.
+ The <emphasis>plain</emphasis> algorithm is almost always supported.</para>
+
+ <para>An algorithm may require that the <function>OpenSession()</function> method is
+ called multiple times in succession to be complete. Each iteration transfers
+ algorithm specific data back forth between the service and the client. The object path
+ '/' is returned from <function>OpenSession()</function> when session negotiation is
+ incomplete.</para>
+
+ <para>None of the algorithms documented in this initial version of the specification
+ require multiple calls to <function>OpenSession()</function>.</para>
+
+ <para>When <function>OpenSession()</function> completes, it returns the session object
+ path along with a valid session object path.</para>
+
+ <para>Once an session algorithm has been negotiated, it is used for all transfer a
+ secrets whenever that session is specified along with the
+ <link linkend='type-Secret'><classname>secret</classname></link>.</para>
+ </section>
+
+ <section>
+ <title>Algorithm: plain</title>
+
+ <segmentedlist>
+ <?dbhtml list-presentation="list"?>
+ <segtitle>Session algorithm</segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session input</link></segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session output</link></segtitle>
+ <segtitle><link linkend='type-Secret'>
+ <classname>Secret</classname> parameter</link></segtitle>
+ <seglistitem>
+ <!-- TODO: literal? -->
+ <seg><emphasis>plain</emphasis></seg>
+ <seg>empty string</seg>
+ <seg>empty string</seg>
+ <seg>empty string</seg>
+ </seglistitem>
+ </segmentedlist>
+
+ <para>The plain algorithm does no encryption whatsoever.</para>
+
+ <para>It is strongly recommended that a service implementing this API support
+ the <emphasis>plain</emphasis> algorithm.</para>
+ </section>
+
+ <section>
+ <title>Algorithm: dh-ietf1024-sha256-aes128-cbc-pkcs7</title>
+
+ <segmentedlist>
+ <?dbhtml list-presentation="list"?>
+ <segtitle>Session algorithm</segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session input</link></segtitle>
+ <segtitle><link linkend='org.freedesktop.Secret.Service.OpenSession'>
+ Session output</link></segtitle>
+ <segtitle><link linkend='type-Secret'>
+ <classname>Secret</classname> parameter</link></segtitle>
+ <seglistitem>
+ <!-- TODO: literal? -->
+ <seg><emphasis>dh-ietf1024-sha256-aes128-cbc-pkcs7</emphasis></seg>
+ <seg>Client DH pub key as an array of bytes</seg>
+ <seg>Service DH pub key as an array of bytes</seg>
+ <seg>16 byte AES initialization vector</seg>
+ </seglistitem>
+ </segmentedlist>
+
+ <para>DH key agreement <citation>rfc2631</citation> is used to create a secret key
+ using 1024 bit parameters of the standard IETF 'Second Oakley Group'
+ <citation>rfc2409</citation>. The secret key is then digested into a 128-bit key
+ appropriate for AES. This is done using HKDF <citation>rfc5869</citation> with NULL
+ salt and empty info, using the SHA-2 256 hash algorithm
+ <citation>fips-180-3.2008</citation>. The secrets are encrypted using AES
+ <citation>fips-197.2001</citation> in cipher block chaining mode with pkcs7 style
+ padding <citation>rfc2315</citation>.</para>
+
+ <para>The public keys are transferred as an array of bytes representing an
+ unsigned integer of arbitrary size, most-significant byte first (e.g., the
+ integer 32768 is represented as the 2-byte string 0x80 0x00)</para>
+ </section>
+
+ </chapter>
+
+ <chapter xml:id='unlocking'>
+ <title>Locking and Unlocking</title>
+
+ <para>Some items and/or collections may be marked as locked by the service.
+ The secrets of locked items cannot be accessed. Additionally, locked items or collections
+ cannot be modified by the client application.</para>
+
+ <para>It's up to the service whether to unlock items individually, or collections as a
+ whole. The client application should act as if it must unlock each item individually.</para>
+
+ <para>A service may upon unlocking a collection, unlock all items in that collection. If
+ a service is not able to unlock an item individually, it should treat a request to unlock
+ an item as a request to unlock the connection that the item is in.</para>
+
+ <para>A service may choose to unlock items or collections just for a single client
+ application. Alternatively the service may choose to allow any client application to access
+ items or collections unlocked by a another client application.</para>
+
+ <para>A client application should always be ready to unlock the items for the secrets
+ it needs, or objects it must modify. It must not assume that an item is already unlocked
+ for whatever reason.</para>
+
+ <para>A service may lock previously unlocked items for any reason at any time. Usually this
+ is done in response to user actions, timeouts, or external actions (such as the computer
+ sleeping). The inherent race conditions present due to this are unavoidable, and must be
+ handled gracefully.</para>
+
+ <para>In order to unlock an item or collection the service's
+ <link linkend='org.freedesktop.Secret.Service.Unlock'>
+ <function>Unlock()</function></link>
+ method is called with one or more DBus object paths of items or collections. The
+ <function>Unlock()</function> will return the DBus object paths of objects it could
+ immediately unlock without prompting.</para>
+
+ <para>The <function>Unlock()</function> method may also return a
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'>prompt object</link>. If a prompt
+ object is returned, it must be <link linkend='prompts'>acted upon</link> in order to complete
+ the unlocking of the remaining objects. The
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object paths that were successfully unlocked by the prompt.</para>
+
+ <para>In order to lock an item or collection the service's
+ <link linkend='org.freedesktop.Secret.Service.Unlock'>
+ <function>Lock()</function></link>
+ method is called with one or more DBus object paths of items or collections. The
+ <function>Lock()</function> will return the DBus object paths of objects it could
+ immediately lock without prompting.</para>
+
+ <para>The <function>Lock()</function> method may also return a
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'>prompt object</link>. If a prompt
+ object is returned, it must be <link linkend='prompts'>acted upon</link> in order to complete
+ the locking of the remaining objects. The
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'>result of the prompt</link>
+ will contain the object paths that were successfully locked by the prompt.</para>
+ </chapter>
+
+ <chapter xml:id='prompts'>
+ <title>Prompts and Prompting</title>
+
+ <para>In order to complete various operations, such as unlocking a collection, the
+ service may need to prompt the user for additional information, such as a master password.</para>
+
+ <para>The prompts are displayed by the service on behalf of the client application.</para>
+
+ <para>Operations that require a prompt to complete will return a prompt object. The client
+ application must then call the
+ <link linkend='org.freedesktop.Secret.Prompt.Prompt'><function>Prompt()</function></link>
+ method of the prompt object to display the prompt. Client applications can use the
+ <parameter class='function'>window-id</parameter>
+ argument to display the prompt attached to their application window.</para>
+
+ <para>Once the user provides the additional required information to the prompt, the service
+ completes the operation that required the prompt. Then it emits the the
+ <link linkend='org.freedesktop.Secret.Prompt.Completed'><function>Completed</function></link>
+ signal of the prompt object. The <parameter class='function'>result</parameter> argument of
+ the signal contains operation an operation specific result.</para>
+
+ <para>Either the user or the client application can dismiss a prompt. In this case the
+ operation that required the additional information is cancelled. The client application
+ can dismiss a prompt by calling the
+ <link linkend='org.freedesktop.Secret.Prompt.Dismiss'><function>Dismiss()</function></link>
+ method of the prompt object. The <function>Completed</function> signal will be emitted
+ with its <parameter class='function'>dismissed</parameter> argument set to
+ <constant>TRUE</constant>.</para>
+
+ <para>Once the <function>Completed</function> signal is emitted on a prompt object, it
+ is no longer valid. Prompt objects are specific to the client application's connection
+ to the DBus bus. Once an application disconnects, all its prompts are no longer valid.</para>
+
+ <para>There is an inherent race between the <function>Dismiss()</function> method and the
+ <function>Completed</function> signal. An application calling <function>Dismiss()</function>
+ must be prepared to handle the fact that the <function>Completed</function> has already been
+ emitted (although perhaps not received by the client). In addition the client must be prepared
+ to handle the fact that the prompt object is no longer valid.</para>
+ </chapter>
+
+ <chapter>
+ <title>What's not included in the API</title>
+
+ <para>A service may implement additional DBus interfaces for further capabilities not
+ included in this specification. Password management applications or other narrowly
+ focused tools should make use of these when necessary.</para>
+
+ <para>This specification does not mandate the use of master passwords to lock a
+ collection of secrets. The service may choose to implement any method for locking
+ secrets.</para>
+
+ <para>This specification does not mandate any form of access control. The service may
+ choose to allow certain applications to access a keyring, and others.</para>
+
+ <para>[TODO: complete]</para>
+ </chapter>
+
+ <chapter>
+ <title>Notes for Service Implementors</title>
+
+ <para>[TODO: complete]</para>
+ </chapter>
+
+ </part>
+
+ <part xml:id="ref-dbus-api">
+ <title>D-Bus API Reference</title>
+
+ <chapter xml:id='object-paths'>
+ <title>Object Paths</title>
+
+ <para>The various DBus object paths used with the Secret Service API are designed to be human
+ readable but not displayed to the user. The object path of an item or collection should
+ not change for its lifetime, under normal circumstances.</para>
+
+ <programlisting>/org/freedesktop/secrets</programlisting>
+ <para>The object path for the service.</para>
+
+ <programlisting>/org/freedesktop/secrets/collection/<emphasis>xxxx</emphasis></programlisting>
+ <para>The object path for a collection, where <emphasis>xxxx</emphasis> represents a
+ possibly encoded or truncated version of the initial label of the collection.</para>
+
+ <programlisting>/org/freedesktop/secrets/collection/<emphasis>xxxx</emphasis>/<emphasis>iiii</emphasis></programlisting>
+ <para>The object path for an item, where <emphasis>xxxx</emphasis> is the collection (above)
+ and <emphasis>iiii</emphasis> is an auto-generated item specific identifier.</para>
+
+ <programlisting>/org/freedesktop/secrets/session/<emphasis>ssss</emphasis></programlisting>
+ <para>The object path for a session, where <emphasis>ssss</emphasis> is an auto-generated
+ session specific identifier.</para>
+
+ <programlisting>/org/freedesktop/secrets/aliases/default</programlisting>
+ <para>The default collection for client applications to store secrets is available under
+ this object path in addition to its real object path (above). Other aliases may also be
+ present.</para>
+ </chapter>
+
+ <xi:include href="reference.xml" xpointer="interfaces" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+ <xi:include href="reference.xml" xpointer="types" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+ <xi:include href="reference.xml" xpointer="errors" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+
+ </part>
+
+ <xi:include href="xml/annotation-glossary.xml" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:fallback/>
+ </xi:include>
+
+ <bibliography>
+ <title>References</title>
+
+ <bibliomixed>
+ <abbrev>rfc2315</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2315.txt">RFC 2315</ulink>:
+ PKCS #7: Cryptographic Message Syntax Version 1.5
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc2409</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2409.txt">RFC 2409</ulink>:
+ The Internet Key Exchange (IKE)
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc2631</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc2631.txt">RFC 2631</ulink>:
+ Diffie-Hellman Key Agreement Method
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>rfc5869</abbrev>
+ IETF <ulink url="http://www.ietf.org/rfc/rfc5869.txt">RFC 5869</ulink>:
+ HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>fips-180-3.2008</abbrev>
+ NIST <ulink url="http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf">FIPS PUB 180-3</ulink>:
+ Secure Hash Standard (SHS), October 2008
+ </bibliomixed>
+
+ <bibliomixed>
+ <abbrev>fips-197.2001</abbrev>
+ NIST <ulink url="http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf">FIPS PUB 197</ulink>:
+ Advanced Encryption Standard (AES), November 2001
+ </bibliomixed>
+
+ </bibliography>
+
+</book>
diff --git a/secret-service/tools/resolve-type.xsl b/secret-service/tools/resolve-type.xsl
new file mode 100644
index 0000000..a37f2af
--- /dev/null
+++ b/secret-service/tools/resolve-type.xsl
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ exclude-result-prefixes="tp html">
+
+<!--
+ Helper templates for Telepathy D-Bus Introspection conversion.
+
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <!-- Resolve the type a node has. This will first look at tp:type and
+ - if not found - use the type attribute -->
+ <xsl:template name="ResolveType">
+ <xsl:param name="node"/>
+ <xsl:variable name="unstripped">
+ <xsl:choose>
+ <xsl:when test="$node//@tp:type">
+ <xsl:call-template name="TpType">
+ <xsl:with-param name="type" select="$node//@tp:type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="$node//@type">
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="$node//@type"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Node doesn't contain a type.
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:value-of select="translate(translate($unstripped, ' ', ''), '&#xa;', '')"/>
+ </xsl:template>
+
+ <!-- Map a D-Bus type to its EggDBus counterpart -->
+ <xsl:template name="DBusType">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="$type='o'">ObjectPath</xsl:when>
+ <xsl:when test="$type='s'">String</xsl:when>
+ <xsl:when test="$type='y'">Byte</xsl:when>
+ <xsl:when test="$type='b'">Boolean</xsl:when>
+ <xsl:when test="$type='n'">Int16</xsl:when>
+ <xsl:when test="$type='q'">UInt16</xsl:when>
+ <xsl:when test="$type='i'">Int32</xsl:when>
+ <xsl:when test="$type='u'">UInt32</xsl:when>
+ <xsl:when test="$type='x'">Int64</xsl:when>
+ <xsl:when test="$type='t'">UInt64</xsl:when>
+ <xsl:when test="$type='d'">Double</xsl:when>
+ <xsl:when test="$type='g'">Signature</xsl:when>
+ <xsl:when test="$type='v'">Variant</xsl:when>
+ <xsl:when test="starts-with($type, 'a{')">
+ Dict&lt;
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 3, 1)"/>
+ </xsl:call-template>
+ ,
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 4, 1)"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <xsl:when test="starts-with($type, 'a')">
+ Array&lt;
+ <xsl:call-template name="DBusType">
+ <xsl:with-param name="type" select="substring($type, 2)"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <!-- TODO: doesn't implement dict-entries and structs -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Unknown DBus Type <xsl:value-of select="$type"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Resolve tp:type attributes by searching for matching tp:struct
+ and tp:mapping elements -->
+ <xsl:template name="TpType">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="/tp:spec/tp:struct[@name=$type]">
+ <xsl:value-of select="$type"/>
+ </xsl:when>
+ <xsl:when test="/tp:spec/tp:mapping[@name=$type]">
+ Dict&lt;
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Key']"/>
+ </xsl:call-template>,
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="/tp:spec/tp:mapping[@name=$type]/tp:member[@name='Value']"/>
+ </xsl:call-template>
+ &gt;
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ Unspecified type <xsl:value-of select="$type"/>.
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/secret-service/tools/spec-to-docbook.xsl b/secret-service/tools/spec-to-docbook.xsl
new file mode 100644
index 0000000..a6b18a3
--- /dev/null
+++ b/secret-service/tools/spec-to-docbook.xsl
@@ -0,0 +1,1242 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:docbook="http://docbook.org/ns/docbook"
+ exclude-result-prefixes="tp html">
+
+<!--
+ Telepathy D-Bus Introspection to Docbook XML translator.
+ Based on Telepathy's doc-generator.xsl.
+
+ Copyright (C) 2006-2008 Collabora Limited
+ Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+ <xsl:output method="xml" indent="yes" encoding="ascii"
+ omit-xml-declaration="no"/>
+
+ <xsl:include href="resolve-type.xsl"/>
+
+ <xsl:param name="allow-undefined-interfaces" select="false()"/>
+
+ <xsl:template match="docbook:* | html:* | @*">
+ <xsl:copy>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template name="direction">
+ <xsl:param name="indirection"/>
+ <xsl:choose>
+ <xsl:when test="$indirection = 'in'">IN</xsl:when>
+ <xsl:otherwise>OUT</xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="tp:type">
+ <xsl:call-template name="tp-type">
+ <xsl:with-param name="tp-type" select="string(.)"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- tp:dbus-ref: reference a D-Bus interface, signal, method or property -->
+ <xsl:template match="tp:dbus-ref">
+ <xsl:variable name="name">
+ <xsl:choose>
+ <xsl:when test="@namespace">
+ <xsl:value-of select="@namespace"/>
+ <xsl:text>.</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:value-of select="string(.)"/>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="//interface[@name=$name]
+ or //interface/method[concat(../@name, '.', @name)=$name]
+ or //interface/signal[concat(../@name, '.', @name)=$name]
+ or //interface/property[concat(../@name, '.', @name)=$name]
+ or //interface[@name=concat($name, '.DRAFT')]
+ or //interface/method[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ or //interface/signal[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ or //interface/property[
+ concat(../@name, '.', @name)=concat($name, '.DRAFT')]
+ ">
+ <link linkend="{$name}">
+ <literal><xsl:value-of select="$name"/></literal>
+ </link>
+ </xsl:when>
+
+ <xsl:when test="$allow-undefined-interfaces">
+ <!-- TODO: Convert to docbook -->
+ <span xmlns="http://www.w3.org/1999/xhtml" title="defined elsewhere">
+ <xsl:value-of select="string(.)"/>
+ </span>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: cannot find D-Bus interface, method, </xsl:text>
+ <xsl:text>signal or property called '</xsl:text>
+ <xsl:value-of select="$name"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- tp:member-ref: reference a property of the current interface -->
+ <xsl:template match="tp:member-ref">
+ <xsl:variable name="prefix" select="concat(ancestor::interface/@name,
+ '.')"/>
+ <xsl:variable name="name" select="string(.)"/>
+
+ <xsl:if test="not(ancestor::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: Cannot use tp:member-ref when not in an</xsl:text>
+ <xsl:text> &lt;interface&gt;&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::interface/signal[@name=$name]"/>
+ <xsl:when test="ancestor::interface/method[@name=$name]"/>
+ <xsl:when test="ancestor::interface/property[@name=$name]"/>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: interface </xsl:text>
+ <xsl:value-of select="ancestor::interface/@name"/>
+ <xsl:text> has no signal/method/property called </xsl:text>
+ <xsl:value-of select="$name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <link linkend="{$prefix}{$name}">
+ <literal><xsl:value-of select="concat($prefix, $name)"/></literal>
+ </link>
+ </xsl:template>
+
+ <xsl:template match="*" mode="identity">
+ <xsl:copy>
+ <xsl:apply-templates mode="identity"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring">
+ <para>
+ <xsl:copy-of select="child::node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring" mode="nopara">
+ <xsl:copy-of select="child::node()"/>
+ </xsl:template>
+
+ <xsl:template match="tp:added">
+ <para>
+ Added in version <xsl:value-of select="@version"/>.
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:changed">
+ <xsl:choose>
+ <xsl:when test="node()">
+ <para>
+ Changed in version <xsl:value-of select="@version"/>:
+ <xsl:apply-templates select="node()"/></para>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>Changed in version
+ <xsl:value-of select="@version"/></para>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="tp:deprecated">
+ <para>
+ Deprecated since version <xsl:value-of select="@version"/>.
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:rationale">
+ <!-- TODO: special? -->
+ <para>
+ <xsl:apply-templates select="node()"/>
+ </para>
+ </xsl:template>
+
+ <xsl:template match="tp:errors">
+ <title>Errors</title>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template name="generic-types">
+ <chapter>
+ <xsl:attribute name="xml:id">types</xsl:attribute>
+ <title>Types</title>
+ <xsl:call-template name="do-types"/>
+ </chapter>
+ </xsl:template>
+
+ <xsl:template name="do-types">
+ <xsl:if test="tp:simple-type">
+ <section>
+ <title>Simple types</title>
+ <xsl:apply-templates select="tp:simple-type"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:enum">
+ <section>
+ <title>Enumerated types</title>
+ <xsl:apply-templates select="tp:enum"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:flags">
+ <section>
+ <title>Sets of flags</title>
+ <xsl:apply-templates select="tp:flags"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:struct">
+ <section>
+ <title>Struct types</title>
+ <xsl:apply-templates select="tp:struct"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:mapping">
+ <section>
+ <title>Map types</title>
+ <xsl:apply-templates select="tp:mapping"/>
+ </section>
+ </xsl:if>
+
+ <xsl:if test="tp:external-type">
+ <section>
+ <title>Types defined elsewhere</title>
+ <glosslist>
+ <xsl:apply-templates select="tp:external-type"/>
+ </glosslist>
+ </section>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="tp:error">
+ <simplesect>
+ <title>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/>
+ </xsl:attribute>
+ <literal><xsl:value-of select="concat(../@namespace, '.', translate(@name, ' ', ''))"/></literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </simplesect>
+ </xsl:template>
+
+ <xsl:template match="/tp:spec/tp:copyright">
+ <!-- TODO: use <copyright> -->
+ <legalnotice>
+ <para><xsl:apply-templates mode="text"/></para>
+ </legalnotice>
+ </xsl:template>
+ <xsl:template match="/tp:spec/tp:license">
+ <!-- TODO: right tag? -->
+ <legalnotice>
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </legalnotice>
+ </xsl:template>
+
+ <xsl:template match="tp:copyright"/>
+ <xsl:template match="tp:license"/>
+
+ <xsl:template match="interface">
+
+ <refentry>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <refmeta>
+ <refentrytitle><literal><xsl:value-of select="@name"/></literal></refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refdescriptor><xsl:value-of select="@name"/></refdescriptor>
+ <refname><xsl:value-of select="@name"/></refname>
+ <refpurpose><xsl:apply-templates select="tp:docstring" mode="nopara"/></refpurpose>
+ </refnamediv>
+
+ <xsl:if test="tp:added">
+ <refsection>
+ <xsl:apply-templates select="tp:added"/>
+ </refsection>
+ </xsl:if>
+ <xsl:if test="tp:changed">
+ <refsection>
+ <xsl:apply-templates select="tp:changed"/>
+ </refsection>
+ </xsl:if>
+ <xsl:if test="tp:deprecated">
+ <refsection>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="@tp:causes-havoc">
+ <refsection>
+ <warning>
+ <para>
+ This interface is <xsl:value-of select="@tp:causes-havoc"/>
+ and is likely to cause havoc to your API/ABI if bindings are generated.
+ Don't include it in libraries that care about compatibility.
+ </para>
+ </warning>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="tp:requires">
+ <refsection>
+ <tip>
+ <para>Implementations of this interface must also implement:</para>
+ <itemizedlist>
+ <xsl:for-each select="tp:requires">
+ <listitem>
+ <para>
+ <link linkend="{@interface}">
+ <literal><xsl:value-of select="@interface"/></literal>
+ </link>
+ </para>
+ </listitem>
+ </xsl:for-each>
+ </itemizedlist>
+ </tip>
+ </refsection>
+ </xsl:if>
+
+ <refsynopsisdiv>
+ <xsl:if test="method">
+ <refsect2>
+ <title>Methods</title>
+ <funcsynopsis>
+ <xsl:apply-templates select="method" mode="funcsynopsislinked"/>
+ </funcsynopsis>
+ </refsect2>
+ </xsl:if>
+ <xsl:if test="signal">
+ <refsect2>
+ <title>Signals</title>
+ <funcsynopsis>
+ <xsl:apply-templates select="signal" mode="funcsynopsislinked"/>
+ </funcsynopsis>
+ </refsect2>
+ </xsl:if>
+ <xsl:if test="property">
+ <refsect2>
+ <title>Properties</title>
+ <refsynopsisdiv>
+ <title> </title>
+ <xsl:apply-templates select="property" mode="fieldsynopsislinked"/>
+ </refsynopsisdiv>
+ </refsect2>
+ </xsl:if>
+ </refsynopsisdiv>
+
+ <xsl:if test="method">
+ <refsection>
+ <title>Methods</title>
+ <xsl:apply-templates select="method" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="signal">
+ <refsection>
+ <title>Signals</title>
+ <xsl:apply-templates select="signal" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="tp:property">
+ <refsection>
+ <title>Telepathy Properties</title>
+ <para>
+ Accessed using the
+ <link linkend="org.freedesktop.Telepathy.Properties">
+ <literal>org.freedesktop.Telepathy.Properties</literal>
+ </link>
+ </para>
+ <glosslist>
+ <xsl:apply-templates select="tp:property" mode="detail"/>
+ </glosslist>
+ </refsection>
+ </xsl:if>
+
+ <xsl:if test="property">
+ <refsection>
+ <title>D-Bus Properties</title>
+ <para>
+ Accessed using the org.freedesktop.DBus.Properties interface.
+ </para>
+ <xsl:apply-templates select="property" mode="detail"/>
+ </refsection>
+ </xsl:if>
+
+ <xsl:call-template name="do-types"/>
+
+ </refentry>
+
+ </xsl:template>
+
+ <xsl:template match="tp:flags">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:flags type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:flags type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <title><literal><xsl:value-of select="@name"/></literal></title>
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <glosslist>
+ <xsl:variable name="value-prefix">
+ <xsl:choose>
+ <xsl:when test="@value-prefix">
+ <xsl:value-of select="@value-prefix"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:for-each select="tp:flag">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:for-each>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:enum">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:enum type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:enum type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title><literal><xsl:value-of select="@name"/></literal></title>
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <glosslist>
+ <xsl:variable name="value-prefix">
+ <xsl:choose>
+ <xsl:when test="@value-prefix">
+ <xsl:value-of select="@value-prefix"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:for-each select="tp:enumvalue">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="concat($value-prefix, '_', @suffix)"/> = <xsl:value-of select="@value"/>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:for-each>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="property" mode="fieldsynopsis">
+
+ <fieldsynopsis>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ </xsl:attribute>
+ <modifier>
+ <xsl:choose>
+ <xsl:when test="@access = 'read'">
+ <xsl:text>READ</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'write'">
+ <xsl:text>WRITE</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'readwrite'">
+ <xsl:text>READWRITE</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: unknown or missing value for </xsl:text>
+ <xsl:text>@access on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </modifier>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <varname>
+ <xsl:value-of select="@name"/>
+ </varname>
+ </fieldsynopsis>
+
+ </xsl:template>
+
+ <xsl:template match="property" mode="fieldsynopsislinked">
+
+ <fieldsynopsis>
+ <modifier>
+ <xsl:choose>
+ <xsl:when test="@access = 'read'">
+ <xsl:text>READ</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'write'">
+ <xsl:text>WRITE</xsl:text>
+ </xsl:when>
+ <xsl:when test="@access = 'readwrite'">
+ <xsl:text>READWRITE</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: unknown or missing value for </xsl:text>
+ <xsl:text>@access on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </modifier>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <varname>
+ <xsl:attribute name="xlink:href">
+ <xsl:value-of select="concat('#', ../@name, '.', @name)"/>
+ </xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </varname>
+ </fieldsynopsis>
+
+ </xsl:template>
+
+ <xsl:template match="property" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: property </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a property of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on property </xsl:text>
+ <xsl:value-of select="concat(../@name, '.', @name)"/>
+ <xsl:text>: '</xsl:text>
+ <xsl:value-of select="@access"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="fieldsynopsis"/>
+
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </xsl:template>
+
+ <xsl:template match="tp:property" mode="detail">
+ <glossentry>
+ <glossterm>
+ <xsl:if test="@name">
+ <xsl:value-of select="@name"/> −
+ </xsl:if>
+ <xsl:value-of select="@type"/>
+ </glossterm>
+ <glossdef>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:mapping">
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="@name"/></literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:if test="string(@array-name) != ''">
+ <para>
+ In bindings that need a separate name, arrays of
+ <xsl:value-of select="@name"/> should be called
+ <xsl:value-of select="@array-name"/>.
+ </para>
+ </xsl:if>
+ <section>
+ <title>Members</title>
+ <glosslist>
+ <xsl:apply-templates select="tp:member" mode="description"/>
+ </glosslist>
+ </section>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:docstring" mode="in-index"/>
+
+ <xsl:template match="tp:simple-type | tp:enum | tp:flags | tp:external-type"
+ mode="in-index">
+ − <xsl:value-of select="@type"/>
+ </xsl:template>
+
+ <xsl:template match="tp:simple-type">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:simple-type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:simple-type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="@name"/> − <xsl:value-of select="@type"/></literal>
+ </title>
+ <para>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ </para>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="tp:external-type">
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a tp:external-type&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @type on tp:external-type</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <glossentry>
+ <glossterm>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <xsl:value-of select="@name"/> − <xsl:value-of select="@type"/>
+ </glossterm>
+ <glossdef>Defined by: <xsl:value-of select="@from"/></glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:struct" mode="in-index">
+ − ( <xsl:for-each select="tp:member">
+ <xsl:value-of select="@type"/>
+ <xsl:if test="position() != last()">, </xsl:if>
+ </xsl:for-each> )
+ </xsl:template>
+
+ <xsl:template match="tp:mapping" mode="in-index">
+ − a{ <xsl:for-each select="tp:member">
+ <xsl:value-of select="@type"/>
+ <xsl:if test="position() != last()"> &#x2192; </xsl:if>
+ </xsl:for-each> }
+ </xsl:template>
+
+ <xsl:template match="tp:struct">
+ <section>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat('type-', @name)"/>
+ </xsl:attribute>
+ <title>
+ <literal>
+ <xsl:value-of select="@name"/>
+ </literal>
+ </title>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+ <xsl:choose>
+ <xsl:when test="string(@array-name) != ''">
+ <para>In bindings that need a separate name, arrays of
+ <xsl:value-of select="@name"/> should be called
+ <xsl:value-of select="@array-name"/>.</para>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>Arrays of <xsl:value-of select="@name"/> don't generally
+ make sense.</para>
+ </xsl:otherwise>
+ </xsl:choose>
+ <classsynopsis>
+ <ooclass>
+ <modifier>struct</modifier>
+ <classname><xsl:value-of select="@name"/></classname>
+ </ooclass>
+ <xsl:apply-templates select="tp:member" mode="fieldsynopsis"/>
+ </classsynopsis>
+ <glosslist>
+ <xsl:apply-templates select="tp:member" mode="description"/>
+ </glosslist>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="arg" mode="paramdef">
+ <paramdef>
+ <xsl:call-template name="direction">
+ <xsl:with-param name="indirection" select="@direction"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <type>
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </type>
+ <xsl:text> </xsl:text>
+ <parameter><xsl:value-of select="@name"/></parameter>
+ </paramdef>
+ </xsl:template>
+
+ <xsl:template match="arg" mode="paramtable">
+ <glossentry>
+ <glossterm><literal><xsl:value-of select="@name"/></literal></glossterm>
+ <glossdef>
+ <para><xsl:apply-templates select="tp:docstring" mode="nopara"/></para>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="method|signal" mode="funcsynopsis">
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ <function>
+ <xsl:value-of select="@name"/>
+ </function>
+ </funcdef>
+ <xsl:choose>
+ <xsl:when test="arg">
+ <xsl:apply-templates select="arg" mode="paramdef"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <void/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </funcprototype>
+ </funcsynopsis>
+ </xsl:template>
+
+ <xsl:template match="method|signal" mode="funcsynopsislinked">
+ <funcprototype>
+ <funcdef>
+ <function linkend="{concat(parent::interface//@name, '.', @name)}">
+ <xsl:value-of select="@name"/>
+ </function>
+ </funcdef>
+ <xsl:choose>
+ <xsl:when test="arg">
+ <xsl:apply-templates select="arg" mode="paramdef"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <void/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </funcprototype>
+ </xsl:template>
+
+ <xsl:template match="method" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: method </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a method of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="arg">
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no type</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@direction='in'">
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an 'in' arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="@direction='out'">
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="no">
+ <xsl:text>INFO: an 'out' arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of method </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has direction neither 'in' nor 'out'</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <refsection>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+ </title>
+ <xsl:apply-templates select="." mode="funcsynopsis"/>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+
+ <xsl:if test="arg">
+ <glosslist>
+ <xsl:apply-templates select="arg" mode="paramtable"/>
+ </glosslist>
+ </xsl:if>
+
+ <xsl:if test="tp:possible-errors">
+ <formalpara>
+ <title>Possible errors</title>
+ <para>
+ <glosslist>
+ <xsl:apply-templates select="tp:possible-errors/tp:error"/>
+ </glosslist>
+ </para>
+ </formalpara>
+ </xsl:if>
+ </refsection>
+ </xsl:template>
+
+ <xsl:template name="tp-type">
+ <xsl:param name="tp-type"/>
+ <xsl:param name="type"/>
+
+ <xsl:variable name="single-type">
+ <xsl:choose>
+ <xsl:when test="contains($tp-type, '[]')">
+ <xsl:value-of select="substring-before($tp-type, '[]')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$tp-type"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="type-of-tp-type">
+ <xsl:if test="contains($tp-type, '[]')">
+ <!-- one 'a', plus one for each [ after the [], and delete all ] -->
+ <xsl:value-of select="concat('a',
+ translate(substring-after($tp-type, '[]'), '[]', 'a'))"/>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="//tp:simple-type[@name=$single-type]">
+ <xsl:value-of select="string(//tp:simple-type[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:struct[@name=$single-type]">
+ <xsl:text>(</xsl:text>
+ <xsl:for-each select="//tp:struct[@name=$single-type]/tp:member">
+ <xsl:value-of select="@type"/>
+ </xsl:for-each>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="//tp:enum[@name=$single-type]">
+ <xsl:value-of select="string(//tp:enum[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:flags[@name=$single-type]">
+ <xsl:value-of select="string(//tp:flags[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:when test="//tp:mapping[@name=$single-type]">
+ <xsl:text>a{</xsl:text>
+ <xsl:for-each select="//tp:mapping[@name=$single-type]/tp:member">
+ <xsl:value-of select="@type"/>
+ </xsl:for-each>
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+ <xsl:when test="//tp:external-type[@name=$single-type]">
+ <xsl:value-of select="string(//tp:external-type[@name=$single-type]/@type)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: Unable to find type '</xsl:text>
+ <xsl:value-of select="$tp-type"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:if test="string($type) != '' and
+ string($type-of-tp-type) != string($type)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: tp:type '</xsl:text>
+ <xsl:value-of select="$tp-type"/>
+ <xsl:text>' has D-Bus type '</xsl:text>
+ <xsl:value-of select="$type-of-tp-type"/>
+ <xsl:text>' but has been used with type='</xsl:text>
+ <xsl:value-of select="$type"/>
+ <xsl:text>'&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <link linkend="type-{$single-type}">
+ <literal><xsl:value-of select="concat('type-', $single-type)"/></literal>
+ </link>
+
+ </xsl:template>
+
+ <xsl:template name="parenthesized-tp-type">
+ <xsl:if test="@tp:type">
+ <xsl:text> (</xsl:text>
+ <xsl:call-template name="tp-type">
+ <xsl:with-param name="tp-type" select="@tp:type"/>
+ <xsl:with-param name="type" select="@type"/>
+ </xsl:call-template>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="tp:member" mode="fieldsynopsis">
+ <xsl:variable name="type">
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <fieldsynopsis>
+ <type><xsl:value-of select="normalize-space($type)"/></type>
+ <varname><xsl:value-of select="@name"/></varname>
+ </fieldsynopsis>
+ </xsl:template>
+
+ <xsl:template match="tp:member" mode="description">
+ <glossentry>
+ <glossterm>
+ <varname><xsl:value-of select="@name"/></varname>
+ </glossterm>
+ <glossdef>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring" />
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- emphasize -->
+ (undocumented)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="tp:possible-errors/tp:error">
+ <glossentry>
+ <glossterm>
+ <xsl:value-of select="@name"/>
+ </glossterm>
+ <glossdef>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:choose>
+ <xsl:when test="tp:docstring">
+ <xsl:apply-templates select="tp:docstring"/>
+ </xsl:when>
+ <xsl:when test="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring">
+ <xsl:apply-templates select="//tp:errors/tp:error[concat(../@namespace, '.', translate(@name, ' ', ''))=$name]/tp:docstring"/> <!-- TODO: emphasize -->(generic description)
+ </xsl:when>
+ <xsl:otherwise>
+ (Undocumented.)
+ </xsl:otherwise>
+ </xsl:choose>
+ </glossdef>
+ </glossentry>
+ </xsl:template>
+
+ <xsl:template match="signal" mode="detail">
+
+ <xsl:if test="not(parent::interface)">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: signal </xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text> does not have an interface as parent&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: missing @name on a signal of </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:for-each select="arg">
+ <xsl:if test="not(@type) or @type = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no type</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:if test="not(@name) or @name = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has no name</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="not(@direction)"/>
+ <xsl:when test="@direction='in'"/>
+ <!-- This doesn't work with the DTD (see comment in DTD)
+ <xsl:when test="@direction='in'">
+ <xsl:message terminate="no">
+ <xsl:text>INFO: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has unnecessary direction 'in'</xsl:text>
+ </xsl:message>
+ </xsl:when>
+ -->
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:text>ERR: an arg of signal </xsl:text>
+ <xsl:value-of select="concat(../../@name, '.', ../@name)"/>
+ <xsl:text> has direction other than 'in'</xsl:text>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <refsection>
+ <xsl:attribute name="xml:id">
+ <xsl:value-of select="concat(../@name, concat('.', @name))"/>
+ </xsl:attribute>
+ <title>
+ <literal><xsl:value-of select="concat(../@name, concat('.', @name))"/></literal>
+ </title>
+ <xsl:apply-templates select="." mode="funcsynopsis"/>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:added"/>
+ <xsl:apply-templates select="tp:changed"/>
+ <xsl:apply-templates select="tp:deprecated"/>
+
+ <xsl:if test="arg">
+ <glosslist>
+ <xsl:apply-templates select="arg" mode="paramtable"/>
+ </glosslist>
+ </xsl:if>
+ </refsection>
+ </xsl:template>
+
+ <xsl:template match="/tp:spec">
+ <book xmlns="http://docbook.org/ns/docbook" version="5.0">
+ <bookinfo>
+ <title><xsl:value-of select="tp:title"/></title>
+ <xsl:apply-templates select="tp:copyright"/>
+ <xsl:apply-templates select="tp:license"/>
+ <xsl:if test="tp:docstring">
+ <abstract>
+ <xsl:apply-templates select="tp:docstring"/>
+ </abstract>
+ </xsl:if>
+ <!-- TODO: Version
+ <xsl:if test="tp:version">
+ <xsl:text> version </xsl:text>
+ <xsl:value-of select="tp:version"/>
+ </xsl:if> -->
+ </bookinfo>
+ <chapter>
+ <xsl:attribute name="xml:id">interfaces</xsl:attribute>
+ <title>Interfaces</title>
+ <xsl:apply-templates select="//node"/>
+ </chapter>
+ <xsl:call-template name="generic-types"/>
+ <xsl:if test="tp:errors">
+ <chapter>
+ <xsl:attribute name="xml:id">errors</xsl:attribute>
+ <xsl:apply-templates select="tp:errors"/>
+ </chapter>
+ </xsl:if>
+ </book>
+ </xsl:template>
+
+ <xsl:template match="node">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="text()">
+ <xsl:if test="normalize-space(.) != ''">
+ <xsl:message terminate="yes">
+ <xsl:text>Stray text: {{{</xsl:text>
+ <xsl:value-of select="." />
+ <xsl:text>}}}&#10;</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*">
+ <xsl:message terminate="yes">
+ <xsl:text>Unrecognised element: {</xsl:text>
+ <xsl:value-of select="namespace-uri(.)" />
+ <xsl:text>}</xsl:text>
+ <xsl:value-of select="local-name(.)" />
+ <xsl:text>&#10;</xsl:text>
+ </xsl:message>
+ </xsl:template>
+</xsl:stylesheet>
+
+<!-- vim:set sw=2 sts=2 et: -->
diff --git a/secret-service/tools/spec-to-introspect.xsl b/secret-service/tools/spec-to-introspect.xsl
new file mode 100644
index 0000000..ca8b4b6
--- /dev/null
+++ b/secret-service/tools/spec-to-introspect.xsl
@@ -0,0 +1,147 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
+ exclude-result-prefixes="tp">
+
+<!--
+ Telepathy D-Bus Introspection to EggDBus Introspection format translator.
+
+ Copyright 2009 Michael Leupold <lemma@confuego.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+-->
+
+<!--
+ TODO:
+ - Enable conversion of dictionary element types (eg. "{ss}") and
+ struct types (eg. "(sayay)")
+ - unhandled: tp:simple-type, tp:enum, tp:flags, tp:external-type
+ - tp:docstring may contain XHTML which this template doesn't handle
+-->
+
+ <xsl:include href="resolve-type.xsl"/>
+
+ <!-- main template -->
+ <xsl:template match="tp:spec">
+ <node>
+ <xsl:apply-templates select="tp:errors"/>
+ <xsl:apply-templates select="tp:struct"/>
+ <!-- TODO: <xsl:apply-templates select="tp:mapping"/> -->
+ <xsl:apply-templates select="node/interface"/>
+ </node>
+ </xsl:template>
+
+ <!-- handle most of the D-Bus introspection elements -->
+ <xsl:template match="interface|annotation|method|signal">
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:if test="not(starts-with(name(), 'tp:'))">
+ <xsl:copy/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- handle the arg and property D-Bus introspection elements.
+ They get special handling because they may contain a tp:type
+ attribute -->
+ <xsl:template match="arg|property">
+ <xsl:copy>
+ <xsl:for-each select="@*">
+ <xsl:choose>
+ <xsl:when test="not(starts-with(name(), 'tp:'))">
+ <xsl:copy/>
+ </xsl:when>
+ <xsl:when test="name() = 'tp:type'">
+ <xsl:variable name="type">
+ <xsl:call-template name="TpType">
+ <xsl:with-param name="type" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <annotation name="org.gtk.EggDBus.Type">
+ <xsl:attribute name="value">
+ <xsl:value-of select="translate(translate($type, ' ', ''), '&#xa;', '')"/>
+ </xsl:attribute>
+ </annotation>
+ </xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:for-each>
+ <xsl:apply-templates/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- tp:docstring to org.gtk.EggDBus.DocString -->
+ <xsl:template match="tp:docstring">
+ <annotation name="org.gtk.EggDBus.DocString">
+ <xsl:attribute name="value">
+ <xsl:value-of select="normalize-space(text())"/>
+ </xsl:attribute>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:errors to org.gtk.EggDBus.DeclareErrorDomain -->
+ <xsl:template match="tp:errors">
+ <annotation value="Error" name="org.gtk.EggDBus.DeclareErrorDomain">
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:error"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:error to org.gtk.EggDBus.ErrorDomain.Member -->
+ <xsl:template match="tp:error">
+ <annotation name="org.gtk.EggDBus.ErrorDomain.Member">
+ <xsl:attribute name="value">
+ <xsl:value-of select="concat(../@namespace, '.', @name)"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:struct to org.gtk.EggDBus.DeclareStruct -->
+ <xsl:template match="tp:struct">
+ <annotation name="org.gtk.EggDBus.DeclareStruct">
+ <xsl:attribute name="value">
+ <xsl:value-of select="@name"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ <xsl:apply-templates select="tp:member"/>
+ </annotation>
+ </xsl:template>
+
+ <!-- tp:member to org.gtk.EggDBus.Struct.Member -->
+ <xsl:template match="tp:member">
+ <xsl:variable name="type">
+ <xsl:call-template name="ResolveType">
+ <xsl:with-param name="node" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <annotation name="org.gtk.EggDBus.Struct.Member">
+ <xsl:attribute name="value">
+ <xsl:value-of select="concat(normalize-space($type), ':', @name)"/>
+ </xsl:attribute>
+ <xsl:apply-templates select="tp:docstring"/>
+ </annotation>
+ </xsl:template>
+
+ <xsl:template match="text()"/>
+
+ <xsl:output method="xml" indent="yes" encoding="UTF-8"
+ omit-xml-declaration="no"
+ doctype-system="http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
+ doctype-public="-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"/>
+
+</xsl:stylesheet>
diff --git a/systemtray/ChangeLog b/systemtray/ChangeLog
new file mode 100644
index 0000000..9487095
--- /dev/null
+++ b/systemtray/ChangeLog
@@ -0,0 +1,10 @@
+2009-01-10 Vincent Untz <vuntz@gnome.org>
+
+ * systemtray-spec.xml: add _NET_SYSTEM_TRAY_VISUAL and a description of
+ visual and background pixmap handling..
+ See http://lists.freedesktop.org/archives/xdg/2009-January/010122.html
+
+2004-11-30 Mark McLoughlin <mark@skynet.ie>
+
+ * systemtray-spec.xml: add _NET_SYSTEM_TRAY_ORIENTATION.
+
diff --git a/systemtray/systemtray-spec.xml b/systemtray/systemtray-spec.xml
new file mode 100644
index 0000000..669f3d0
--- /dev/null
+++ b/systemtray/systemtray-spec.xml
@@ -0,0 +1,444 @@
+<?xml version="1.0"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<article id="index">
+ <articleinfo>
+ <title>System Tray Protocol Specification</title>
+ <releaseinfo>Version 0.3</releaseinfo>
+ <date>10 January 2009</date>
+ <authorgroup>
+ <author>
+ <firstname>Havoc</firstname>
+ <surname>Pennington</surname>
+ <affiliation>
+ <address>
+ <email>hp@redhat.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+ </articleinfo>
+
+ <sect1 id="overview">
+ <title>Overview</title>
+ <para>
+ The "system tray" is an application running on a given X screen
+ that can display small icons provided by running
+ applications. Windows XP calls this feature the "notification area."
+ <footnote><para>According to the MSDN documentation for the
+ <literal>Shell_NotifyIcon()</literal> function,
+ "The taskbar notification area is sometimes erroneously called
+ the 'tray.'" So presumably "notification area" is the official
+ term on Windows. Parts of the docs also call it the "status
+ area."</para></footnote> Inspired by KDE, this specification
+ uses the term "system tray."
+ </para>
+ <para>
+ From a UI standpoint, the system tray is normally used for
+ transient icons that indicate some special state, while
+ full-blown "applets" are used for permanent dock/panel
+ features. For example, a system tray icon might appear to tell
+ the user that they have new mail, or have an incoming instant
+ message, or something along those lines.
+ </para>
+ <para>
+ The basic idea is that creating an icon in the notification
+ area is less annoying than popping up a dialog. However it's
+ also harder to notice, so Windows XP adds a feature allowing
+ tray icons to pop up small message balloons. (Users can disable
+ these via a hidden registry setting.) This specification
+ also supports the balloon feature.
+ </para>
+ </sect1>
+
+ <sect1 id="definitions">
+ <title>Definitions</title>
+ <variablelist>
+ <varlistentry>
+ <term>System tray</term>
+ <listitem>
+ <para>
+ The system tray is an X client which owns a special
+ manager selection on a given screen and provides
+ container windows.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Selection owner window</term>
+ <listitem>
+ <para>
+ The selection owner window is the window belonging to the
+ System Tray that owns the manager selection (as in
+ <literal>XGetSelectionOwner()</literal>/<literal>XSetSelectionOwner()</literal>.
+ Note that this probably is not the same window that's used
+ to contain the system tray icons.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Tray icon</term>
+ <listitem>
+ <para>
+ The tray icon is a window to be embedded in the
+ system tray.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect1>
+
+ <sect1 id="locating">
+ <title>Locating the system tray</title>
+ <para>
+ On startup, the system tray must acquire a manager selection
+ called <literal>_NET_SYSTEM_TRAY_Sn</literal>, replacing
+ <literal>n</literal> with the screen number the tray wants to
+ use. The conventions for manager selections are defined in the
+ ICCCM.
+ </para>
+ <para>
+ Because the selection owner window should be destroyed when the
+ manager selection is lost, normally the selection owner window
+ will not be the same as any of the user-visible windows provided
+ by the system tray.
+ </para>
+ <para>
+ A system tray that fails to get the selection or loses the
+ selection should assume that another system tray is running,
+ and let the selection owner handle tray icons.
+ </para>
+ <para>
+ An application wishing to provide an icon to the system tray
+ should first locate the system tray by requesting the owner
+ window of the manager selection. If the manager selection has no
+ owner, clients may use the method described in the ICCCM
+ (watching for a <literal>MANAGER</literal> client message) to be
+ notified when a system tray appears.
+ </para>
+ </sect1>
+
+ <sect1 id="messages">
+ <title>Opcode messages</title>
+ <para>
+ Tray icons can send "opcodes" to
+ the system tray. These are X client messages, sent with
+ <literal>NoEventMask</literal>, a
+ <literal>message_type</literal> of
+ <literal>_NET_SYSTEM_TRAY_OPCODE</literal>, and format 32.
+ The first data field in the message is a timestamp (the stamp
+ of the current event, if available, otherwise CurrentTime).
+ The second data field is an integer indicating the op code
+ of the message:
+ <programlisting>
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+ </programlisting>
+ The content remaining three data fields depends on the type of
+ message being sent. If they are unused by a particular
+ message, they should always be set to 0.
+ </para>
+ <para>
+ Here is an example of how to send a client message:
+ <programlisting><!--
+-->#include &lt;X11/Xlib.h&gt;
+
+void send_message(
+ Display* dpy, /* display */
+ Window w, /* sender (tray icon window) */
+ long message, /* message opcode */
+ long data1 /* message data 1 */
+ long data2 /* message data 2 */
+ long data3 /* message data 3 */
+){
+ XEvent ev;
+
+ memset(&amp;ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = x_time;
+ ev.xclient.data.l[1] = message;
+ ev.xclient.data.l[2] = data1;
+ ev.xclient.data.l[3] = data2;
+ ev.xclient.data.l[4] = data3;
+
+ trap_errors();
+ XSendEvent(dpy, w, False, NoEventMask, &amp;ev);
+ XSync(dpy, False);
+ if (untrap_errors()) {
+ /* Handle failure */
+ }
+}<!--
+ --></programlisting>
+
+ </para>
+
+ </sect1>
+
+ <sect1 id="docking">
+ <title>Docking a tray icon</title>
+ <para>
+ A tray icon must support the "client" or "plug" side of the
+ XEMBED specification. XEMBED is a protocol for cross-toolkit
+ widget embedding.
+ </para>
+ <para>
+ To begin the docking process, the tray icon application sends
+ a client message event to the manager selection owner window,
+ as described in <xref linkend="messages"/>. This event
+ should contain the <literal>SYSTEM_TRAY_REQUEST_DOCK</literal>
+ opcode, <literal>xclient.data.l[2]</literal> should contain
+ the X window ID of the tray icon to be docked.
+ </para>
+ <para>
+ At this point the "embedding life cycle" explained in the XEMBED
+ specification begins. The XEMBED specification explains how the
+ embedding application will interact with the embedded tray
+ icon, and how the embedder/embedded relationship may be ended.
+ </para>
+ <para>
+ Tray icons may be assigned any size by the system tray, and
+ should do their best to cope with any size effectively.
+ </para>
+ </sect1>
+
+ <sect1 id="visuals">
+ <title>Visual and background pixmap handling</title>
+ <para>
+ If the _NET_SYSTEM_TRAY_VISUAL property (see below) is present,
+ tray icon windows should be created using that visual. If the
+ property is not present, then tray icon windows should be
+ created using the default visual of the screen.
+ </para>
+ <para>
+ Historically, to allow the appearance of icons with transparent
+ backgrounds on servers that did not support visuals with an
+ alpha channel or the Composite extension, a convention was
+ adopted where a background pixmap was set on the XEMBED embedder
+ window, aligned properly to match the contents of the embedder
+ window's parent, the tray icon window was set to have a
+ background of ParentRelative, and drawing of the icon done on
+ top of this background.
+ </para>
+ <para>
+ Setting the background of a window to ParentRelative when the
+ depth of the window does not match the depth of the window's
+ parent, or reparenting a window with a ParentRelative
+ background into a parent with a non-matching depth produces a
+ BadMatch error, so the embedder window must be created to match
+ the visual of the tray icon window, even if the tray icon window
+ does not have the visual provided in _NET_SYSTEM_TRAY_VISUAL. If
+ convenient, the tray manager may set an appropriate background
+ pixmap on the embedder window to provide the appearance of
+ transparency. However, the preferred method of transparency is
+ to use a visual with an alpha channel in _NET_SYSTEM_TRAY_VISUAL.
+ </para>
+ </sect1>
+
+ <sect1 id="hints">
+ <title>Tray icon hints</title>
+ <para>
+ Tray icons should set the following hints to help the system
+ tray provide a nice user interface. The name and icon hints
+ are used if the system tray needs to refer to a tray icon;
+ for example, the system tray may present a list of tray
+ icons and let the user reorder them or change their properties.
+ </para>
+
+ <sect2><title>_NET_WM_NAME</title>
+ <programlisting><![CDATA[
+_NET_WM_NAME, UTF8_STRING
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the Extended Window Manager Hints
+ Specification (EWMH). The hint MUST be in UTF-8 encoding. It
+ provides a human-readable, localized name for the tray icon.
+ </para>
+ </sect2>
+
+ <sect2><title>WM_CLASS</title>
+ <programlisting><![CDATA[
+WM_CLASS, STRING
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the ICCCM. The system tray can use it
+ to distinguish different kinds of tray icon. This is useful
+ for example if the system tray wants to save and restore the
+ positions of the icons in the tray.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>_NET_WM_ICON</title>
+ <programlisting><![CDATA[
+_NET_WM_ICON CARDINAL[][2+n]/32
+]]></programlisting>
+ <para>
+ This hint should be set as it would be for a normal toplevel
+ window, as defined in the Extended Window Manager Hints
+ Specification (EWMH). See that specification for the format
+ of the icon data.
+ </para>
+ </sect2>
+
+ </sect1>
+
+ <sect1 id="manager-hints">
+ <title>Tray manager hints</title>
+ <para>
+The tray manager should set the following hints on the selection
+owner window.
+ </para>
+
+ <sect2>
+ <title>_NET_SYSTEM_TRAY_ORIENTATION</title>
+ <programlisting><![CDATA[
+_NET_SYSTEM_TRAY_ORIENTATION orientation CARDINAL/32
+]]>
+ #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
+ #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
+ </programlisting>
+
+ <para>
+The property should be set by the tray manager to indicate the current
+orientation of the tray. Tray icons may use this hint in order to
+maintain the icon's aspect ratio and also as an indication of how the
+icon contents should be laid out.
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>_NET_SYSTEM_TRAY_VISUAL</title>
+ <programlisting><![CDATA[
+_NET_SYSTEM_TRAY_VISUAL visual_id VISUALID/32
+]]>
+ </programlisting>
+
+ <para>
+The property should be set by the tray manager to indicate the preferred
+visual for icon windows. To avoid ambiguity about the colormap to use
+this visual must either be the default visual for the screen or it
+must be a TrueColor visual. If this property is set to a visual with
+an alpha channel, the tray manager must use the Composite extension to
+composite the icon against the background using PictOpOver.
+ </para>
+
+ </sect2>
+ </sect1>
+
+ <sect1 id="balloon">
+ <title>Balloon messages</title>
+ <para>
+ Tray icons may ask the system tray to display a balloon message
+ to the user. The system tray coordinates balloon messages
+ to ensure that they have a consistent look-and-feel, and to
+ avoid displaying multiple balloon messages at once.
+ </para>
+ <para>
+ A balloon message is a short text message to be displayed to
+ the user. The message may have a timeout; if so, the message
+ will be taken down after the timeout expires. Messages are
+ displayed in a queue, as only one can appear at a time;
+ if a message has a timeout, the timer begins when the message
+ is first displayed. Users may be allowed to close messages at
+ any time, and may be allowed to disable all message display.
+ </para>
+ <para>
+ System trays may display balloon messages in any way they
+ see fit; for example, instead of popping up a balloon, they
+ could choose to put a special indicator around icons with
+ pending messages, and display the message on mouseover.
+ </para>
+ <para>
+ Balloon messages are sent from the tray icon to the system tray
+ selection owner window as a series of client messages. The first
+ client message is an opcode message, and contains the usual timestamp,
+ and the op code <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>.
+ <literal>xclient.data.l[2]</literal> contains the timeout in
+ thousandths of a second or zero for infinite timeout,
+ <literal>xclient.data.l[3]</literal> contains the length
+ of the message string in bytes, not including any nul bytes, and
+ <literal>xclient.data.l[4]</literal> contains an ID number
+ for the message. This ID number should never be reused by
+ the same tray icon. (The simplest way to generate the ID number
+ is to increment it with each message sent.)
+ </para>
+ <para>
+ Following the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>
+ op code, the tray icon should send a series of client messages
+ with a <literal>message_type</literal> of
+ <literal>_NET_SYSTEM_TRAY_MESSAGE_DATA</literal>. These client
+ messages must have their <literal>window</literal> field set
+ to the window ID of the tray icon, and have a
+ <literal>format</literal> of 8.
+ </para>
+ <para>
+ Each <literal>_NET_SYSTEM_TRAY_MESSAGE_DATA</literal> message
+ contains 20 bytes of the message string, up to the length given
+ in the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal> opcode.
+ If the message string is zero-length, then no messages need be
+ sent beyond the <literal>SYSTEM_TRAY_BEGIN_MESSAGE</literal>.
+ A terminating nul byte should never be sent.
+ </para>
+ <para>
+ System trays may receive portions of messages from several
+ tray icons at once, so are required to reassemble the messages
+ based on the window ID of the tray icon.
+ </para>
+ <para>
+ The tray icon may wish to cancel a previously-sent balloon
+ message. To do so, it sends a
+ <literal>SYSTEM_TRAY_CANCEL_MESSAGE</literal> opcode with
+ <literal>data.l[2]</literal> set to the ID number of the message
+ to cancel.
+ </para>
+ </sect1>
+
+ <appendix id="changes">
+ <title>Change history</title>
+ <formalpara>
+ <title>Version 0.3, 10 January 2009, Owen Taylor</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Added the _NET_SYSTEM_TRAY_VISUAL hint and a
+ description of visual and background pixmap handling.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>Version 0.2, 23 November 2004, Mark McLoughlin</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Added the _NET_SYSTEM_TRAY_ORIENTATION hint.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ <formalpara>
+ <title>Version 0.1, 20 April 2002, Havoc Pennington</title>
+ <para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ Created initial draft.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </formalpara>
+ </appendix>
+</article>