diff options
author | lanius <lanius> | 2003-07-09 14:21:53 +0000 |
---|---|---|
committer | lanius <lanius> | 2003-07-09 14:21:53 +0000 |
commit | c8475bef1d1f84afcc89c710b6b5b214f11a670f (patch) | |
tree | 36b2646795ef447b7c507150eae8e172fe0ed295 /menu | |
parent | 4f78e6cdc2263594b6c185b7d86e95c7d518ca70 (diff) | |
download | xdg-specs-c8475bef1d1f84afcc89c710b6b5b214f11a670f.tar.xz |
added module
Diffstat (limited to 'menu')
-rw-r--r-- | menu/menu-spec.xml | 1507 |
1 files changed, 1507 insertions, 0 deletions
diff --git a/menu/menu-spec.xml b/menu/menu-spec.xml new file mode 100644 index 0000000..d48e2aa --- /dev/null +++ b/menu/menu-spec.xml @@ -0,0 +1,1507 @@ +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ + ]> +<article id="index"> + <artheader> + <title>Desktop Menu Specification</title> + <releaseinfo>Version 0.4</releaseinfo> + <date>31 May 2003</date> + <authorgroup> + <author> + <firstname>Waldo</firstname> + <surname>Bastian</surname> + <affiliation> + <address> + <email>waldo@kde.org</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> + </authorgroup> + </artheader> + + <sect1 id="introduction"> + <title>Introduction</title> + <para> + This DRAFT 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.html">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> + </sect1> + <sect1 id="paths"> + <title>File locations</title> + <para> + Files involved in this specification are located according to the "desktop + base directory specification" which can be found on <ulink + url="http://www.freedesktop.org/standards/">www.freedesktop.org</ulink>. + </para> + <para> + Here are the files defined by this specification: + <variablelist> + <varlistentry> + <term><varname>$XDG_CONFIG_DIRS</varname>/menus/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 applications.menu, it replaces the systemwide one. + (Though the user's menu may explicitly merge the systemwide one.) + </para> + <para> + Other menu files may exist, but are not specified in this + document. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><varname>$XDG_CONFIG_DIRS</varname>/menus/<replaceable>menu-file-basename</replaceable>-merged/</term> + <listitem> + <para> + One of the default merge directories included in the + <DefaultMergeDirs> element. By convention, third parties + may add new <Menu> files in this + location. <replaceable>menu-file-basename</replaceable> means the + "applications" from "applications.menu" for example. So the merge + directory would be "applications-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 <DefaultAppDirs> 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 + <DefaultAppDirs>, 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 <DefaultDirectoryDirs> 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 + <DefaultDirectoryDirs>, 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 two new fields to <ulink +url="http://www.freedesktop.org/standards/desktop-entry-spec.html">desktop +entries</ulink>: <varname>Categories</varname> and + <varname>OnlyShowIn</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 & + 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> + <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, conform to the menu file DTD, + and end in the extension ".menu". DTD conformance implies that + implementation-specific extensions to the file format are not allowed; + implementations are expected to stop processing if they encounter XML + elements or attributes that are not specified in this document. + </para> + <sect2 id="menu-file-doctype"> + <title>Doctype 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 1.0</term> + <listitem> + <para> + <literal>PUBLIC "-//freedesktop//DTD Menu 1.0//EN"</literal> + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>System Identifier for 1.0</term> + <listitem> + <para> + <literal>http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd</literal> + </para> + </listitem> + </varlistentry> + </variablelist> + + Here is a sample doctype declaration: + <informalexample> + <programlisting> + <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" + "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd"> + </programlisting> + </informalexample> + + All menu files MUST include the doctype 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><Menu></term> + <listitem> + <para> + The root element is <Menu>. Each <Menu> element may + contain any number of nested <Menu> elements, indicating submenus. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><AppDir></term> + <listitem> + <para> + This element may only appear below <Menu>. 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 <Menu> and its submenus. + </para> + <para> + Desktop entries in the pool of available entries are identified + by their <firstterm>relative path</firstterm> (see <xref + linkend="term-relative-path"/>). + </para> + <para> + <AppDir> elements appearing later in the menu file have + priority in case of collisions between relative paths. + </para> + <para> + If the filename given as an <AppDir> is not an absolute + path, it should be located relative to the location of the menu + file being parsed. An <AppDir> should be recursively + scanned, finding all desktop entries below the given directory. + Only files ending in ".desktop" should be used, other files are + ignored. + </para> + <para> + Duplicate <AppDir> 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 + <AppDir> elements with respect to <Include> and + <Exclude> elements is not relevant, also to facilitate + merging. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><DefaultAppDirs></term> + <listitem> + <para> + This element may only appear below <Menu>. The element has + no content. The element should be treated as if it were a list + of <AppDir> elements containing the default app dir + locations + (<replaceable>datadir</replaceable>/applications/ etc.). When expanding + <DefaultAppDirs> to a list of <AppDir>, the default + locations that are earlier in the search path go later in the + <Menu> so that they have priority. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><DirectoryDir></term> + <listitem> + <para> + This element may only appear below <Menu>. The content of + this element is a directory name. Each directory listed in a + <DirectoryDir> element will be searched for directory + entries to be used when resolving the <Directory> element + for this menu and its submenus. + If the filename given as a <DirectoryDir> 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 <DirectoryDir> elements (that specify the same + directory) are handled as with duplicate <AppDir> + elements (the last duplicate is used). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><DefaultDirectoryDirs></term> + <listitem> + <para> + This element may only appear below <Menu>. The element has + no content. The element should be treated as if it were a list + of <DirectoryDir> 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 + <Menu> so that they have priority. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Name></term> + <listitem> + <para> + Each <Menu> element must have a single <Name> + element. The content of the <Name> element is a name to + be used when referring to the given menu. Each submenu of a + given <Menu> must have a unique name. <Menu> + elements can thus be referenced by a menu path, for example + "Applications/Graphics." The <Name> 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><Directory></term> + <listitem> + <para> + Each <Menu> element has any number of <Directory> + elements. The content of the <Directory> element + is the relative path of a directory entry containing metainformation + about the <Menu>, such as its icon and localized name. + If no <Directory> is specified for a <Menu>, + its <Name> field should be used as the user-visible + name of the menu. + </para> + <para> + Duplicate <Directory> elements are allowed in order + to simplify menu merging, and allow user menus to override + system menus. The last <Directory> 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><OnlyUnallocated> and <NotOnlyUnallocated></term> + <listitem> + <para> + Each <Menu> may contain any number of + <OnlyUnallocated> and <NotOnlyUnallocated> + elements. Only the last such element to appear is relevant, as + it determines whether the <Menu> can contain any desktop + entries, or only those desktop entries that do not match other + menus. If neither <OnlyUnallocated> nor + <NotOnlyUnallocated> elements are present, the default + is <NotOnlyUnallocated>. + </para> + <para> + To handle <OnlyUnallocated>, the menu file must be + analyzed in two conceptual passes. The first pass processes + <Menu> elements that can match any desktop entry. During + this pass, each desktop entry is marked according to whether it + was included in some <Menu>. The second pass processes + only <Menu> elements that are restricted to unallocated + desktop entries. During the second pass, queries may only match + desktop entries that were not allocated to some menu during the + first pass. See <xref linkend="query-algorithm"/>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Deleted> and <NotDeleted></term> + <listitem> + <para> + Each <Menu> may contain any number of <Deleted> and + <NotDeleted> elements. Only the last such element to + appear is relevant, as it determines whether the <Menu> + has been deleted. If neither <Deleted> nor + <NotDeleted> elements are present, the default is + <NotDeleted>. The purpose of this element is to support + menu editing. If a menu contains a <Deleted> element + not followed by a <NotDeleted> element, that menu + should be ignored. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Include></term> + <listitem> + <para> + An <Include> element is a set of rules attempting to match + some of the known desktop entries. The <Include> element + contains a list of any number of matching rules. Matching rules + are specified using the elements <And>, <Or>, + <Not>, <All>, <Filename>, and + <Category>. 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> + <Include> elements must appear immediately under + <Menu> elements. The desktop entries they match are + included in the menu. <Include> and <Exclude> + elements for a given <Menu> 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 <Include> and <Exclude> elements. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Exclude></term> + <listitem> + <para> + Any number of <Exclude> elements may appear below a + <Menu> element. The content of an <Exclude> element + is a list of matching rules, just as with an + <Include>. However, the desktop entries matched are + removed from the list of desktop entries included so far. (Thus + an <Exclude> element that appears before any + <Include> elements will have no effect, for example, as no + desktop entries have been included yet.) + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Filename></term> + <listitem> + <para> + The <Filename> element is the most basic matching rule. + It matches a desktop entry if the desktop entry has the given + relative path. Absolute paths <emphasis>must not be used</emphasis>, + and relative paths must be the canonical form used to reference + a desktop entry in the pool of desktop entries (they can't + contain ".." or anything of that nature, and can't contain + extra "/" characters). See <xref linkend="term-relative-path"/>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Category></term> + <listitem> + <para> + The <Category> 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><All></term> + <listitem> + <para> + The <All> element is a matching rule that matches + all desktop entries. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><And></term> + <listitem> + <para> + The <And> element contains a list of matching rules. + If each of the matching rules inside the <And> + element match a desktop entry, then the entire + <And> rule matches the desktop entry. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Or></term> + <listitem> + <para> + The <Or> element contains a list of matching rules. + If any of the matching rules inside the <Or> + element match a desktop entry, then the entire + <Or> rule matches the desktop entry. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Not></term> + <listitem> + <para> + The <Not> element contains a list of matching rules. If + any of the matching rules inside the <Not> element matches + a desktop entry, then the entire <Not> rule does + <emphasis>not</emphasis> match the desktop entry. That is, + matching rules below <Not> have a logical OR relationship. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><MergeFile></term> + <listitem> + <para> + Any number of <MergeFile> elements may be listed below a + <Menu> 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 <Menu> of the + merged file will be merged into the immediate parent of the + <MergeFile> element. The <Name> element of the + root <Menu> of the merged file are ignored. If the + filename given as a <MergeFile> is not an absolute path, + it should be located relative to the location of the menu file + being parsed. + </para> + <para> + Duplicate <MergeFile> elements (that specify the same + file) are handled as with duplicate <AppDir> + elements (the last duplicate is used). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><MergeDir></term> + <listitem> + <para> + Any number of <MergeDir> elements may be listed below a + <Menu> element. A <MergeDir> 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 + <MergeFile> would be. If the filename given as a + <MergeDir> 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 <MergeDir> elements (that specify the same + directory) are handled as with duplicate <AppDir> + elements (the last duplicate is used). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><DefaultMergeDirs></term> + <listitem> + <para> + This element may only appear below <Menu>. The element has + no content. The element should be treated as if it were a list + of <MergeDir> elements containing the default merge + directory locations. When expanding <DefaultMergeDirs> to a + list of <MergeDir>, the default locations that are earlier + in the search path go later in the <Menu> so that they + have priority. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><LegacyDir></term> + <listitem> + <para> + This element may only appear below <Menu>. The text + content of this element is a directory name. Each directory + listed in a <LegacyDir> 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 <LegacyDir> is not an absolute path, + it should be located relative to the location of the menu file + being parsed. + </para> + <para> + Duplicate <LegacyDir> elements (that specify the same + directory) are handled as with duplicate <AppDir> + elements (the last duplicate is used). + </para> + <para> + The <LegacyDir> element may have one attribute, + <literal>prefix</literal>. Normally, given a <LegacyDir> + <filename>/foo/bar</filename> and desktop entry + <filename>/foo/bar/baz/Hello.desktop</filename> the desktop + entry would be included as + <filename>baz/Hello.desktop</filename>. Given a prefix of + <literal>boo-</literal>, it would instead be loaded as + <filename>boo-Hello.desktop</filename>. In other words, if a + prefix is present it replaces the relative path to the desktop + or directory entry. The prefix may contain path separator ('/') + characters. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><KDELegacyDirs></term> + <listitem> + <para> + This element may only appear below <Menu>. The element has + no content. The element should be treated as if it were a list + of <LegacyDir> elements containing the traditional desktop + file locations supported by KDE. When expanding + <KDELegacyDirs> to a list of <LegacyDir>, the + locations that are earlier in the search path go later in the + <Menu> so that they have priority. + [FIXME what are the exact <LegacyDir prefix="???"> that + this expands to?] + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Move></term> + <listitem> + <para> + This element may only appear below <Menu>. The + <Move> element contains pairs of <Old>/<New> + elements indicating how to rename a descendant of the current + <Menu>. If the destination path already exists, the moved + menu is merged with the destination menu (see <xref + linkend="merge-algorithm"/> for details). + </para> + <para> + <Move> is used primarily to fix up legacy directories. + For example, say you are merging a <LegacyDir> 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> + <Move> may or may not be useful for implementing menu + editing, see <xref linkend="menu-editing"/>. + </para> + <para> + Duplicate <Move> elements are merged as specified in <xref + linkend="merge-algorithm"/>. Note that duplicates (two moves of + the same path) can be detected easily because of the following + rule: a move must reside at the lowest possible point. + </para> + <para> + In other words, all moves have exactly one permissible location + in the <Menu> hierarchy. Implementations can trivially + detect violations of this rule: the <Old> and <New> + paths may never share a common prefix. Moving "Foo/Bar" to + "Foo/Baz" must be done with a <Move> element that's a + child of the "Foo" <Menu>, i.e. by moving "Bar" to "Baz", not + by moving "Foo/Bar" to "Foo/Baz". + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><Old></term> + <listitem> + <para> + This element may only appear below <Move>, and + must be followed by a <New> element. The content of both + <Old> and <New> should be a menu path + (slash-separated concatenation of <Name> fields, see + <xref linkend="term-menu-path"/>). + Paths are interpreted relative to the menu containing + the <Move> element. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><New></term> + <listitem> + <para> + This element may only appear below <Move>, and must + be preceded by an <Old> element. The <New> element + specifies the new path for the preceding <Old> element. + </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 <MergeFile> 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 <Menu> and a merged <Menu>. The base + is the "target" menu and the merged <Menu> 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 + <MergeFile>, <MergeDir>, or <LegacyDir> element, replace + the <MergeFile>, <MergeDir>, or <LegacyDir> element with + the child elements of the root <Menu> of the file(s) being + merged. As a special exception, remove the <Name> element from the + root element of each file being merged. To generate a + <Menu> based on a <LegacyDir>, see + <xref linkend="legacy-hierarchies"/>. + </para> + <para> + Continue processing until no <MergeFile>, <MergeDir>, or + <LegacyDir> 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: + <orderedlist> + <listitem> + <para> + Consolidate child menus. Each group of child <Menu>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 <Menu> elements, keeping the + last one. + </para> + </listitem> + <listitem> + <para> + Expand <DefaultAppDirs> and <DefaultDirectoryDirs> + elements to <AppDir> and <DirectoryDir> elements. + Consolidate duplicate <AppDir>, <DirectoryDir>, + and <Directory> elements by keeping the last one. + For <Directory> 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 <Menu>, performing this list of + steps for each child in order. + </para> + </listitem> + <listitem> + <para> + Resolve duplicate <Move> operations (with the same + origin path) by keeping the last one. + </para> + </listitem> + <listitem> + <para> + Execute <Move> operations in the order that they appear. If + the destination path does not exist, simply relocate the origin + <Menu> element, and change its <Name> field to match the + destination path. If the origin path does not exist, do nothing. + If both paths exist, take the origin <Menu> element, delete + its <Name> element, and prepend its remaining child elements + to the destination <Menu> element. Then, recursively re-run + the previous merge steps on the resulting combined <Menu> in order to + consolidate duplicates. + </para> + </listitem> + <listitem> + <para> + For each <Menu> containing a <Deleted> element which is + not followed by a <NotDeleted> element, remove that menu. + </para> + </listitem> + </orderedlist> + </para> + + <para> + Merged menu elements are kept in order because <Include> and + <Exclude> elements later in the file override <Include> and + <Exclude> 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 <MergeFile> + that incorporates the system file. + </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 <Menu>, 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 <Include> and <Exclude> + directives. + </para> + <para> + For each <Menu> element, build a pool of desktop entries by + collecting entries found in each <AppDir> for the menu element. If + two entries have the same relative path, the entry for the earlier (closer + to the top of the file) <AppDir> must be discarded. Next, add to the + pool the entries for any <AppDir>s specified by ancestor + <Menu> elements. If a parent menu has a duplicate entry (same + relative path), the entry for the child menu has priority. + </para> + <para> + Next, walk through all <Include> and <Exclude> statements. + For each <Include>, 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. For each <Exclude>, match + the rules against the currently-included desktop entries. For each + desktop entry that matches, remove it again from the menu. + </para> + <para> + Two passes are necessary, once for regular menus, and + once for <OnlyUnallocated> menus. + </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 + <Menu>, and then this menu layout is merged with the menu that + specified <LegacyDir>. + </para> + <para> + Desktop entries in the legacy hierarchy should be added to the pool of + desktop entries as if the <LegacyDir> were an + <AppDir>. Directory entries in the legacy hierarchy should be added + to the pool of directory entries as if the <LegacyDir> were a + <DirectoryDir>. This can be trivially implemented by adding + appropriate <AppDir> and <DirectoryDir> statements to the root + legacy <Menu>. There is one slight complexity, namely the + "prefix" attribute of <LegacyDir>. + </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 + <Menu> is created with the same <Name> + as the directory on disk. + </para> + </listitem> + <listitem> + <para> + This menu then contains an <Include> element that includes + each desktop entry in the directory. That is, it should have a + + <Filename><replaceable>Foo/Bar/foo.desktop</replaceable></Filename> + 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 <Include> 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 <Directory> 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 <LegacyDir> and an <AppDir>, its desktop + entries should be labeled "Legacy" only if the <LegacyDir> + appears later in the file than the <AppDir>.) + </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 <Menu>: + <informalexample> + <programlisting> + <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" + "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd"> + + <Menu> + <AppDir>/usr/share/applnk</AppDir> + <DirectoryDir>/usr/share/applnk</DirectoryDir> + <Directory>.directory</Directory> + <Include> + <Filename>bar.desktop</Filename> + </Include> + <Menu> + <Name>System</Name> + <Directory>System/.directory</Directory> + <Include> + <Filename>System/foo.desktop</Filename> + </Include> + </Menu> + </Menu> + </programlisting> + </informalexample> + This <Menu> is then merged as if it were in a file + and loaded with <MergeFile>. + </para> + </sect1> + + <sect1 id="example"> + <title>Example Menu File</title> + <para> + <informalexample> + <programlisting> + <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" + "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd"> + + <Menu> + <Name>Applications</Name> + <Directory>Applications.directory</Directory> + + <-- Search the default locations --> + <DefaultAppDirs/> + <DefaultDirectoryDirs/> + + <-- Merge third-party submenus --> + <MergeDir>applications-merged</MergeDir> + + <-- Merge legacy hierarchy --> + <LegacyDir>/usr/share/applnk</LegacyDir> + + <-- some random moves, maybe to clean up legacy dirs, + maybe from menu editing --> + <Move> + <Old>Foo</Old> + <New>Bar</New> + <Old>Faz/Abc</Old> + <New>Baz/Xyz</New> + </Move> + + <-- A preferences submenu, kept in a separate file + so it can also be used standalone --> + <Menu> + <Name>Preferences</Name> + <Directory>Preferences.directory</Directory> + <MergeFile>preferences.menu</MergeFile> + </Menu> + + <-- An Office submenu, specified inline --> + <Menu> + <Name>Office</Name> + <Directory>Office.directory</Directory> + <Include> + <Category>Office</Category> + </Include> + <Exclude> + <Filename>foo.desktop</Filename> + </Exclude> + </Menu> + + </Menu> + </programlisting> + </informalexample> + </para> + </sect1> + + <appendix id="category-registry"> + <title>Registered Categories</title> + <para> + Remember, these are case-sensitive. + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry>Category</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry>Legacy</entry> + <entry>Keyword that must be added to menu + entries merged from legacy locations. May not actually exist in a + desktop entry (should be stripped out if found there). If the same + directory is given as both a <LegacyDir> and an + <AppDir>, its desktop entries should be labeled "Legacy" + only if the <LegacyDir> appears later in the file than the + <AppDir>.</entry> + + </row><row> + <entry>Core</entry> + <entry>Important application, core to the desktop such as a filemanager or a help browser</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>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>Screensaver</entry> + <entry>A screensaver (launching this desktop entry should activate the screensaver)</entry> + + </row><row> + <entry>TerminalEmulator</entry> + <entry>A terminal emulator application.</entry> + + </row><row> + <entry>Development</entry> + <entry>An application for development</entry> + + </row><row> + <entry>GUIDesigner</entry> + <entry>A GUI designer application</entry> + + </row><row> + <entry>IDE</entry> + <entry>IDE application</entry> + + </row><row> + <entry>TextEditor</entry> + <entry>A text editor</entry> + + </row><row> + <entry>Office</entry> + <entry>An office type application</entry> + + </row><row> + <entry>Spreadsheet</entry> + <entry>A spreadsheet</entry> + + </row><row> + <entry>WordProcessor</entry> + <entry>A word processor</entry> + + </row><row> + <entry>Presentation</entry> + <entry>Presentation software</entry> + + </row><row> + <entry>Calendar</entry> + <entry>Calendar app</entry> + + </row><row> + <entry>Email</entry> + <entry>Email application</entry> + + </row><row> + <entry>TODO</entry> + <entry>TODO list application</entry> + + </row><row> + <entry>ProjectManagement</entry> + <entry>Project management application</entry> + + </row><row> + <entry>Graphics</entry> + <entry>Graphical application</entry> + + </row><row> + <entry>VectorGraphics</entry> + <entry>Vector based graphical application (should also include 'Graphics' category)</entry> + + </row><row> + <entry>RasterGraphics</entry> + <entry>Raster based graphical application (should also include 'Graphics' category)</entry> + + </row><row> + <entry>System</entry> + <entry>System application, "System Tools" such as say a log viewer or network monitor.</entry> + + </row><row> + <entry>SystemSetup</entry> + <entry>System setup application, hardware installation, hardware clock setup, kernel setup, X server setup, etc.; i.e. system configuration tools. + </entry> + + </row><row> + <entry>PackageManager</entry> + <entry>A package manager application, should include the System keyword as well</entry> + + </row><row> + <entry>Utility</entry> + <entry>Small utility application, "Accessories"</entry> + + </row><row> + <entry>Settings</entry> + <entry>Desktop settings applications (not system settings application, those should be System;SystemSetup;)</entry> + + </row><row> + <entry>AdvancedSettings</entry> + <entry>Advanced desktop settings.</entry> + + </row><row> + <entry>Accessibility</entry> + <entry>Accessibility settings</entry> + + </row><row> + <entry>Network</entry> + <entry>Network application such as a webbrowser</entry> + + </row><row> + <entry>Clock</entry> + <entry>A clock application/applet</entry> + + </row><row> + <entry>Monitor</entry> + <entry>Monitor application/applet that monitors some resource or activity.</entry> + + </row><row> + <entry>AudioVideo</entry> + <entry>A multimedia (audio/video) application</entry> + + </row><row> + <entry>Amusement</entry> + <entry>A simple amusement</entry> + + </row><row> + <entry>Emulator</entry> + <entry>Emulator of another platform, such as a DOS emulator.</entry> + + </row><row> + <entry>Game</entry> + <entry>A game</entry> + + </row><row> + <entry>3DGame</entry> + <entry>A game in 3D</entry> + + </row><row> + <entry>ArcadeGame</entry> + <entry>Arcade style game</entry> + + </row><row> + <entry>BoardGame</entry> + <entry>A board game</entry> + + </row><row> + <entry>CardGame</entry> + <entry>A card game</entry> + + </row><row> + <entry>FirstPersonGame</entry> + <entry>First person perspective game</entry> + + </row><row> + <entry>PlatformGame</entry> + <entry>Platform style game</entry> + + </row><row> + <entry>PuzzleGame</entry> + <entry>Puzzle game</entry> + + </row><row> + <entry>SportsGame</entry> + <entry>Sports game</entry> + + </row><row> + <entry>StrategyGame</entry> + <entry>Strategy game</entry> + + </row><row> + <entry>BlocksGame</entry> + <entry>Falling blocks game</entry> + + </row><row> + <entry>Education</entry> + <entry>Educational software</entry> + + </row><row> + <entry>Math</entry> + <entry>Math software</entry> + + </row><row> + <entry>Astronomy</entry> + <entry>Astronomy software</entry> + + </row><row> + <entry>Physics</entry> + <entry>Physics software</entry> + + </row><row> + <entry>Chemistry</entry> + <entry>Chemistry software</entry> + + </row><row> + <entry>Science</entry> + <entry>Scientific software</entry> + + </row><row> + <entry>HamRadio</entry> + <entry>HAM radio software</entry> + + </row><row> + <entry>KDE</entry> + <entry>Application based on KDE libraries.</entry> + + </row><row> + <entry>GNOME</entry> + <entry>Application based on GNOME libraries.</entry> + + </row><row> + <entry>GTK</entry> + <entry>Application based on GTK+ libraries (may also have GNOME category).</entry> + + </row><row> + <entry>Qt</entry> + <entry>Application based on Qt libraries (may also have KDE category).</entry> + + </row><row> + <entry>Motif</entry> + <entry>Application based on Motif libraries.</entry> + + </row><row> + <entry>ConsoleOnly</entry> + <entry>Application that only works inside a terminal (text-based or command line application).</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>ROX</entry><entry>ROX Desktop</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>How to add your application to the menus</title> + <para> + The short answer for third party applications is: + <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>/desktop/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> + <para> + Also, at least for a good long while, installing a directory hierarchy to + the old GNOME/KDE specific locations such as /usr/share/applnk and + /usr/share/gnome/apps should work. 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> + </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 <MergeFile> with the + systemwide file, so that system changes are inherited. When the user + deletes a menu item, you add + <literal><Exclude><Filename>foo.desktop</Filename></Exclude></literal>. If + the user adds a menu item, you use + <literal><Include><Filename>foo.desktop</Filename></Include></literal>. + </para> + <para> + If the user moves a folder, you might try to use <Move> elements + to represent that, but it's tricky. (Move A/B/C to D/E/F, then move D/E + to D/G, note that D/E/F still contains A/B/C while only the original D/E + was moved to D/G.) In order to move a folder, you have to "fix up" + all moves that move things <emphasis>into</emphasis> the folder being + moved to instead move things into the folder's new location. + </para> + <para> + To delete a folder, simply append the <Deleted> element. + </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>. 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-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>. It describes gives 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 <Name> of each parent + of the menu, followed by the <Name> 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 desktop or directory entry, relative to the + <AppDir> or <DirectoryDir> containing the + entry. For example, if <filename>/usr/share/applications</filename> is + specified as an <AppDir>, the relative path to + <filename>/usr/share/applications/foo/bar.desktop</filename> is + <filename>foo/bar.desktop</filename>. + </para> + </glossdef> + </glossentry> + </glossary> +</article> + |