summaryrefslogtreecommitdiffstats
path: root/menu
diff options
context:
space:
mode:
Diffstat (limited to 'menu')
-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.xml2392
-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
114 files changed, 5785 insertions, 0 deletions
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..7bd8568
--- /dev/null
+++ b/menu/menu-spec.xml
@@ -0,0 +1,2392 @@
+<!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.
+ </para>
+ <para>
+ Category-based menus based on the Main Categories listed in this
+ specification do not provide a complete ontology for all
+ available applications. Category-based menu implementations
+ SHOULD therefore provide a "catch-all" submenu for applications
+ that cannot be appropriately placed elsewhere.
+ </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. 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>MATE</entry><entry>MATÉ Desktop</entry>
+ </row><row>
+ <entry>Razor</entry><entry>Razor-qt 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