summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeinrich Wendel <h_wendel@cojobo.net>2008-03-02 13:58:09 +0000
committerHeinrich Wendel <h_wendel@cojobo.net>2008-03-02 13:58:09 +0000
commit0b063fa389fe9753ceec0c22b3ece88539ffccb8 (patch)
treee9b09cbfdd52f840e06ebe43276348a591e0bb07
parent322eaceb197ee545e577aadbecc24c390526b776 (diff)
replace tabs with spaces
-rw-r--r--ChangeLog166
-rw-r--r--setup.py14
-rwxr-xr-xtest/test-desktop.py38
-rwxr-xr-xtest/test-menu.py26
-rw-r--r--xdg/BaseDirectory.py102
-rw-r--r--xdg/Config.py34
-rw-r--r--xdg/DesktopEntry.py714
-rw-r--r--xdg/Exceptions.py60
-rw-r--r--xdg/IconTheme.py682
-rw-r--r--xdg/IniFile.py742
-rw-r--r--xdg/Locale.py114
-rw-r--r--xdg/Menu.py1894
-rw-r--r--xdg/MenuEditor.py902
-rw-r--r--xdg/Mime.py776
-rw-r--r--xdg/RecentFiles.py292
15 files changed, 3278 insertions, 3278 deletions
diff --git a/ChangeLog b/ChangeLog
index 7e9b700..bad2ed8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,183 +2,183 @@ Version 0.16
2008-03-02 Heinrich Wendel <h_wendel@cojobo.net>
* IniFile.py: Fix saving of relative filenames
- * IniFile.py: Fix __cmp__ method
+ * IniFile.py: Fix __cmp__ method
Version 0.15
2005-08-10 Heinrich Wendel <h_wendel@cojoob.net>
- * Menu.py: Add support for TryExec
+ * Menu.py: Add support for TryExec
2005-08-09 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py: Unicode bug fixed!
- * IconTheme.py: small speedup
+ * Menu.py: Unicode bug fixed!
+ * IconTheme.py: small speedup
2005-08-04 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py, IconTheme.py: Bugfixes...
- * MenuEditor.py: Make xml nice; add hide/unhide functions
+ * Menu.py, IconTheme.py: Bugfixes...
+ * MenuEditor.py: Make xml nice; add hide/unhide functions
Versinon 0.14
2005-06-02 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py, MenuEditor.py: Bugfixes...
+ * Menu.py, MenuEditor.py: Bugfixes...
version 0.13
2005-06-01 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py, MenuEditor.py: Bugfixes...
- * Config.py: Add root_mode
+ * Menu.py, MenuEditor.py: Bugfixes...
+ * Config.py: Add root_mode
Version 0.12
2005-05-30 Heinrich Wendel <h_wendel@cojobo.net>
- * MenuEditor.py: New in this release, use to edit Menus thx to Travis
- Watkins <alleykat@gmail.com> and Matt Kynaston <mattkyn@gmail.com> for
- their help
- * Menu.py, IniFile.py, DesktopEntry.py: Lot of bugfixing...
- * BaseDirectory.py: Add xdg_cache_home
- * IconTheme.py, Config.py: More caching stuff, make cachetime
- configurable
+ * MenuEditor.py: New in this release, use to edit Menus thx to Travis
+ Watkins <alleykat@gmail.com> and Matt Kynaston <mattkyn@gmail.com> for
+ their help
+ * Menu.py, IniFile.py, DesktopEntry.py: Lot of bugfixing...
+ * BaseDirectory.py: Add xdg_cache_home
+ * IconTheme.py, Config.py: More caching stuff, make cachetime
+ configurable
Version 0.11
2005-05-23 Heinrich Wendel <h_wendel@cojobo.net>
- * DesktopEntry.p, Menu.py: A lot of bugfixes, thx to Travis Watkins
- <alleykat@gmail.com>
+ * DesktopEntry.p, Menu.py: A lot of bugfixes, thx to Travis Watkins
+ <alleykat@gmail.com>
2005-05-02 Heinrich Wendel <h_wendel@cojobo.net>
- * Config.py:
- Module to configure Basic Settings, currently available:
- - Locale, IconTheme, IconSize, WindowManager
- * Locale.py:
- Internal Module to support Locales
- * Mime.py:
- Implementation of the Mime Specification
- * Menu.py:
- Now supports LegacyDirs
- * RecentFiles.py:
- Implementation of the Recent Files Specification
+ * Config.py:
+ Module to configure Basic Settings, currently available:
+ - Locale, IconTheme, IconSize, WindowManager
+ * Locale.py:
+ Internal Module to support Locales
+ * Mime.py:
+ Implementation of the Mime Specification
+ * Menu.py:
+ Now supports LegacyDirs
+ * RecentFiles.py:
+ Implementation of the Recent Files Specification
Version 0.10
2005-04-26 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py:
- various bug fixing to support version 1.0.draft-1
+ * Menu.py:
+ various bug fixing to support version 1.0.draft-1
2005-04-13 Heinrich Wendel <h_wendel@cojobo.net>
- * IniFily.py:
- Detect if a .desktop file was edited
- * Menu.py
- Fix bug caused by excluding NoDisplay/Hidden Items to early
+ * IniFily.py:
+ Detect if a .desktop file was edited
+ * Menu.py
+ Fix bug caused by excluding NoDisplay/Hidden Items to early
Version 0.9
2005-03-23 Heinrich Wendel <h_wendel@cojobo.net>
- * IniFile.py:
- various speedups
- * Menu.py:
- add support for <MergeFile type="parent">, menu-spec-0.91
+ * IniFile.py:
+ various speedups
+ * Menu.py:
+ add support for <MergeFile type="parent">, menu-spec-0.91
2005-03-21 Heinrich Wendel <h_wendel@cojobo.net>
- * IniFily.py:
- Small fixes
- * Menu.py:
- remove __preparse and don't edit the parsed document, so menu editing is
- possible
- store parsed document in Menu.Doc
- store document name in Menu.Filename
+ * IniFily.py:
+ Small fixes
+ * Menu.py:
+ remove __preparse and don't edit the parsed document, so menu editing is
+ possible
+ store parsed document in Menu.Doc
+ store document name in Menu.Filename
2005-03-18 Heinrich Wendel <h_wendel@cojobo.net>
- * Menu.py:
- fix basename argument, thx to Matt Kynaston <mattkyn@gmail.com>;
- make it comply to menu-spec-0.9
+ * Menu.py:
+ fix basename argument, thx to Matt Kynaston <mattkyn@gmail.com>;
+ make it comply to menu-spec-0.9
2004-30-11 Heinrich Wendel <h_wendel@cojobo.net>
- * Update BaseDirectory.py to the current ROX version
+ * Update BaseDirectory.py to the current ROX version
Version 0.8
2004-10-18 Ross Burton <ross@burtonini.com>
- * xdg/DesktopEntry.py, xdg/IconTheme.py:
- Add . to the literal FileExtensions so that the checks work.
- * xdg/Menu.py:
- Don't read .desktop-* files, only .desktop
+ * xdg/DesktopEntry.py, xdg/IconTheme.py:
+ Add . to the literal FileExtensions so that the checks work.
+ * xdg/Menu.py:
+ Don't read .desktop-* files, only .desktop
2004-10-18 Martin Grimme <martin@pycage.de>
- * xdg/IconTheme.py (getIconPath): The "hicolor" theme has to be used as
- the fallback.
- * xdg/IniFile.py (IniFile.getList): Fixed bug in splitting up strings.
+ * xdg/IconTheme.py (getIconPath): The "hicolor" theme has to be used as
+ the fallback.
+ * xdg/IniFile.py (IniFile.getList): Fixed bug in splitting up strings.
Version 0.7
2004-09-04 Heinrich Wendel <h_wendel@cojobo.net>
- * Add 'import codecs' to IniFile, needed by write support
- * Fix parsing of lists with only one entry
+ * Add 'import codecs' to IniFile, needed by write support
+ * Fix parsing of lists with only one entry
Version 0.6
2004-08-04 Heinrich Wendel <h_wendel@cojobo.net>
- * Performance Improvements
+ * Performance Improvements
Version 0.5
2004-03-29 Heinrich Wendel <h_wendel@cojobo.net>
- * Finished Support for menu-spec 0.7
+ * Finished Support for menu-spec 0.7
2004-03-27 Heinrich Wendel <h_wendel@cojobo.net>
* 5* speed improvement in Menu.py parsing code
2004-03-20 Heinrich Wendel <h_wendel@cojobo.net>
- * check values of Categories/OnlyShowIn keys
- * various misc changes
+ * check values of Categories/OnlyShowIn keys
+ * various misc changes
2004-03-17 Martin Grimme <martin@pycage.de>
- * xdg/Menu.py (__preparse):
- * xdg/IconTheme.py (IconTheme.parse): Made compatible with Python 2.3
- (None is a keyword).
- (__parseTheme): Prepend new icon themes to make sure that they have
- priority when looking up icons.
- (icondirs): Add "~/.icons" to the paths where to look for icons. Users
- may have icon themes installed in their home directory.
+ * xdg/Menu.py (__preparse):
+ * xdg/IconTheme.py (IconTheme.parse): Made compatible with Python 2.3
+ (None is a keyword).
+ (__parseTheme): Prepend new icon themes to make sure that they have
+ priority when looking up icons.
+ (icondirs): Add "~/.icons" to the paths where to look for icons. Users
+ may have icon themes installed in their home directory.
2003-10-08 Heinrich Wendel <h_wendel@cojobo.net>
- * Completed write-support in IniFile
+ * Completed write-support in IniFile
2003-10-05 Heinrich Wendel <h_wendel@cojobo.net>
- * Added support for Hidden and NoDisplay in menu-spec
- * inital write-support in IniFile
+ * Added support for Hidden and NoDisplay in menu-spec
+ * inital write-support in IniFile
2003-10-04 Heinrich Wendel <h_wendel@cojobo.net>
- * License change to LGPL-2
- * initial support for menu-spec 0.7
+ * License change to LGPL-2
+ * initial support for menu-spec 0.7
Version 0.4
2003-09-30 Heinrich Wendel <h_wendel@cojobo.net>
- * Bugfix release
+ * Bugfix release
Version 0.3
2003-09-12 Heinrich Wendel <h_wendel@cojobo.net>
- * Complete IconSpec implementation, including cache and validation
+ * Complete IconSpec implementation, including cache and validation
2003-09-07 Heinrich Wendel <h_wendel@cojobo.net>
- * Basedir spec converted to version 0.6
- * First part of separating DesktopEntry backend in IniFile
- * added getPath(...) function to Menu.py
+ * Basedir spec converted to version 0.6
+ * First part of separating DesktopEntry backend in IniFile
+ * added getPath(...) function to Menu.py
Version 0.2
2003-09-05 Heinrich Wendel <h_wendel@cojobo.net>
- * Rewrite of menu-spec code
- * Taken basedir-spec code from ROX
+ * Rewrite of menu-spec code
+ * Taken basedir-spec code from ROX
Version 0.1
2003-08-08 Heinrich Wendel <h_wendel@cojobo.net>
- * initial public release
+ * initial public release
diff --git a/setup.py b/setup.py
index f7f6aac..95e97fb 100644
--- a/setup.py
+++ b/setup.py
@@ -3,10 +3,10 @@
from distutils.core import setup
setup( name = "pyxdg",
- version = "0.15",
- description = "PyXDG contains implementations of freedesktop.org standards in python.",
- maintainer = "Heinrich Wendel",
- maintainer_email = "h_wendel@cojobo.net",
- url = "http://cvs.freedesktop.org/cgi-bin/viewcvs.cgi/pyxdg/",
- packages = ['xdg'],
- license = "LGPL-2")
+ version = "0.16",
+ description = "PyXDG contains implementations of freedesktop.org standards in python.",
+ maintainer = "Heinrich Wendel",
+ maintainer_email = "h_wendel@cojobo.net",
+ url = "http://cvs.freedesktop.org/cgi-bin/viewcvs.cgi/pyxdg/",
+ packages = ['xdg'],
+ license = "LGPL-2")
diff --git a/test/test-desktop.py b/test/test-desktop.py
index 9409cf9..8304b62 100755
--- a/test/test-desktop.py
+++ b/test/test-desktop.py
@@ -4,28 +4,28 @@ from xdg.DesktopEntry import *
import os, sys
def checkfiles(path):
- if os.path.isdir(path):
- ls = os.listdir(path)
- for file in ls:
- checkfiles(os.path.join(path, file))
- else:
- entry = DesktopEntry()
- try:
- entry.parse(path)
- except ParsingError, e:
- print e
- return
+ if os.path.isdir(path):
+ ls = os.listdir(path)
+ for file in ls:
+ checkfiles(os.path.join(path, file))
+ else:
+ entry = DesktopEntry()
+ try:
+ entry.parse(path)
+ except ParsingError, e:
+ print e
+ return
- entry.setLocale("C")
- entry.getName()
+ entry.setLocale("C")
+ entry.getName()
- try:
- entry.validate()
- except ValidationError, e:
- print e
+ try:
+ entry.validate()
+ except ValidationError, e:
+ print e
try:
- checkfiles(sys.argv[1])
+ checkfiles(sys.argv[1])
except IndexError:
- print("No file or directory given!")
+ print("No file or directory given!")
diff --git a/test/test-menu.py b/test/test-menu.py
index 892dc90..2b1e7f3 100755
--- a/test/test-menu.py
+++ b/test/test-menu.py
@@ -6,19 +6,19 @@ import xdg.Menu
import xdg.DesktopEntry
def show_menu(menu, depth = 0):
-# print depth*"-" + "\x1b[01m" + menu.getName().encode("ascii", 'ignore') + "\x1b[0m"
-# depth += 1
- for entry in menu.getEntries():
- if isinstance(entry, xdg.Menu.Menu):
- show_menu(entry, depth)
- elif isinstance(entry, xdg.Menu.MenuEntry):
-# print depth*"-" + entry.DesktopEntry.getName().encode("ascii", 'ignore')
- print menu.getPath() + "/\t" + entry.DesktopFileID + "\t" + entry.DesktopEntry.getFileName()
-# elif isinstance(entry, xdg.Menu.Separator):
-# print depth*"-" + "|||"
-# elif isinstance(entry, xdg.Menu.Header):
-# print depth*"-" + "\x1b[01m" + entry.Name + "\x1b[0m"
-# depth -= 1
+# print depth*"-" + "\x1b[01m" + menu.getName().encode("ascii", 'ignore') + "\x1b[0m"
+# depth += 1
+ for entry in menu.getEntries():
+ if isinstance(entry, xdg.Menu.Menu):
+ show_menu(entry, depth)
+ elif isinstance(entry, xdg.Menu.MenuEntry):
+# print depth*"-" + entry.DesktopEntry.getName().encode("ascii", 'ignore')
+ print menu.getPath() + "/\t" + entry.DesktopFileID + "\t" + entry.DesktopEntry.getFileName()
+# elif isinstance(entry, xdg.Menu.Separator):
+# print depth*"-" + "|||"
+# elif isinstance(entry, xdg.Menu.Header):
+# print depth*"-" + "\x1b[01m" + entry.Name + "\x1b[0m"
+# depth -= 1
menu = xdg.Menu.parse()
show_menu(menu)
diff --git a/xdg/BaseDirectory.py b/xdg/BaseDirectory.py
index 9c1d53c..6f532c9 100644
--- a/xdg/BaseDirectory.py
+++ b/xdg/BaseDirectory.py
@@ -6,7 +6,7 @@ http://cvs.sourceforge.net/viewcvs.py/rox/ROX-Lib2/python/rox/basedir.py?rev=1.9
The freedesktop.org Base Directory specification provides a way for
applications to locate shared data and configuration:
- http://standards.freedesktop.org/basedir-spec/
+ http://standards.freedesktop.org/basedir-spec/
(based on version 0.6)
@@ -14,13 +14,13 @@ This module can be used to load and save from and to these directories.
Typical usage:
- from rox import basedir
-
- for dir in basedir.load_config_paths('mydomain.org', 'MyProg', 'Options'):
- print "Load settings from", dir
+ from rox import basedir
+
+ for dir in basedir.load_config_paths('mydomain.org', 'MyProg', 'Options'):
+ print "Load settings from", dir
- dir = basedir.save_config_path('mydomain.org', 'MyProg')
- print >>file(os.path.join(dir, 'Options'), 'w'), "foo=2"
+ dir = basedir.save_config_path('mydomain.org', 'MyProg')
+ print >>file(os.path.join(dir, 'Options'), 'w'), "foo=2"
Note: see the rox.Options module for a higher-level API for managing options.
"""
@@ -30,68 +30,68 @@ import os
_home = os.environ.get('HOME', '/')
xdg_data_home = os.environ.get('XDG_DATA_HOME',
- os.path.join(_home, '.local', 'share'))
+ os.path.join(_home, '.local', 'share'))
xdg_data_dirs = [xdg_data_home] + \
- os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
+ os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':')
xdg_config_home = os.environ.get('XDG_CONFIG_HOME',
- os.path.join(_home, '.config'))
+ os.path.join(_home, '.config'))
xdg_config_dirs = [xdg_config_home] + \
- os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':')
+ os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':')
xdg_cache_home = os.environ.get('XDG_CACHE_HOME',
- os.path.join(_home, '.cache'))
+ os.path.join(_home, '.cache'))
xdg_data_dirs = filter(lambda x: x, xdg_data_dirs)
xdg_config_dirs = filter(lambda x: x, xdg_config_dirs)
def save_config_path(*resource):
- """Ensure $XDG_CONFIG_HOME/<resource>/ exists, and return its path.
- 'resource' should normally be the name of your application. Use this
- when SAVING configuration settings. Use the xdg_config_dirs variable
- for loading."""
- resource = os.path.join(*resource)
- assert not resource.startswith('/')
- path = os.path.join(xdg_config_home, resource)
- if not os.path.isdir(path):
- os.makedirs(path, 0700)
- return path
+ """Ensure $XDG_CONFIG_HOME/<resource>/ exists, and return its path.
+ 'resource' should normally be the name of your application. Use this
+ when SAVING configuration settings. Use the xdg_config_dirs variable
+ for loading."""
+ resource = os.path.join(*resource)
+ assert not resource.startswith('/')
+ path = os.path.join(xdg_config_home, resource)
+ if not os.path.isdir(path):
+ os.makedirs(path, 0700)
+ return path
def save_data_path(*resource):
- """Ensure $XDG_DATA_HOME/<resource>/ exists, and return its path.
- 'resource' is the name of some shared resource. Use this when updating
- a shared (between programs) database. Use the xdg_data_dirs variable
- for loading."""
- resource = os.path.join(*resource)
- assert not resource.startswith('/')
- path = os.path.join(xdg_data_home, resource)
- if not os.path.isdir(path):
- os.makedirs(path)
- return path
+ """Ensure $XDG_DATA_HOME/<resource>/ exists, and return its path.
+ 'resource' is the name of some shared resource. Use this when updating
+ a shared (between programs) database. Use the xdg_data_dirs variable
+ for loading."""
+ resource = os.path.join(*resource)
+ assert not resource.startswith('/')
+ path = os.path.join(xdg_data_home, resource)
+ if not os.path.isdir(path):
+ os.makedirs(path)
+ return path
def load_config_paths(*resource):
- """Returns an iterator which gives each directory named 'resource' in the
- configuration search path. Information provided by earlier directories should
- take precedence over later ones (ie, the user's config dir comes first)."""
- resource = os.path.join(*resource)
- for config_dir in xdg_config_dirs:
- path = os.path.join(config_dir, resource)
- if os.path.exists(path): yield path
+ """Returns an iterator which gives each directory named 'resource' in the
+ configuration search path. Information provided by earlier directories should
+ take precedence over later ones (ie, the user's config dir comes first)."""
+ resource = os.path.join(*resource)
+ for config_dir in xdg_config_dirs:
+ path = os.path.join(config_dir, resource)
+ if os.path.exists(path): yield path
def load_first_config(*resource):
- """Returns the first result from load_config_paths, or None if there is nothing
- to load."""
- for x in load_config_paths(*resource):
- return x
- return None
+ """Returns the first result from load_config_paths, or None if there is nothing
+ to load."""
+ for x in load_config_paths(*resource):
+ return x
+ return None
def load_data_paths(*resource):
- """Returns an iterator which gives each directory named 'resource' in the
- shared data search path. Information provided by earlier directories should
- take precedence over later ones."""
- resource = os.path.join(*resource)
- for data_dir in xdg_data_dirs:
- path = os.path.join(data_dir, resource)
- if os.path.exists(path): yield path
+ """Returns an iterator which gives each directory named 'resource' in the
+ shared data search path. Information provided by earlier directories should
+ take precedence over later ones."""
+ resource = os.path.join(*resource)
+ for data_dir in xdg_data_dirs:
+ path = os.path.join(data_dir, resource)
+ if os.path.exists(path): yield path
diff --git a/xdg/Config.py b/xdg/Config.py
index c47389f..e2fbe64 100644
--- a/xdg/Config.py
+++ b/xdg/Config.py
@@ -10,30 +10,30 @@ cache_time = 5
root_mode = False
def setWindowManager(wm):
- global windowmanager
- windowmanager = wm
+ global windowmanager
+ windowmanager = wm
def setIconTheme(theme):
- global icon_theme
- icon_theme = theme
- import xdg.IconTheme
- xdg.IconTheme.themes = []
+ global icon_theme
+ icon_theme = theme
+ import xdg.IconTheme
+ xdg.IconTheme.themes = []
def setIconSize(size):
- global icon_size
- icon_size = size
+ global icon_size
+ icon_size = size
def setCacheTime(time):
- global cache_time
- cache_time = time
+ global cache_time
+ cache_time = time
def setLocale(lang):
- import locale
- lang = locale.normalize(lang)
- locale.setlocale(locale.LC_ALL, lang)
- import xdg.Locale
- xdg.Locale.update(lang)
+ import locale
+ lang = locale.normalize(lang)
+ locale.setlocale(locale.LC_ALL, lang)
+ import xdg.Locale
+ xdg.Locale.update(lang)
def setRootMode(boolean):
- global root_mode
- root_mode = boolean
+ global root_mode
+ root_mode = boolean
diff --git a/xdg/DesktopEntry.py b/xdg/DesktopEntry.py
index 44f0426..979971c 100644
--- a/xdg/DesktopEntry.py
+++ b/xdg/DesktopEntry.py
@@ -15,366 +15,366 @@ from xdg.BaseDirectory import *
import os.path
class DesktopEntry(IniFile):
- "Class to parse and validate DesktopEntries"
-
- defaultGroup = 'Desktop Entry'
-
- def __init__(self, filename=None):
- self.content = dict()
- if filename and os.path.exists(filename):
- self.parse(filename)
- elif filename:
- self.new(filename)
-
- def __str__(self):
- return self.getName()
-
- def parse(self, file):
- IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
-
- # start standard keys
- def getType(self):
- return self.get('Type')
- def getVersion(self):
- return self.get('Version', type="numeric")
- def getEncoding(self):
- return self.get('Encoding')
- def getName(self):
- return self.get('Name', locale=True)
- def getGenericName(self):
- return self.get('GenericName', locale=True)
- def getComment(self):
- return self.get('Comment', locale=True)
- def getNoDisplay(self):
- return self.get('NoDisplay', type="boolean")
- def getIcon(self):
- return self.get('Icon', locale=True)
- def getHidden(self):
- return self.get('Hidden', type="boolean")
- def getFilePattern(self):
- return self.get('FilePattern', type="regex")
- def getTryExec(self):
- return self.get('TryExec')
- def getExec(self):
- return self.get('Exec')
- def getPath(self):
- return self.get('Path')
- def getTerminal(self):
- return self.get('Terminal', type="boolean")
- def getSwallowTitle(self):
- return self.get('SwallowTitle', locale=True)
- def getSwallowExec(self):
- return self.get('SwallowExec')
- def getActions(self):
- return self.get('Actions', list=True)
- def getMimeType(self):
- return self.get('MimeType', list=True, type="regex")
- def getSortOrder(self):
- return self.get('SortOrder', list=True)
- def getDev(self):
- return self.get('Dev')
- def getFSType(self):
- return self.get('FSType')
- def getMountPoint(self):
- return self.get('MountPoint')
- def getReadonly(self):
- return self.get('ReadOnly', type="boolean")
- def getUnmountIcon(self):
- return self.get('UnmountIcon', locale=True)
- def getURL(self):
- return self.get('URL')
- def getCategories(self):
- return self.get('Categories', list=True)
- def getOnlyShowIn(self):
- return self.get('OnlyShowIn', list=True)
- def getNotShowIn(self):
- return self.get('NotShowIn', list=True)
- def getStartupNotify(self):
- return self.get('StartupNotify', type="boolean")
- def getStartupWMClass(self):
- return self.get('StartupWMClass')
- # end standard keys
-
- # start kde keys
- def getServiceTypes(self):
- return self.get('ServiceTypes', list=True)
- def getDocPath(self):
- return self.get('DocPath')
- def getKeywords(self):
- return self.get('Keywords', list=True, locale=True)
- def getInitialPreference(self):
- return self.get('InitialPreference')
- # end kde keys
-
- # start deprecated keys
- def getMiniIcon(self):
- return self.get('MiniIcon', locale=True)
- def getTerminalOptions(self):
- return self.get('TerminalOptions')
- def getDefaultApp(self):
- return self.get('DefaultApp')
- def getProtocols(self):
- return self.get('Protocols', list=True)
- def getExtensions(self):
- return self.get('Extensions', list=True)
- def getBinaryPattern(self):
- return self.get('BinaryPattern')
- def getMapNotify(self):
- return self.get('MapNotify')
- # end deprecated keys
-
- # desktop entry edit stuff
- def new(self, filename):
- if os.path.splitext(filename)[1] == ".desktop":
- type = "Application"
- elif os.path.splitext(filename)[1] == ".directory":
- type = "Directory"
- self.content = dict()
- self.addGroup(self.defaultGroup)
- self.set("Encoding", "UTF-8")
- self.set("Type", type)
- self.filename = filename
- # end desktop entry edit stuff
-
- # validation stuff
- def checkExtras(self):
- # header
- if self.defaultGroup == "KDE Desktop Entry":
- self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
-
- # file extension
- if self.fileExtension == ".kdelnk":
- self.warnings.append("File extension .kdelnk is deprecated")
- elif self.fileExtension != ".desktop" and self.fileExtension != ".directory":
- self.warnings.append('Unknown File extension')
-
- # Type
- try:
- self.type = self.content[self.defaultGroup]["Type"]
- except KeyError:
- self.errors.append("Key 'Type' is missing")
-
- # Encoding
- try:
- self.encoding = self.content[self.defaultGroup]["Encoding"]
- except KeyError:
- self.errors.append("Key 'Encoding' is missing")
-
- # Version
- try:
- self.version = self.content[self.defaultGroup]["Version"]
- except KeyError:
- self.warnings.append("Key 'Version' is missing")
-
- # Name
- try:
- self.name = self.content[self.defaultGroup]["Name"]
- except KeyError:
- self.errors.append("Key 'Name' is missing")
-
- def checkGroup(self, group):
- # check if group header is valid
- if not (group == self.defaultGroup \
- or re.match("^\Desktop Action [a-zA-Z]+\$", group) \
- or (re.match("^\X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group)):
- self.errors.append("Invalid Group name: %s" % group)
- else:
- #OnlyShowIn and NotShowIn
- if self.content[group].has_key("OnlyShowIn") and self.content[group].has_key("NotShowIn"):
- self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
-
- def checkKey(self, key, value, group):
- # standard keys
- if key == "Type":
- if value == "ServiceType" or value == "Service":
- self.warnings.append("Type=%s is a KDE extension" % key)
- elif value == "MimeType":
- self.warnings.append("Type=MimeType is deprecated")
- elif not (value == "Application" or value == "Link" or value == "FSDevice" or value == "Directory"):
- self.errors.append("Value of key 'Type' must be Application, Link, FSDevice or Directory, but is '%s'" % value)
-
- if self.fileExtension == ".directory" and not value == "Directory":
- self.warnings.append("File extension is .directory, but Type is '%s'" % value)
- elif self.fileExtension == ".desktop" and value == "Directory":
- self.warnings.append("Files with Type=Directory should have the extension .directory")
-
- elif key == "Version":
- self.checkValue(key, value, type="number")
-
- elif key == "Encoding":
- if value == "Legacy-Mixed":
- self.errors.append("Encoding=Legacy-Mixed is deprecated and not supported by this parser")
- elif not value == "UTF-8":
- self.errors.append("Value of key 'Encoding' must be UTF-8")
-
- elif re.match("^Name"+xdg.Locale.regex+"$", key):
- pass # locale string
-
- elif re.match("^GenericName"+xdg.Locale.regex+"$", key):
- pass # locale string
-
- elif re.match("^Comment"+xdg.Locale.regex+"$", key):
- pass # locale string
-
- elif key == "NoDisplay":
- self.checkValue(key, value, type="boolean")
-
- elif key == "Hidden":
- self.checkValue(key, value, type="boolean")
-
- elif key == "Terminal":
- self.checkValue(key, value, type="boolean")
- self.checkType(key, "Application")
-
- elif key == "TryExec":
- self.checkValue(key, value)
- self.checkType(key, "Application")
-
- elif key == "Exec":
- self.checkValue(key, value)
- self.checkType(key, "Application")
-
- elif key == "Path":
- self.checkValue(key, value)
- self.checkType(key, "Application")
-
- elif re.match("^Icon"+xdg.Locale.regex+"$", key):
- self.checkValue(key, value)
-
- elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key):
- self.checkType(key, "Application")
-
- elif key == "SwallowExec":
- self.checkValue(key, value)
- self.checkType(key, "Application")
-
- elif key == "FilePatterns":
- self.checkValue(key, value, type="regex", list=True)
- self.checkType(key, "Application")
-
- elif key == "Actions":
- self.checkValue(key, value, list=True)
- self.checkType(key, "Application")
-
- elif key == "MimeType":
- self.checkValue(key, value, type="regex", list=True)
- self.checkType(key, "Application")
-
- elif key == "Categories":
- self.checkValue(key, value)
- self.checkType(key, "Application")
- self.checkCategorie(value)
-
- elif key == "OnlyShowIn":
- self.checkValue(key, value, list=True)
- self.checkOnlyShowIn(value)
-
- elif key == "NotShowIn":
- self.checkValue(key, value, list=True)
- self.checkOnlyShowIn(value)
-
- elif key == "StartupNotify":
- self.checkValue(key, value, type="boolean")
- self.checkType(key, "Application")
-
- elif key == "StartupWMClass":
- self.checkType(key, "Application")
-
- elif key == "SortOrder":
- self.checkValue(key, value, list=True)
- self.checkType(key, "Directory")
-
- elif key == "URL":
- self.checkValue(key, value)
- self.checkType(key, "URL")
-
- elif key == "Dev":
- self.checkValue(key, value)
- self.checkType(key, "FSDevice")
+ "Class to parse and validate DesktopEntries"
+
+ defaultGroup = 'Desktop Entry'
+
+ def __init__(self, filename=None):
+ self.content = dict()
+ if filename and os.path.exists(filename):
+ self.parse(filename)
+ elif filename:
+ self.new(filename)
+
+ def __str__(self):
+ return self.getName()
+
+ def parse(self, file):
+ IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
+
+ # start standard keys
+ def getType(self):
+ return self.get('Type')
+ def getVersion(self):
+ return self.get('Version', type="numeric")
+ def getEncoding(self):
+ return self.get('Encoding')
+ def getName(self):
+ return self.get('Name', locale=True)
+ def getGenericName(self):
+ return self.get('GenericName', locale=True)
+ def getComment(self):
+ return self.get('Comment', locale=True)
+ def getNoDisplay(self):
+ return self.get('NoDisplay', type="boolean")
+ def getIcon(self):
+ return self.get('Icon', locale=True)
+ def getHidden(self):
+ return self.get('Hidden', type="boolean")
+ def getFilePattern(self):
+ return self.get('FilePattern', type="regex")
+ def getTryExec(self):
+ return self.get('TryExec')
+ def getExec(self):
+ return self.get('Exec')
+ def getPath(self):
+ return self.get('Path')
+ def getTerminal(self):
+ return self.get('Terminal', type="boolean")
+ def getSwallowTitle(self):
+ return self.get('SwallowTitle', locale=True)
+ def getSwallowExec(self):
+ return self.get('SwallowExec')
+ def getActions(self):
+ return self.get('Actions', list=True)
+ def getMimeType(self):
+ return self.get('MimeType', list=True, type="regex")
+ def getSortOrder(self):
+ return self.get('SortOrder', list=True)
+ def getDev(self):
+ return self.get('Dev')
+ def getFSType(self):
+ return self.get('FSType')
+ def getMountPoint(self):
+ return self.get('MountPoint')
+ def getReadonly(self):
+ return self.get('ReadOnly', type="boolean")
+ def getUnmountIcon(self):
+ return self.get('UnmountIcon', locale=True)
+ def getURL(self):
+ return self.get('URL')
+ def getCategories(self):
+ return self.get('Categories', list=True)
+ def getOnlyShowIn(self):
+ return self.get('OnlyShowIn', list=True)
+ def getNotShowIn(self):
+ return self.get('NotShowIn', list=True)
+ def getStartupNotify(self):
+ return self.get('StartupNotify', type="boolean")
+ def getStartupWMClass(self):
+ return self.get('StartupWMClass')
+ # end standard keys
+
+ # start kde keys
+ def getServiceTypes(self):
+ return self.get('ServiceTypes', list=True)
+ def getDocPath(self):
+ return self.get('DocPath')
+ def getKeywords(self):
+ return self.get('Keywords', list=True, locale=True)
+ def getInitialPreference(self):
+ return self.get('InitialPreference')
+ # end kde keys
+
+ # start deprecated keys
+ def getMiniIcon(self):
+ return self.get('MiniIcon', locale=True)
+ def getTerminalOptions(self):
+ return self.get('TerminalOptions')
+ def getDefaultApp(self):
+ return self.get('DefaultApp')
+ def getProtocols(self):
+ return self.get('Protocols', list=True)
+ def getExtensions(self):
+ return self.get('Extensions', list=True)
+ def getBinaryPattern(self):
+ return self.get('BinaryPattern')
+ def getMapNotify(self):
+ return self.get('MapNotify')
+ # end deprecated keys
+
+ # desktop entry edit stuff
+ def new(self, filename):
+ if os.path.splitext(filename)[1] == ".desktop":
+ type = "Application"
+ elif os.path.splitext(filename)[1] == ".directory":
+ type = "Directory"
+ self.content = dict()
+ self.addGroup(self.defaultGroup)
+ self.set("Encoding", "UTF-8")
+ self.set("Type", type)
+ self.filename = filename
+ # end desktop entry edit stuff
+
+ # validation stuff
+ def checkExtras(self):
+ # header
+ if self.defaultGroup == "KDE Desktop Entry":
+ self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
+
+ # file extension
+ if self.fileExtension == ".kdelnk":
+ self.warnings.append("File extension .kdelnk is deprecated")
+ elif self.fileExtension != ".desktop" and self.fileExtension != ".directory":
+ self.warnings.append('Unknown File extension')
+
+ # Type
+ try:
+ self.type = self.content[self.defaultGroup]["Type"]
+ except KeyError:
+ self.errors.append("Key 'Type' is missing")
+
+ # Encoding
+ try:
+ self.encoding = self.content[self.defaultGroup]["Encoding"]
+ except KeyError:
+ self.errors.append("Key 'Encoding' is missing")
+
+ # Version
+ try:
+ self.version = self.content[self.defaultGroup]["Version"]
+ except KeyError:
+ self.warnings.append("Key 'Version' is missing")
+
+ # Name
+ try:
+ self.name = self.content[self.defaultGroup]["Name"]
+ except KeyError:
+ self.errors.append("Key 'Name' is missing")
+
+ def checkGroup(self, group):
+ # check if group header is valid
+ if not (group == self.defaultGroup \
+ or re.match("^\Desktop Action [a-zA-Z]+\$", group) \
+ or (re.match("^\X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group)):
+ self.errors.append("Invalid Group name: %s" % group)
+ else:
+ #OnlyShowIn and NotShowIn
+ if self.content[group].has_key("OnlyShowIn") and self.content[group].has_key("NotShowIn"):
+ self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
+
+ def checkKey(self, key, value, group):
+ # standard keys
+ if key == "Type":
+ if value == "ServiceType" or value == "Service":
+ self.warnings.append("Type=%s is a KDE extension" % key)
+ elif value == "MimeType":
+ self.warnings.append("Type=MimeType is deprecated")
+ elif not (value == "Application" or value == "Link" or value == "FSDevice" or value == "Directory"):
+ self.errors.append("Value of key 'Type' must be Application, Link, FSDevice or Directory, but is '%s'" % value)
+
+ if self.fileExtension == ".directory" and not value == "Directory":
+ self.warnings.append("File extension is .directory, but Type is '%s'" % value)
+ elif self.fileExtension == ".desktop" and value == "Directory":
+ self.warnings.append("Files with Type=Directory should have the extension .directory")
+
+ elif key == "Version":
+ self.checkValue(key, value, type="number")
+
+ elif key == "Encoding":
+ if value == "Legacy-Mixed":
+ self.errors.append("Encoding=Legacy-Mixed is deprecated and not supported by this parser")
+ elif not value == "UTF-8":
+ self.errors.append("Value of key 'Encoding' must be UTF-8")
+
+ elif re.match("^Name"+xdg.Locale.regex+"$", key):
+ pass # locale string
+
+ elif re.match("^GenericName"+xdg.Locale.regex+"$", key):
+ pass # locale string
+
+ elif re.match("^Comment"+xdg.Locale.regex+"$", key):
+ pass # locale string
+
+ elif key == "NoDisplay":
+ self.checkValue(key, value, type="boolean")
+
+ elif key == "Hidden":
+ self.checkValue(key, value, type="boolean")
+
+ elif key == "Terminal":
+ self.checkValue(key, value, type="boolean")
+ self.checkType(key, "Application")
+
+ elif key == "TryExec":
+ self.checkValue(key, value)
+ self.checkType(key, "Application")
+
+ elif key == "Exec":
+ self.checkValue(key, value)
+ self.checkType(key, "Application")
+
+ elif key == "Path":
+ self.checkValue(key, value)
+ self.checkType(key, "Application")
+
+ elif re.match("^Icon"+xdg.Locale.regex+"$", key):
+ self.checkValue(key, value)
+
+ elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key):
+ self.checkType(key, "Application")
+
+ elif key == "SwallowExec":
+ self.checkValue(key, value)
+ self.checkType(key, "Application")
+
+ elif key == "FilePatterns":
+ self.checkValue(key, value, type="regex", list=True)
+ self.checkType(key, "Application")
+
+ elif key == "Actions":
+ self.checkValue(key, value, list=True)
+ self.checkType(key, "Application")
+
+ elif key == "MimeType":
+ self.checkValue(key, value, type="regex", list=True)
+ self.checkType(key, "Application")
+
+ elif key == "Categories":
+ self.checkValue(key, value)
+ self.checkType(key, "Application")
+ self.checkCategorie(value)
+
+ elif key == "OnlyShowIn":
+ self.checkValue(key, value, list=True)
+ self.checkOnlyShowIn(value)
+
+ elif key == "NotShowIn":
+ self.checkValue(key, value, list=True)
+ self.checkOnlyShowIn(value)
+
+ elif key == "StartupNotify":
+ self.checkValue(key, value, type="boolean")
+ self.checkType(key, "Application")
+
+ elif key == "StartupWMClass":
+ self.checkType(key, "Application")
+
+ elif key == "SortOrder":
+ self.checkValue(key, value, list=True)
+ self.checkType(key, "Directory")
+
+ elif key == "URL":
+ self.checkValue(key, value)
+ self.checkType(key, "URL")
+
+ elif key == "Dev":
+ self.checkValue(key, value)
+ self.checkType(key, "FSDevice")
- elif key == "FSType":
- self.checkValue(key, value)
- self.checkType(key, "FSDevice")
+ elif key == "FSType":
+ self.checkValue(key, value)
+ self.checkType(key, "FSDevice")
- elif key == "MountPoint":
- self.checkValue(key, value)
- self.checkType(key, "FSDevice")
+ elif key == "MountPoint":
+ self.checkValue(key, value)
+ self.checkType(key, "FSDevice")
- elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key):
- self.checkValue(key, value)
- self.checkType(key, "FSDevice")
+ elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key):
+ self.checkValue(key, value)
+ self.checkType(key, "FSDevice")
- elif key == "ReadOnly":
- self.checkValue(key, value, type="boolean")
- self.checkType(key, "FSDevice")
+ elif key == "ReadOnly":
+ self.checkValue(key, value, type="boolean")
+ self.checkType(key, "FSDevice")
- # kde extensions
- elif key == "ServiceTypes":
- self.checkValue(key, value, list=True)
- self.warnings.append("Key '%s' is a KDE extension" % key)
+ # kde extensions
+ elif key == "ServiceTypes":
+ self.checkValue(key, value, list=True)
+ self.warnings.append("Key '%s' is a KDE extension" % key)
- elif key == "DocPath":
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is a KDE extension" % key)
-
- elif re.match("^Keywords"+xdg.Locale.regex+"$", key):
- self.checkValue(key, value, list=True)
- self.warnings.append("Key '%s' is a KDE extension" % key)
-
- elif key == "InitialPreference":
- self.checkValue(key, value, type="number")
- self.warnings.append("Key '%s' is a KDE extension" % key)
-
- # deprecated keys
- elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key):
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "TerminalOptions":
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "DefaultApp":
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "Protocols":
- self.checkValue(key, value, list=True)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "Extensions":
- self.checkValue(key, value, list=True)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "BinaryPattern":
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- elif key == "MapNotify":
- self.checkValue(key, value)
- self.warnings.append("Key '%s' is deprecated" % key)
-
- # "X-" extensions
- elif re.match("^X-[a-zA-Z0-9-]+", key):
- pass
-
- else:
- self.errors.append("Invalid key: %s" % key)
-
- def checkType(self, key, type):
- if not self.type == type:
- self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
-
- def checkOnlyShowIn(self, value):
- values = self.getList(value)
- valid = ["GNOME", "KDE", "ROX", "XFCE", "Old"]
- for item in values:
- if item not in valid:
- self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
-
- def checkCategorie(self, value):
- values = self.getList(value)
- valid = ["Legacy","Core","Development","Building","Debugger","IDE","GUIDesigner","Profiling","RevisionControl","Translation","Office","Calendar","ContactManagement","Database","Dictionary","Chart","Email","Finance","FlowChart","PDA","ProjectManagement","Presentation","Spreadsheet","WordProcessor","Graphics","2DGraphics","VectorGraphics","RasterGraphics","3DGraphics","Scanning","OCR","Photograph","Viewer","Settings","DesktopSettings","HardwareSettings","PackageManager","Network","Dialup","InstantMessaging","IRCClient","FileTransfer","HamRadio","News","P2P","RemoteAccess","Telephony","WebBrowser","WebDevelopment","AudioVideo","Audio","Midi","Mixer","Sequencer","Tuner","Video","TV","AudioVideoEditing","Player","Recorder","DiscBurning","Game","ActionGame","AdventureGame","ArcadeGame","BoardGame","BlocksGame","CardGame","KidsGame","LogicGame","RolePlaying","Simulation","SportsGame","StrategyGame","Education","Art","Art","Contruction","Music","Languages","Science","Astronomy","Biology","Chemistry","Geology","Math","MedicalSoftware","Physics","Teaching","Amusement","Applet","Archiving","Electronics","Emulator","Engineering","FileManager","Shell","Screensaver","TerminalEmulator","TrayIcon","System","Filesystem","Monitor","Security","Utility","Accessibility","Calculator","Clock","TextEditor","KDE","GNOME","GTK","Qt","Motif","Java","ConsoleOnly"]
- for item in values:
- if item not in valid:
- self.errors.append("'%s' is not a registered Category" % item);
+ elif key == "DocPath":
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is a KDE extension" % key)
+
+ elif re.match("^Keywords"+xdg.Locale.regex+"$", key):
+ self.checkValue(key, value, list=True)
+ self.warnings.append("Key '%s' is a KDE extension" % key)
+
+ elif key == "InitialPreference":
+ self.checkValue(key, value, type="number")
+ self.warnings.append("Key '%s' is a KDE extension" % key)
+
+ # deprecated keys
+ elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key):
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "TerminalOptions":
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "DefaultApp":
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "Protocols":
+ self.checkValue(key, value, list=True)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "Extensions":
+ self.checkValue(key, value, list=True)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "BinaryPattern":
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ elif key == "MapNotify":
+ self.checkValue(key, value)
+ self.warnings.append("Key '%s' is deprecated" % key)
+
+ # "X-" extensions
+ elif re.match("^X-[a-zA-Z0-9-]+", key):
+ pass
+
+ else:
+ self.errors.append("Invalid key: %s" % key)
+
+ def checkType(self, key, type):
+ if not self.type == type:
+ self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
+
+ def checkOnlyShowIn(self, value):
+ values = self.getList(value)
+ valid = ["GNOME", "KDE", "ROX", "XFCE", "Old"]
+ for item in values:
+ if item not in valid:
+ self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
+
+ def checkCategorie(self, value):
+ values = self.getList(value)
+ valid = ["Legacy","Core","Development","Building","Debugger","IDE","GUIDesigner","Profiling","RevisionControl","Translation","Office","Calendar","ContactManagement","Database","Dictionary","Chart","Email","Finance","FlowChart","PDA","ProjectManagement","Presentation","Spreadsheet","WordProcessor","Graphics","2DGraphics","VectorGraphics","RasterGraphics","3DGraphics","Scanning","OCR","Photograph","Viewer","Settings","DesktopSettings","HardwareSettings","PackageManager","Network","Dialup","InstantMessaging","IRCClient","FileTransfer","HamRadio","News","P2P","RemoteAccess","Telephony","WebBrowser","WebDevelopment","AudioVideo","Audio","Midi","Mixer","Sequencer","Tuner","Video","TV","AudioVideoEditing","Player","Recorder","DiscBurning","Game","ActionGame","AdventureGame","ArcadeGame","BoardGame","BlocksGame","CardGame","KidsGame","LogicGame","RolePlaying","Simulation","SportsGame","StrategyGame","Education","Art","Art","Contruction","Music","Languages","Science","Astronomy","Biology","Chemistry","Geology","Math","MedicalSoftware","Physics","Teaching","Amusement","Applet","Archiving","Electronics","Emulator","Engineering","FileManager","Shell","Screensaver","TerminalEmulator","TrayIcon","System","Filesystem","Monitor","Security","Utility","Accessibility","Calculator","Clock","TextEditor","KDE","GNOME","GTK","Qt","Motif","Java","ConsoleOnly"]
+ for item in values:
+ if item not in valid:
+ self.errors.append("'%s' is not a registered Category" % item);
diff --git a/xdg/Exceptions.py b/xdg/Exceptions.py
index ab6c35a..f7d08be 100644
--- a/xdg/Exceptions.py
+++ b/xdg/Exceptions.py
@@ -5,47 +5,47 @@ Exception Classes for the xdg package
debug = False
class Error(Exception):
- def __init__(self, msg):
- self.msg = msg
- Exception.__init__(self, msg)
- def __str__(self):
- return self.msg
+ def __init__(self, msg):
+ self.msg = msg
+ Exception.__init__(self, msg)
+ def __str__(self):
+ return self.msg
class ValidationError(Error):
- def __init__(self, msg, file):
- self.msg = msg
- self.file = file
- Error.__init__(self, "ValidationError in file '%s': %s " % (file, msg))
+ def __init__(self, msg, file):
+ self.msg = msg
+ self.file = file
+ Error.__init__(self, "ValidationError in file '%s': %s " % (file, msg))
class ParsingError(Error):
- def __init__(self, msg, file):
- self.msg = msg
- self.file = file
- Error.__init__(self, "ParsingError in file '%s', %s" % (file, msg))
+ def __init__(self, msg, file):
+ self.msg = msg
+ self.file = file
+ Error.__init__(self, "ParsingError in file '%s', %s" % (file, msg))
class NoKeyError(Error):
- def __init__(self, key, group, file):
- Error.__init__(self, "No key '%s' in group %s of file %s" % (key, group, file))
- self.key = key
- self.group = group
+ def __init__(self, key, group, file):
+ Error.__init__(self, "No key '%s' in group %s of file %s" % (key, group, file))
+ self.key = key
+ self.group = group
class DuplicateKeyError(Error):
- def __init__(self, key, group, file):
- Error.__init__(self, "Duplicate key '%s' in group %s of file %s" % (key, group, file))
- self.key = key
- self.group = group
+ def __init__(self, key, group, file):
+ Error.__init__(self, "Duplicate key '%s' in group %s of file %s" % (key, group, file))
+ self.key = key
+ self.group = group
class NoGroupError(Error):
- def __init__(self, group, file):
- Error.__init__(self, "No group: %s in file %s" % (group, file))
- self.group = group
+ def __init__(self, group, file):
+ Error.__init__(self, "No group: %s in file %s" % (group, file))
+ self.group = group
class DuplicateGroupError(Error):
- def __init__(self, group, file):
- Error.__init__(self, "Duplicate group: %s in file %s" % (group, file))
- self.group = group
+ def __init__(self, group, file):
+ Error.__init__(self, "Duplicate group: %s in file %s" % (group, file))
+ self.group = group
class NoThemeError(Error):
- def __init__(self, theme):
- Error.__init__(self, "No such icon-theme: %s" % theme)
- self.theme = theme
+ def __init__(self, theme):
+ Error.__init__(self, "No such icon-theme: %s" % theme)
+ self.theme = theme
diff --git a/xdg/IconTheme.py b/xdg/IconTheme.py
index 45d5c22..5c30ebf 100644
--- a/xdg/IconTheme.py
+++ b/xdg/IconTheme.py
@@ -12,209 +12,209 @@ from xdg.Exceptions import *
import xdg.Config
class IconTheme(IniFile):
- "Class to parse and validate IconThemes"
- def __init__(self):
- IniFile.__init__(self)
-
- def __repr__(self):
- return self.name
-
- def parse(self, file):
- IniFile.parse(self, file, ["Icon Theme", "KDE Icon Theme"])
- self.dir = os.path.dirname(file)
- (nil, self.name) = os.path.split(self.dir)
-
- def getDir(self):
- return self.dir
-
- # Standard Keys
- def getName(self):
- return self.get('Name', locale=True)
- def getComment(self):
- return self.get('Comment', locale=True)
- def getInherits(self):
- return self.get('Inherits', list=True)
- def getDirectories(self):
- return self.get('Directories', list=True)
- def getHidden(self):
- return self.get('Hidden', type="boolean")
- def getExample(self):
- return self.get('Example')
-
- # Per Directory Keys
- def getSize(self, directory):
- return self.get('Size', type="integer", group=directory)
- def getContext(self, directory):
- return self.get('Context', group=directory)
- def getType(self, directory):
- value = self.get('Type', group=directory)
- if value:
- return value
- else:
- return "Threshold"
- def getMaxSize(self, directory):
- value = self.get('MaxSize', type="integer", group=directory)
- if value or value == 0:
- return value
- else:
- return self.getSize(directory)
- def getMinSize(self, directory):
- value = self.get('MinSize', type="integer", group=directory)
- if value or value == 0:
- return value
- else:
- return self.getSize(directory)
- def getThreshold(self, directory):
- value = self.get('Threshold', type="integer", group=directory)
- if value or value == 0:
- return value
- else:
- return 2
-
- # validation stuff
- def checkExtras(self):
- # header
- if self.defaultGroup == "KDE Icon Theme":
- self.warnings.append('[KDE Icon Theme]-Header is deprecated')
-
- # file extension
- if self.fileExtension == ".theme":
- pass
- elif self.fileExtension == ".desktop":
- self.warnings.append('.desktop fileExtension is deprecated')
- else:
- self.warnings.append('Unknown File extension')
-
- # Check required keys
- # Name
- try:
- self.name = self.content[self.defaultGroup]["Name"]
- except KeyError:
- self.errors.append("Key 'Name' is missing")
-
- # Comment
- try:
- self.comment = self.content[self.defaultGroup]["Comment"]
- except KeyError:
- self.errors.append("Key 'Comment' is missing")
-
- # Directories
- try:
- self.directories = self.content[self.defaultGroup]["Directories"]
- except KeyError:
- self.errors.append("Key 'Directories' is missing")
-
- def checkGroup(self, group):
- # check if group header is valid
- if group == self.defaultGroup:
- pass
- elif group in self.getDirectories():
- try:
- self.type = self.content[group]["Type"]
- except KeyError:
- self.type = "Threshold"
- try:
- self.name = self.content[group]["Name"]
- except KeyError:
- self.errors.append("Key 'Name' in Group '%s' is missing" % group)
- elif not (re.match("^\[X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group):
- self.errors.append("Invalid Group name: %s" % group)
-
- def checkKey(self, key, value, group):
- # standard keys
- if group == self.defaultGroup:
- if re.match("^Name"+xdg.Locale.regex+"$", key):
- pass
- elif re.match("^Comment"+xdg.Locale.regex+"$", key):
- pass
- elif key == "Inherits":
- self.checkValue(key, value, list=True)
- elif key == "Directories":
- self.checkValue(key, value, list=True)
- elif key == "Hidden":
- self.checkValue(key, value, type="boolean")
- elif key == "Example":
- self.checkValue(key, value)
- elif re.match("^X-[a-zA-Z0-9-]+", key):
- pass
- else:
- self.errors.append("Invalid key: %s" % key)
- elif group in self.getDirectories():
- if key == "Size":
- self.checkValue(key, value, type="integer")
- elif key == "Context":
- self.checkValue(key, value)
- elif key == "Type":
- self.checkValue(key, value)
- if value not in ["Fixed", "Scalable", "Threshold"]:
- self.errors.append("Key 'Type' must be one out of 'Fixed','Scalable','Threshold', but is %s" % value)
- elif key == "MaxSize":
- self.checkValue(key, value, type="integer")
- if self.type != "Scalable":
- self.errors.append("Key 'MaxSize' give, but Type is %s" % self.type)
- elif key == "MinSize":
- self.checkValue(key, value, type="integer")
- if self.type != "Scalable":
- self.errors.append("Key 'MinSize' give, but Type is %s" % self.type)
- elif key == "Threshold":
- self.checkValue(key, value, type="integer")
- if self.type != "Threshold":
- self.errors.append("Key 'Threshold' give, but Type is %s" % self.type)
- elif re.match("^X-[a-zA-Z0-9-]+", key):
- pass
- else:
- self.errors.append("Invalid key: %s" % key)
+ "Class to parse and validate IconThemes"
+ def __init__(self):
+ IniFile.__init__(self)
+
+ def __repr__(self):
+ return self.name
+
+ def parse(self, file):
+ IniFile.parse(self, file, ["Icon Theme", "KDE Icon Theme"])
+ self.dir = os.path.dirname(file)
+ (nil, self.name) = os.path.split(self.dir)
+
+ def getDir(self):
+ return self.dir
+
+ # Standard Keys
+ def getName(self):
+ return self.get('Name', locale=True)
+ def getComment(self):
+ return self.get('Comment', locale=True)
+ def getInherits(self):
+ return self.get('Inherits', list=True)
+ def getDirectories(self):
+ return self.get('Directories', list=True)
+ def getHidden(self):
+ return self.get('Hidden', type="boolean")
+ def getExample(self):
+ return self.get('Example')
+
+ # Per Directory Keys
+ def getSize(self, directory):
+ return self.get('Size', type="integer", group=directory)
+ def getContext(self, directory):
+ return self.get('Context', group=directory)
+ def getType(self, directory):
+ value = self.get('Type', group=directory)
+ if value:
+ return value
+ else:
+ return "Threshold"
+ def getMaxSize(self, directory):
+ value = self.get('MaxSize', type="integer", group=directory)
+ if value or value == 0:
+ return value
+ else:
+ return self.getSize(directory)
+ def getMinSize(self, directory):
+ value = self.get('MinSize', type="integer", group=directory)
+ if value or value == 0:
+ return value
+ else:
+ return self.getSize(directory)
+ def getThreshold(self, directory):
+ value = self.get('Threshold', type="integer", group=directory)
+ if value or value == 0:
+ return value
+ else:
+ return 2
+
+ # validation stuff
+ def checkExtras(self):
+ # header
+ if self.defaultGroup == "KDE Icon Theme":
+ self.warnings.append('[KDE Icon Theme]-Header is deprecated')
+
+ # file extension
+ if self.fileExtension == ".theme":
+ pass
+ elif self.fileExtension == ".desktop":
+ self.warnings.append('.desktop fileExtension is deprecated')
+ else:
+ self.warnings.append('Unknown File extension')
+
+ # Check required keys
+ # Name
+ try:
+ self.name = self.content[self.defaultGroup]["Name"]
+ except KeyError:
+ self.errors.append("Key 'Name' is missing")
+
+ # Comment
+ try:
+ self.comment = self.content[self.defaultGroup]["Comment"]
+ except KeyError:
+ self.errors.append("Key 'Comment' is missing")
+
+ # Directories
+ try:
+ self.directories = self.content[self.defaultGroup]["Directories"]
+ except KeyError:
+ self.errors.append("Key 'Directories' is missing")
+
+ def checkGroup(self, group):
+ # check if group header is valid
+ if group == self.defaultGroup:
+ pass
+ elif group in self.getDirectories():
+ try:
+ self.type = self.content[group]["Type"]
+ except KeyError:
+ self.type = "Threshold"
+ try:
+ self.name = self.content[group]["Name"]
+ except KeyError:
+ self.errors.append("Key 'Name' in Group '%s' is missing" % group)
+ elif not (re.match("^\[X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group):
+ self.errors.append("Invalid Group name: %s" % group)
+
+ def checkKey(self, key, value, group):
+ # standard keys
+ if group == self.defaultGroup:
+ if re.match("^Name"+xdg.Locale.regex+"$", key):
+ pass
+ elif re.match("^Comment"+xdg.Locale.regex+"$", key):
+ pass
+ elif key == "Inherits":
+ self.checkValue(key, value, list=True)
+ elif key == "Directories":
+ self.checkValue(key, value, list=True)
+ elif key == "Hidden":
+ self.checkValue(key, value, type="boolean")
+ elif key == "Example":
+ self.checkValue(key, value)
+ elif re.match("^X-[a-zA-Z0-9-]+", key):
+ pass
+ else:
+ self.errors.append("Invalid key: %s" % key)
+ elif group in self.getDirectories():
+ if key == "Size":
+ self.checkValue(key, value, type="integer")
+ elif key == "Context":
+ self.checkValue(key, value)
+ elif key == "Type":
+ self.checkValue(key, value)
+ if value not in ["Fixed", "Scalable", "Threshold"]:
+ self.errors.append("Key 'Type' must be one out of 'Fixed','Scalable','Threshold', but is %s" % value)
+ elif key == "MaxSize":
+ self.checkValue(key, value, type="integer")
+ if self.type != "Scalable":
+ self.errors.append("Key 'MaxSize' give, but Type is %s" % self.type)
+ elif key == "MinSize":
+ self.checkValue(key, value, type="integer")
+ if self.type != "Scalable":
+ self.errors.append("Key 'MinSize' give, but Type is %s" % self.type)
+ elif key == "Threshold":
+ self.checkValue(key, value, type="integer")
+ if self.type != "Threshold":
+ self.errors.append("Key 'Threshold' give, but Type is %s" % self.type)
+ elif re.match("^X-[a-zA-Z0-9-]+", key):
+ pass
+ else:
+ self.errors.append("Invalid key: %s" % key)
class IconData(IniFile):
- "Class to parse and validate IconData Files"
- def __init__(self):
- IniFile.__init__(self)
-
- def __repr__(self):
- return self.getDisplayName()
-
- def parse(self, file):
- IniFile.parse(self, file, ["Icon Data"])
-
- # Standard Keys
- def getDisplayName(self):
- return self.get('DisplayName', locale=True)
- def getEmbeddedTextRectangle(self):
- return self.get('EmbeddedTextRectangle', list=True)
- def getAttachPoints(self):
- return self.get('AttachPoints', type="point", list=True)
-
- # validation stuff
- def checkExtras(self):
- # file extension
- if self.fileExtension != ".icon":
- self.warnings.append('Unknown File extension')
-
- def checkGroup(self, group):
- # check if group header is valid
- if not (group == self.defaultGroup \
- or (re.match("^\[X-", group) and group.encode("ascii", 'ignore') == group)):
- self.errors.append("Invalid Group name: %s" % group.encode("ascii", "replace"))
-
- def checkKey(self, key, value, group):
- # standard keys
- if re.match("^DisplayName"+xdg.Locale.regex+"$", key):
- pass
- elif key == "EmbeddedTextRectangle":
- self.checkValue(key, value, type="integer", list=True)
- elif key == "AttachPoints":
- self.checkValue(key, value, type="point", list=True)
- elif re.match("^X-[a-zA-Z0-9-]+", key):
- pass
- else:
- self.errors.append("Invalid key: %s" % key)
+ "Class to parse and validate IconData Files"
+ def __init__(self):
+ IniFile.__init__(self)
+
+ def __repr__(self):
+ return self.getDisplayName()
+
+ def parse(self, file):
+ IniFile.parse(self, file, ["Icon Data"])
+
+ # Standard Keys
+ def getDisplayName(self):
+ return self.get('DisplayName', locale=True)
+ def getEmbeddedTextRectangle(self):
+ return self.get('EmbeddedTextRectangle', list=True)
+ def getAttachPoints(self):
+ return self.get('AttachPoints', type="point", list=True)
+
+ # validation stuff
+ def checkExtras(self):
+ # file extension
+ if self.fileExtension != ".icon":
+ self.warnings.append('Unknown File extension')
+
+ def checkGroup(self, group):
+ # check if group header is valid
+ if not (group == self.defaultGroup \
+ or (re.match("^\[X-", group) and group.encode("ascii", 'ignore') == group)):
+ self.errors.append("Invalid Group name: %s" % group.encode("ascii", "replace"))
+
+ def checkKey(self, key, value, group):
+ # standard keys
+ if re.match("^DisplayName"+xdg.Locale.regex+"$", key):
+ pass
+ elif key == "EmbeddedTextRectangle":
+ self.checkValue(key, value, type="integer", list=True)
+ elif key == "AttachPoints":
+ self.checkValue(key, value, type="point", list=True)
+ elif re.match("^X-[a-zA-Z0-9-]+", key):
+ pass
+ else:
+ self.errors.append("Invalid key: %s" % key)
icondirs = []
for basedir in xdg_data_dirs:
- icondirs.append(os.path.join(basedir, "icons"))
+ icondirs.append(os.path.join(basedir, "icons"))
icondirs.append("/usr/share/pixmaps")
icondirs.append(os.path.expanduser("~/.icons"))
@@ -225,161 +225,161 @@ dache = dict()
eache = dict()
def getIconPath(iconname, size = None, theme = None, extensions = ["png", "svg", "xpm"]):
- global themes
-
- if size == None:
- size = xdg.Config.icon_size
- if theme == None:
- theme = xdg.Config.icon_theme
-
- # if we have an absolute path, just return it
- if os.path.isabs(iconname):
- return iconname
-
- # check if it has an extension and strip it
- if os.path.splitext(iconname)[1][1:] in extensions:
- iconname = os.path.splitext(iconname)[0]
-
- # parse theme files
- try:
- if themes[0].name != theme:
- themes = []
- __addTheme(theme)
- except IndexError:
- __addTheme(theme)
-
- # more caching (icon looked up in the last 5 seconds?)
- tmp = "".join([iconname, str(size), theme, "".join(extensions)])
- if eache.has_key(tmp):
- if int(time.time() - eache[tmp][0]) >= xdg.Config.cache_time:
- del eache[tmp]
- else:
- return eache[tmp][1]
-
- for thme in themes:
- icon = LookupIcon(iconname, size, thme, extensions)
- if icon:
- eache[tmp] = [time.time(), icon]
- return icon
-
- # cache stuff again (directories lookuped up in the last 5 seconds?)
- for directory in icondirs:
- if (not dache.has_key(directory) \
- or (int(time.time() - dache[directory][1]) >= xdg.Config.cache_time \
- and dache[directory][2] < os.path.getmtime(directory))) \
- and os.path.isdir(directory):
- dache[directory] = [os.listdir(directory), time.time(), os.path.getmtime(directory)]
-
- for dir, values in dache.items():
- for extension in extensions:
- if iconname + "." + extension in values[0]:
- icon = os.path.join(dir, iconname + "." + extension)
- eache[tmp] = [time.time(), icon]
- return icon
-
- # we haven't found anything? "hicolor" is our fallback
- if theme != "hicolor":
- icon = getIconPath(iconname, size, "hicolor")
- eache[tmp] = [time.time(), icon]
- return icon
+ global themes
+
+ if size == None:
+ size = xdg.Config.icon_size
+ if theme == None:
+ theme = xdg.Config.icon_theme
+
+ # if we have an absolute path, just return it
+ if os.path.isabs(iconname):
+ return iconname
+
+ # check if it has an extension and strip it
+ if os.path.splitext(iconname)[1][1:] in extensions:
+ iconname = os.path.splitext(iconname)[0]
+
+ # parse theme files
+ try:
+ if themes[0].name != theme:
+ themes = []
+ __addTheme(theme)
+ except IndexError:
+ __addTheme(theme)
+
+ # more caching (icon looked up in the last 5 seconds?)
+ tmp = "".join([iconname, str(size), theme, "".join(extensions)])
+ if eache.has_key(tmp):
+ if int(time.time() - eache[tmp][0]) >= xdg.Config.cache_time:
+ del eache[tmp]
+ else:
+ return eache[tmp][1]
+
+ for thme in themes:
+ icon = LookupIcon(iconname, size, thme, extensions)
+ if icon:
+ eache[tmp] = [time.time(), icon]
+ return icon
+
+ # cache stuff again (directories lookuped up in the last 5 seconds?)
+ for directory in icondirs:
+ if (not dache.has_key(directory) \
+ or (int(time.time() - dache[directory][1]) >= xdg.Config.cache_time \
+ and dache[directory][2] < os.path.getmtime(directory))) \
+ and os.path.isdir(directory):
+ dache[directory] = [os.listdir(directory), time.time(), os.path.getmtime(directory)]
+
+ for dir, values in dache.items():
+ for extension in extensions:
+ if iconname + "." + extension in values[0]:
+ icon = os.path.join(dir, iconname + "." + extension)
+ eache[tmp] = [time.time(), icon]
+ return icon
+
+ # we haven't found anything? "hicolor" is our fallback
+ if theme != "hicolor":
+ icon = getIconPath(iconname, size, "hicolor")
+ eache[tmp] = [time.time(), icon]
+ return icon
def getIconData(path):
- if os.path.isfile(path):
- dirname = os.path.dirname(path)
- basename = os.path.basename(path)
- if os.path.isfile(os.path.join(dirname, basename + ".icon")):
- data = IconData()
- data.parse(os.path.join(dirname, basename + ".icon"))
- return data
+ if os.path.isfile(path):
+ dirname = os.path.dirname(path)
+ basename = os.path.basename(path)
+ if os.path.isfile(os.path.join(dirname, basename + ".icon")):
+ data = IconData()
+ data.parse(os.path.join(dirname, basename + ".icon"))
+ return data
def __addTheme(theme):
- for dir in icondirs:
- if os.path.isfile(os.path.join(dir, theme, "index.theme")):
- __parseTheme(os.path.join(dir,theme, "index.theme"))
- break
- elif os.path.isfile(os.path.join(dir, theme, "index.desktop")):
- __parseTheme(os.path.join(dir,theme, "index.desktop"))
- break
- else:
- if debug:
- raise NoThemeError(theme)
+ for dir in icondirs:
+ if os.path.isfile(os.path.join(dir, theme, "index.theme")):
+ __parseTheme(os.path.join(dir,theme, "index.theme"))
+ break
+ elif os.path.isfile(os.path.join(dir, theme, "index.desktop")):
+ __parseTheme(os.path.join(dir,theme, "index.desktop"))
+ break
+ else:
+ if debug:
+ raise NoThemeError(theme)
def __parseTheme(file):
- theme = IconTheme()
- theme.parse(file)
- themes.append(theme)
- for subtheme in theme.getInherits():
- __addTheme(subtheme)
+ theme = IconTheme()
+ theme.parse(file)
+ themes.append(theme)
+ for subtheme in theme.getInherits():
+ __addTheme(subtheme)
def LookupIcon(iconname, size, theme, extensions):
- # look for the cache
- if not cache.has_key(theme.name):
- cache[theme.name] = []
- cache[theme.name].append(time.time() - (xdg.Config.cache_time + 1)) # [0] last time of lookup
- cache[theme.name].append(0) # [1] mtime
- cache[theme.name].append(dict()) # [2] dir: [subdir, [items]]
-
- # cache stuff (directory lookuped up the in the last 5 seconds?)
- if int(time.time() - cache[theme.name][0]) >= xdg.Config.cache_time:
- cache[theme.name][0] = time.time()
- for subdir in theme.getDirectories():
- for directory in icondirs:
- dir = os.path.join(directory,theme.name,subdir)
- if (not cache[theme.name][2].has_key(dir) \
- or cache[theme.name][1] < os.path.getmtime(os.path.join(directory,theme.name))) \
- and subdir != "" \
- and os.path.isdir(dir):
- cache[theme.name][2][dir] = [subdir, os.listdir(dir)]
- cache[theme.name][1] = os.path.getmtime(os.path.join(directory,theme.name))
-
- for dir, values in cache[theme.name][2].items():
- if DirectoryMatchesSize(values[0], size, theme):
- for extension in extensions:
- if iconname + "." + extension in values[1]:
- return os.path.join(dir, iconname + "." + extension)
-
- minimal_size = sys.maxint
- closest_filename = ""
- for dir, values in cache[theme.name][2].items():
- distance = DirectorySizeDistance(values[0], size, theme)
- if distance < minimal_size:
- for extension in extensions:
- if iconname + "." + extension in values[1]:
- closest_filename = os.path.join(dir, iconname + "." + extension)
- minimal_size = distance
-
- return closest_filename
+ # look for the cache
+ if not cache.has_key(theme.name):
+ cache[theme.name] = []
+ cache[theme.name].append(time.time() - (xdg.Config.cache_time + 1)) # [0] last time of lookup
+ cache[theme.name].append(0) # [1] mtime
+ cache[theme.name].append(dict()) # [2] dir: [subdir, [items]]
+
+ # cache stuff (directory lookuped up the in the last 5 seconds?)
+ if int(time.time() - cache[theme.name][0]) >= xdg.Config.cache_time:
+ cache[theme.name][0] = time.time()
+ for subdir in theme.getDirectories():
+ for directory in icondirs:
+ dir = os.path.join(directory,theme.name,subdir)
+ if (not cache[theme.name][2].has_key(dir) \
+ or cache[theme.name][1] < os.path.getmtime(os.path.join(directory,theme.name))) \
+ and subdir != "" \
+ and os.path.isdir(dir):
+ cache[theme.name][2][dir] = [subdir, os.listdir(dir)]
+ cache[theme.name][1] = os.path.getmtime(os.path.join(directory,theme.name))
+
+ for dir, values in cache[theme.name][2].items():
+ if DirectoryMatchesSize(values[0], size, theme):
+ for extension in extensions:
+ if iconname + "." + extension in values[1]:
+ return os.path.join(dir, iconname + "." + extension)
+
+ minimal_size = sys.maxint
+ closest_filename = ""
+ for dir, values in cache[theme.name][2].items():
+ distance = DirectorySizeDistance(values[0], size, theme)
+ if distance < minimal_size:
+ for extension in extensions:
+ if iconname + "." + extension in values[1]:
+ closest_filename = os.path.join(dir, iconname + "." + extension)
+ minimal_size = distance
+
+ return closest_filename
def DirectoryMatchesSize(subdir, iconsize, theme):
- Type = theme.getType(subdir)
- Size = theme.getSize(subdir)
- Threshold = theme.getThreshold(subdir)
- MinSize = theme.getMinSize(subdir)
- MaxSize = theme.getMaxSize(subdir)
- if Type == "Fixed":
- return Size == iconsize
- elif Type == "Scaleable":
- return MinSize <= iconsize <= MaxSize
- elif Type == "Threshold":
- return Size - Threshold <= iconsize <= Size + Threshold
+ Type = theme.getType(subdir)
+ Size = theme.getSize(subdir)
+ Threshold = theme.getThreshold(subdir)
+ MinSize = theme.getMinSize(subdir)
+ MaxSize = theme.getMaxSize(subdir)
+ if Type == "Fixed":
+ return Size == iconsize
+ elif Type == "Scaleable":
+ return MinSize <= iconsize <= MaxSize
+ elif Type == "Threshold":
+ return Size - Threshold <= iconsize <= Size + Threshold
def DirectorySizeDistance(subdir, iconsize, theme):
- Type = theme.getType(subdir)
- Size = theme.getSize(subdir)
- Threshold = theme.getThreshold(subdir)
- MinSize = theme.getMinSize(subdir)
- MaxSize = theme.getMaxSize(subdir)
- if Type == "Fixed":
- return abs(Size - iconsize)
- elif Type == "Scalable":
- if iconsize < MinSize:
- return MinSize - iconsize
- elif iconsize > MaxSize:
- return MaxSize - iconsize
- return 0
- elif Type == "Threshold":
- if iconsize < Size - Threshold:
- return MinSize - iconsize
- elif iconsize > Size + Threshold:
- return iconsize - MaxSize
- return 0
+ Type = theme.getType(subdir)
+ Size = theme.getSize(subdir)
+ Threshold = theme.getThreshold(subdir)
+ MinSize = theme.getMinSize(subdir)
+ MaxSize = theme.getMaxSize(subdir)
+ if Type == "Fixed":
+ return abs(Size - iconsize)
+ elif Type == "Scalable":
+ if iconsize < MinSize:
+ return MinSize - iconsize
+ elif iconsize > MaxSize:
+ return MaxSize - iconsize
+ return 0
+ elif Type == "Threshold":
+ if iconsize < Size - Threshold:
+ return MinSize - iconsize
+ elif iconsize > Size + Threshold:
+ return iconsize - MaxSize
+ return 0
diff --git a/xdg/IniFile.py b/xdg/IniFile.py
index 90093a6..d711772 100644
--- a/xdg/IniFile.py
+++ b/xdg/IniFile.py
@@ -9,374 +9,374 @@ from Exceptions import *
import xdg.Locale
class IniFile:
- defaultGroup = ''
- fileExtension = ''
-
- filename = ''
-
- tainted = False
-
- def __init__(self, filename=None):
- self.content = dict()
- if filename:
- self.parse(filename)
-
- def __cmp__(self, other):
- return cmp(self.content, other.content)
-
- def parse(self, filename, headers):
- # for performance reasons
- content = self.content
-
- if not os.path.isfile(filename):
- raise ParsingError("File not found", filename)
-
- # parse file
- for line in file(filename,'r'):
- line = line.strip()
- # empty line
- if not line:
- continue
- # comment
- elif line[0] == '#':
- continue
- # new group
- elif line[0] == '[':
- currentGroup = line.lstrip("[").rstrip("]")
- if debug and self.hasGroup(currentGroup):
- raise DuplicateGroupError(currentGroup, filename)
- else:
- content[currentGroup] = {}
- # key
- else:
- index = line.find("=")
- key = line[0:index].strip()
- value = line[index+1:].strip()
- try:
- if debug and self.hasKey(key, currentGroup):
- raise DuplicateKeyError(key, currentGroup, filename)
- else:
- content[currentGroup][key] = value
- except (IndexError, UnboundLocalError):
- raise ParsingError("[%s]-Header missing" % headers[0], filename)
-
- self.filename = filename
- self.tainted = False
-
- # check header
- for header in headers:
- if content.has_key(header):
- self.defaultGroup = header
- break
- else:
- raise ParsingError("[%s]-Header missing" % headers[0], filename)
-
- # start stuff to access the keys
- def get(self, key, group=None, locale=False, type="string", list=False):
- # set default group
- if not group:
- group = self.defaultGroup
-
- # return key (with locale)
- if self.content.has_key(group) and self.content[group].has_key(key):
- if locale:
- value = self.content[group][self.__addLocale(key, group)]
- else:
- value = self.content[group][key]
- else:
- if debug:
- if not self.content.has_key(group):
- raise NoGroupError(group, self.filename)
- elif not self.content[group].has_key(key):
- raise NoKeyError(key, group, self.filename)
- else:
- value = ""
-
- if list == True:
- values = self.getList(value)
- result = []
- else:
- values = [value]
-
- for value in values:
- if type == "string" and locale == True:
- value = value.decode("utf-8", "ignore")
- elif type == "boolean":
- value = self.__getBoolean(value)
- elif type == "integer":
- try:
- value = int(value)
- except ValueError:
- value = 0
- elif type == "numeric":
- try:
- value = float(value)
- except ValueError:
- value = 0.0
- elif type == "regex":
- value = re.compile(value)
- elif type == "point":
- value = value.split(",")
-
- if list == True:
- result.append(value)
- else:
- result = value
-
- return result
- # end stuff to access the keys
-
- # start subget
- def getList(self, string):
- if re.search(r"(?<!\\)\;", string):
- list = re.split(r"(?<!\\);", string)
- elif re.search(r"(?<!\\)\|", string):
- list = re.split(r"(?<!\\)\|", string)
- elif re.search(r"(?<!\\),", string):
- list = re.split(r"(?<!\\),", string)
- else:
- list = [string]
- if list[-1] == "":
- list.pop()
- return list
-
- def __getBoolean(self, boolean):
- if boolean == 1 or boolean == "true" or boolean == "True":
- return True
- elif boolean == 0 or boolean == "false" or boolean == "False":
- return False
- return False
- # end subget
-
- def __addLocale(self, key, group=None):
- "add locale to key according the current lc_messages"
- # set default group
- if not group:
- group = self.defaultGroup
-
- for lang in xdg.Locale.langs:
- if self.content[group].has_key(key+'['+lang+']'):
- return key+'['+lang+']'
-
- return key
-
- # start validation stuff
- def validate(self, report="All"):
- "validate ... report = All / Warnings / Errors"
-
- self.warnings = []
- self.errors = []
-
- # get file extension
- self.fileExtension = os.path.splitext(self.filename)[1]
-
- # overwrite this for own checkings
- self.checkExtras()
-
- # check all keys
- for group in self.content:
- self.checkGroup(group)
- for key in self.content[group]:
- self.checkKey(key, self.content[group][key], group)
- # check if value is empty
- if self.content[group][key] == "":
- self.warnings.append("Value of Key '%s' is empty" % key)
-
- # raise Warnings / Errors
- msg = ""
-
- if report == "All" or report == "Warnings":
- for line in self.warnings:
- msg += "\n- " + line
-
- if report == "All" or report == "Errors":
- for line in self.errors:
- msg += "\n- " + line
-
- if msg:
- raise ValidationError(msg, self.filename)
-
- # check if group header is valid
- def checkGroup(self, group):
- pass
-
- # check if key is valid
- def checkKey(self, key, value, group):
- pass
-
- # check random stuff
- def checkValue(self, key, value, type="string", list=False):
- if list == True:
- values = self.getList(value)
- else:
- values = [value]
-
- for value in values:
- if type == "string":
- code = self.checkString(value)
- elif type == "boolean":
- code = self.checkBoolean(value)
- elif type == "number":
- code = self.checkNumber(value)
- elif type == "integer":
- code = self.checkInteger(value)
- elif type == "regex":
- code = self.checkRegex(value)
- elif type == "point":
- code = self.checkPoint(value)
- if code == 1:
- self.errors.append("'%s' is not a valid %s" % (value, type))
- elif code == 2:
- self.warnings.append("Value of key '%s' is deprecated" % key)
-
- def checkExtras(self):
- pass
-
- def checkBoolean(self, value):
- # 1 or 0 : deprecated
- if (value == "1" or value == "0"):
- return 2
- # true or false: ok
- elif not (value == "true" or value == "false"):
- return 1
-
- def checkNumber(self, value):
- # float() ValueError
- try:
- float(value)
- except:
- return 1
-
- def checkInteger(self, value):
- # int() ValueError
- try:
- int(value)
- except:
- return 1
-
- def checkPoint(self, value):
- if not re.match("^[0-9]+,[0-9]+$", value):
- return 1
-
- def checkString(self, value):
- # convert to ascii
- if not value.decode("utf-8", "ignore").encode("ascii", 'ignore') == value:
- return 1
-
- def checkRegex(self, value):
- try:
- re.compile(value)
- except:
- return 1
-
- # write support
- def write(self, filename=None):
- if not filename and not self.filename:
- raise ParsingError("File not found", "")
-
- if filename:
- self.filename = filename
- else:
- filename = self.filename
-
- if os.path.dirname(filename) and not os.path.isdir(os.path.dirname(filename)):
- os.makedirs(os.path.dirname(filename))
-
- fp = codecs.open(filename, 'w')
- if self.defaultGroup:
- fp.write("[%s]\n" % self.defaultGroup)
- for (key, value) in self.content[self.defaultGroup].items():
- fp.write("%s=%s\n" % (key, value))
- fp.write("\n")
- for (name, group) in self.content.items():
- if name != self.defaultGroup:
- fp.write("[%s]\n" % name)
- for (key, value) in group.items():
- fp.write("%s=%s\n" % (key, value))
- fp.write("\n")
- self.tainted = False
-
- def set(self, key, value, group=None, locale=False):
- # set default group
- if not group:
- group = self.defaultGroup
-
- if locale == True and len(xdg.Locale.langs) > 0:
- key = key + "[" + xdg.Locale.langs[0] + "]"
-
- try:
- if isinstance(value, unicode):
- self.content[group][key] = value.encode("utf-8", "ignore")
- else:
- self.content[group][key] = value
- except KeyError:
- raise NoGroupError(group, self.filename)
-
- self.tainted = (value == self.get(key, group))
-
- def addGroup(self, group):
- if self.hasGroup(group):
- if debug:
- raise DuplicateGroupError(group, self.filename)
- else:
- pass
- else:
- self.content[group] = {}
- self.tainted = True
-
- def removeGroup(self, group):
- existed = group in self.content
- if existed:
- del self.content[group]
- self.tainted = True
- else:
- if debug:
- raise NoGroupError(group, self.filename)
- return existed
-
- def removeKey(self, key, group=None, locales=True):
- # set default group
- if not group:
- group = self.defaultGroup
-
- try:
- if locales:
- for (name, value) in self.content[group].items():
- if re.match("^" + key + xdg.Locale.regex + "$", name) and name != key:
- value = self.content[group][name]
- del self.content[group][name]
- value = self.content[group][key]
- del self.content[group][key]
- self.tainted = True
- return value
- except KeyError, e:
- if debug:
- if e == group:
- raise NoGroupError(group, self.filename)
- else:
- raise NoKeyError(key, group, self.filename)
- else:
- return ""
-
- # misc
- def groups(self):
- return self.content.keys()
-
- def hasGroup(self, group):
- if self.content.has_key(group):
- return True
- else:
- return False
-
- def hasKey(self, key, group=None):
- # set default group
- if not group:
- group = self.defaultGroup
-
- if self.content[group].has_key(key):
- return True
- else:
- return False
-
- def getFileName(self):
- return self.filename
+ defaultGroup = ''
+ fileExtension = ''
+
+ filename = ''
+
+ tainted = False
+
+ def __init__(self, filename=None):
+ self.content = dict()
+ if filename:
+ self.parse(filename)
+
+ def __cmp__(self, other):
+ return cmp(self.content, other.content)
+
+ def parse(self, filename, headers):
+ # for performance reasons
+ content = self.content
+
+ if not os.path.isfile(filename):
+ raise ParsingError("File not found", filename)
+
+ # parse file
+ for line in file(filename,'r'):
+ line = line.strip()
+ # empty line
+ if not line:
+ continue
+ # comment
+ elif line[0] == '#':
+ continue
+ # new group
+ elif line[0] == '[':
+ currentGroup = line.lstrip("[").rstrip("]")
+ if debug and self.hasGroup(currentGroup):
+ raise DuplicateGroupError(currentGroup, filename)
+ else:
+ content[currentGroup] = {}
+ # key
+ else:
+ index = line.find("=")
+ key = line[0:index].strip()
+ value = line[index+1:].strip()
+ try:
+ if debug and self.hasKey(key, currentGroup):
+ raise DuplicateKeyError(key, currentGroup, filename)
+ else:
+ content[currentGroup][key] = value
+ except (IndexError, UnboundLocalError):
+ raise ParsingError("[%s]-Header missing" % headers[0], filename)
+
+ self.filename = filename
+ self.tainted = False
+
+ # check header
+ for header in headers:
+ if content.has_key(header):
+ self.defaultGroup = header
+ break
+ else:
+ raise ParsingError("[%s]-Header missing" % headers[0], filename)
+
+ # start stuff to access the keys
+ def get(self, key, group=None, locale=False, type="string", list=False):
+ # set default group
+ if not group:
+ group = self.defaultGroup
+
+ # return key (with locale)
+ if self.content.has_key(group) and self.content[group].has_key(key):
+ if locale:
+ value = self.content[group][self.__addLocale(key, group)]
+ else:
+ value = self.content[group][key]
+ else:
+ if debug:
+ if not self.content.has_key(group):
+ raise NoGroupError(group, self.filename)
+ elif not self.content[group].has_key(key):
+ raise NoKeyError(key, group, self.filename)
+ else:
+ value = ""
+
+ if list == True:
+ values = self.getList(value)
+ result = []
+ else:
+ values = [value]
+
+ for value in values:
+ if type == "string" and locale == True:
+ value = value.decode("utf-8", "ignore")
+ elif type == "boolean":
+ value = self.__getBoolean(value)
+ elif type == "integer":
+ try:
+ value = int(value)
+ except ValueError:
+ value = 0
+ elif type == "numeric":
+ try:
+ value = float(value)
+ except ValueError:
+ value = 0.0
+ elif type == "regex":
+ value = re.compile(value)
+ elif type == "point":
+ value = value.split(",")
+
+ if list == True:
+ result.append(value)
+ else:
+ result = value
+
+ return result
+ # end stuff to access the keys
+
+ # start subget
+ def getList(self, string):
+ if re.search(r"(?<!\\)\;", string):
+ list = re.split(r"(?<!\\);", string)
+ elif re.search(r"(?<!\\)\|", string):
+ list = re.split(r"(?<!\\)\|", string)
+ elif re.search(r"(?<!\\),", string):
+ list = re.split(r"(?<!\\),", string)
+ else:
+ list = [string]
+ if list[-1] == "":
+ list.pop()
+ return list
+
+ def __getBoolean(self, boolean):
+ if boolean == 1 or boolean == "true" or boolean == "True":
+ return True
+ elif boolean == 0 or boolean == "false" or boolean == "False":
+ return False
+ return False
+ # end subget
+
+ def __addLocale(self, key, group=None):
+ "add locale to key according the current lc_messages"
+ # set default group
+ if not group:
+ group = self.defaultGroup
+
+ for lang in xdg.Locale.langs:
+ if self.content[group].has_key(key+'['+lang+']'):
+ return key+'['+lang+']'
+
+ return key
+
+ # start validation stuff
+ def validate(self, report="All"):
+ "validate ... report = All / Warnings / Errors"
+
+ self.warnings = []
+ self.errors = []
+
+ # get file extension
+ self.fileExtension = os.path.splitext(self.filename)[1]
+
+ # overwrite this for own checkings
+ self.checkExtras()
+
+ # check all keys
+ for group in self.content:
+ self.checkGroup(group)
+ for key in self.content[group]:
+ self.checkKey(key, self.content[group][key], group)
+ # check if value is empty
+ if self.content[group][key] == "":
+ self.warnings.append("Value of Key '%s' is empty" % key)
+
+ # raise Warnings / Errors
+ msg = ""
+
+ if report == "All" or report == "Warnings":
+ for line in self.warnings:
+ msg += "\n- " + line
+
+ if report == "All" or report == "Errors":
+ for line in self.errors:
+ msg += "\n- " + line
+
+ if msg:
+ raise ValidationError(msg, self.filename)
+
+ # check if group header is valid
+ def checkGroup(self, group):
+ pass
+
+ # check if key is valid
+ def checkKey(self, key, value, group):
+ pass
+
+ # check random stuff
+ def checkValue(self, key, value, type="string", list=False):
+ if list == True:
+ values = self.getList(value)
+ else:
+ values = [value]
+
+ for value in values:
+ if type == "string":
+ code = self.checkString(value)
+ elif type == "boolean":
+ code = self.checkBoolean(value)
+ elif type == "number":
+ code = self.checkNumber(value)
+ elif type == "integer":
+ code = self.checkInteger(value)
+ elif type == "regex":
+ code = self.checkRegex(value)
+ elif type == "point":
+ code = self.checkPoint(value)
+ if code == 1:
+ self.errors.append("'%s' is not a valid %s" % (value, type))
+ elif code == 2:
+ self.warnings.append("Value of key '%s' is deprecated" % key)
+
+ def checkExtras(self):
+ pass
+
+ def checkBoolean(self, value):
+ # 1 or 0 : deprecated
+ if (value == "1" or value == "0"):
+ return 2
+ # true or false: ok
+ elif not (value == "true" or value == "false"):
+ return 1
+
+ def checkNumber(self, value):
+ # float() ValueError
+ try:
+ float(value)
+ except:
+ return 1
+
+ def checkInteger(self, value):
+ # int() ValueError
+ try:
+ int(value)
+ except:
+ return 1
+
+ def checkPoint(self, value):
+ if not re.match("^[0-9]+,[0-9]+$", value):
+ return 1
+
+ def checkString(self, value):
+ # convert to ascii
+ if not value.decode("utf-8", "ignore").encode("ascii", 'ignore') == value:
+ return 1
+
+ def checkRegex(self, value):
+ try:
+ re.compile(value)
+ except:
+ return 1
+
+ # write support
+ def write(self, filename=None):
+ if not filename and not self.filename:
+ raise ParsingError("File not found", "")
+
+ if filename:
+ self.filename = filename
+ else:
+ filename = self.filename
+
+ if os.path.dirname(filename) and not os.path.isdir(os.path.dirname(filename)):
+ os.makedirs(os.path.dirname(filename))
+
+ fp = codecs.open(filename, 'w')
+ if self.defaultGroup:
+ fp.write("[%s]\n" % self.defaultGroup)
+ for (key, value) in self.content[self.defaultGroup].items():
+ fp.write("%s=%s\n" % (key, value))
+ fp.write("\n")
+ for (name, group) in self.content.items():
+ if name != self.defaultGroup:
+ fp.write("[%s]\n" % name)
+ for (key, value) in group.items():
+ fp.write("%s=%s\n" % (key, value))
+ fp.write("\n")
+ self.tainted = False
+
+ def set(self, key, value, group=None, locale=False):
+ # set default group
+ if not group:
+ group = self.defaultGroup
+
+ if locale == True and len(xdg.Locale.langs) > 0:
+ key = key + "[" + xdg.Locale.langs[0] + "]"
+
+ try:
+ if isinstance(value, unicode):
+ self.content[group][key] = value.encode("utf-8", "ignore")
+ else:
+ self.content[group][key] = value
+ except KeyError:
+ raise NoGroupError(group, self.filename)
+
+ self.tainted = (value == self.get(key, group))
+
+ def addGroup(self, group):
+ if self.hasGroup(group):
+ if debug:
+ raise DuplicateGroupError(group, self.filename)
+ else:
+ pass
+ else:
+ self.content[group] = {}
+ self.tainted = True
+
+ def removeGroup(self, group):
+ existed = group in self.content
+ if existed:
+ del self.content[group]
+ self.tainted = True
+ else:
+ if debug:
+ raise NoGroupError(group, self.filename)
+ return existed
+
+ def removeKey(self, key, group=None, locales=True):
+ # set default group
+ if not group:
+ group = self.defaultGroup
+
+ try:
+ if locales:
+ for (name, value) in self.content[group].items():
+ if re.match("^" + key + xdg.Locale.regex + "$", name) and name != key:
+ value = self.content[group][name]
+ del self.content[group][name]
+ value = self.content[group][key]
+ del self.content[group][key]
+ self.tainted = True
+ return value
+ except KeyError, e:
+ if debug:
+ if e == group:
+ raise NoGroupError(group, self.filename)
+ else:
+ raise NoKeyError(key, group, self.filename)
+ else:
+ return ""
+
+ # misc
+ def groups(self):
+ return self.content.keys()
+
+ def hasGroup(self, group):
+ if self.content.has_key(group):
+ return True
+ else:
+ return False
+
+ def hasKey(self, key, group=None):
+ # set default group
+ if not group:
+ group = self.defaultGroup
+
+ if self.content[group].has_key(key):
+ return True
+ else:
+ return False
+
+ def getFileName(self):
+ return self.filename
diff --git a/xdg/Locale.py b/xdg/Locale.py
index 88a4fa8..d30d91a 100644
--- a/xdg/Locale.py
+++ b/xdg/Locale.py
@@ -12,68 +12,68 @@ from locale import normalize
regex = "(\[([a-zA-Z]+)(_[a-zA-Z]+)?(\.[a-zA-Z\-0-9]+)?(@[a-zA-Z]+)?\])?"
def _expand_lang(locale):
- locale = normalize(locale)
- COMPONENT_CODESET = 1 << 0
- COMPONENT_MODIFIER = 1 << 1
- COMPONENT_TERRITORY = 1 << 2
- # split up the locale into its base components
- mask = 0
- pos = locale.find('@')
- if pos >= 0:
- modifier = locale[pos:]
- locale = locale[:pos]
- mask |= COMPONENT_MODIFIER
- else:
- modifier = ''
- pos = locale.find('.')
- codeset = ''
- if pos >= 0:
- locale = locale[:pos]
- pos = locale.find('_')
- if pos >= 0:
- territory = locale[pos:]
- locale = locale[:pos]
- mask |= COMPONENT_TERRITORY
- else:
- territory = ''
- language = locale
- ret = []
- for i in range(mask+1):
- if not (i & ~mask): # if all components for this combo exist ...
- val = language
- if i & COMPONENT_TERRITORY: val += territory
- if i & COMPONENT_CODESET: val += codeset
- if i & COMPONENT_MODIFIER: val += modifier
- ret.append(val)
- ret.reverse()
- return ret
+ locale = normalize(locale)
+ COMPONENT_CODESET = 1 << 0
+ COMPONENT_MODIFIER = 1 << 1
+ COMPONENT_TERRITORY = 1 << 2
+ # split up the locale into its base components
+ mask = 0
+ pos = locale.find('@')
+ if pos >= 0:
+ modifier = locale[pos:]
+ locale = locale[:pos]
+ mask |= COMPONENT_MODIFIER
+ else:
+ modifier = ''
+ pos = locale.find('.')
+ codeset = ''
+ if pos >= 0:
+ locale = locale[:pos]
+ pos = locale.find('_')
+ if pos >= 0:
+ territory = locale[pos:]
+ locale = locale[:pos]
+ mask |= COMPONENT_TERRITORY
+ else:
+ territory = ''
+ language = locale
+ ret = []
+ for i in range(mask+1):
+ if not (i & ~mask): # if all components for this combo exist ...
+ val = language
+ if i & COMPONENT_TERRITORY: val += territory
+ if i & COMPONENT_CODESET: val += codeset
+ if i & COMPONENT_MODIFIER: val += modifier
+ ret.append(val)
+ ret.reverse()
+ return ret
def expand_languages(languages=None):
- # Get some reasonable defaults for arguments that were not supplied
- if languages is None:
- languages = []
- for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
- val = os.environ.get(envar)
- if val:
- languages = val.split(':')
- break
- #if 'C' not in languages:
- # languages.append('C')
+ # Get some reasonable defaults for arguments that were not supplied
+ if languages is None:
+ languages = []
+ for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
+ val = os.environ.get(envar)
+ if val:
+ languages = val.split(':')
+ break
+ #if 'C' not in languages:
+ # languages.append('C')
- # now normalize and expand the languages
- nelangs = []
- for lang in languages:
- for nelang in _expand_lang(lang):
- if nelang not in nelangs:
- nelangs.append(nelang)
- return nelangs
+ # now normalize and expand the languages
+ nelangs = []
+ for lang in languages:
+ for nelang in _expand_lang(lang):
+ if nelang not in nelangs:
+ nelangs.append(nelang)
+ return nelangs
def update(language=None):
- global langs
- if language:
- langs = expand_languages([language])
- else:
- langs = expand_languages()
+ global langs
+ if language:
+ langs = expand_languages([language])
+ else:
+ langs = expand_languages()
langs = []
update()
diff --git a/xdg/Menu.py b/xdg/Menu.py
index 03a933a..fd62f12 100644
--- a/xdg/Menu.py
+++ b/xdg/Menu.py
@@ -16,1051 +16,1051 @@ import xdg.Config
ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE
class Menu:
- def __init__(self):
- # Public stuff
- self.Name = ""
- self.Directory = None
- self.Entries = []
- self.Doc = ""
- self.Filename = ""
- self.Depth = 0
- self.Parent = None
- self.NotInXml = False
-
- # Can be one of Deleted/NoDisplay/Hidden/Empty/NotShowIn or True
- self.Show = True
- self.Visible = 0
-
- # Private stuff, only needed for parsing
- self.AppDirs = []
- self.DefaultLayout = None
- self.Deleted = "notset"
- self.Directories = []
- self.DirectoryDirs = []
- self.Layout = None
- self.MenuEntries = []
- self.Moves = []
- self.OnlyUnallocated = "notset"
- self.Rules = []
- self.Submenus = []
-
- def __str__(self):
- return self.Name
-
- def __add__(self, other):
- for dir in other.AppDirs:
- self.AppDirs.append(dir)
-
- for dir in other.DirectoryDirs:
- self.DirectoryDirs.append(dir)
-
- for directory in other.Directories:
- self.Directories.append(directory)
-
- if other.Deleted != "notset":
- self.Deleted = other.Deleted
-
- if other.OnlyUnallocated != "notset":
- self.OnlyUnallocated = other.OnlyUnallocated
-
- if other.Layout:
- self.Layout = other.Layout
-
- if other.DefaultLayout:
- self.DefaultLayout = other.DefaultLayout
-
- for rule in other.Rules:
- self.Rules.append(rule)
-
- for move in other.Moves:
- self.Moves.append(move)
-
- for submenu in other.Submenus:
- self.addSubmenu(submenu)
-
- return self
-
- # FIXME: Performance: cache getName()
- def __cmp__(self, other):
- return locale.strcoll(self.getName(), other.getName())
-
- def __eq__(self, other):
- if self.Name == str(other):
- return True
- else:
- return False
-
- """ PUBLIC STUFF """
- def getEntries(self, hidden=False):
- for entry in self.Entries:
- if hidden == True:
- yield entry
- elif entry.Show == True:
- yield entry
-
- # FIXME: Add searchEntry/seaqrchMenu function
- # search for name/comment/genericname/desktopfileide
- # return multiple items
-
- def getMenuEntry(self, desktopfileid, deep = False):
- for menuentry in self.MenuEntries:
- if menuentry.DesktopFileID == desktopfileid:
- return menuentry
- if deep == True:
- for submenu in self.Submenus:
- submenu.getMenuEntry(desktopfileid, deep)
-
- def getMenu(self, path):
- array = path.split("/", 1)
- for submenu in self.Submenus:
- if submenu.Name == array[0]:
- if len(array) > 1:
- return submenu.getMenu(array[1])
- else:
- return submenu
-
- def getPath(self, org=False, toplevel=False):
- parent = self
- names=[]
- while 1:
- if org:
- names.append(parent.Name)
- else:
- names.append(parent.getName())
- if parent.Depth > 0:
- parent = parent.Parent
- else:
- break
- names.reverse()
- path = ""
- if toplevel == False:
- names.pop(0)
- for name in names:
- path = os.path.join(path, name)
- return path
-
- def getName(self):
- try:
- return self.Directory.DesktopEntry.getName()
- except AttributeError:
- return self.Name
-
- def getGenericName(self):
- try:
- return self.Directory.DesktopEntry.getGenericName()
- except AttributeError:
- return ""
-
- def getComment(self):
- try:
- return self.Directory.DesktopEntry.getComment()
- except AttributeError:
- return ""
-
- def getIcon(self):
- try:
- return self.Directory.DesktopEntry.getIcon()
- except AttributeError:
- return ""
-
- """ PRIVATE STUFF """
- def addSubmenu(self, newmenu):
- for submenu in self.Submenus:
- if submenu == newmenu:
- submenu += newmenu
- break
- else:
- self.Submenus.append(newmenu)
- newmenu.Parent = self
- newmenu.Depth = self.Depth + 1
+ def __init__(self):
+ # Public stuff
+ self.Name = ""
+ self.Directory = None
+ self.Entries = []
+ self.Doc = ""
+ self.Filename = ""
+ self.Depth = 0
+ self.Parent = None
+ self.NotInXml = False
+
+ # Can be one of Deleted/NoDisplay/Hidden/Empty/NotShowIn or True
+ self.Show = True
+ self.Visible = 0
+
+ # Private stuff, only needed for parsing
+ self.AppDirs = []
+ self.DefaultLayout = None
+ self.Deleted = "notset"
+ self.Directories = []
+ self.DirectoryDirs = []
+ self.Layout = None
+ self.MenuEntries = []
+ self.Moves = []
+ self.OnlyUnallocated = "notset"
+ self.Rules = []
+ self.Submenus = []
+
+ def __str__(self):
+ return self.Name
+
+ def __add__(self, other):
+ for dir in other.AppDirs:
+ self.AppDirs.append(dir)
+
+ for dir in other.DirectoryDirs:
+ self.DirectoryDirs.append(dir)
+
+ for directory in other.Directories:
+ self.Directories.append(directory)
+
+ if other.Deleted != "notset":
+ self.Deleted = other.Deleted
+
+ if other.OnlyUnallocated != "notset":
+ self.OnlyUnallocated = other.OnlyUnallocated
+
+ if other.Layout:
+ self.Layout = other.Layout
+
+ if other.DefaultLayout:
+ self.DefaultLayout = other.DefaultLayout
+
+ for rule in other.Rules:
+ self.Rules.append(rule)
+
+ for move in other.Moves:
+ self.Moves.append(move)
+
+ for submenu in other.Submenus:
+ self.addSubmenu(submenu)
+
+ return self
+
+ # FIXME: Performance: cache getName()
+ def __cmp__(self, other):
+ return locale.strcoll(self.getName(), other.getName())
+
+ def __eq__(self, other):
+ if self.Name == str(other):
+ return True
+ else:
+ return False
+
+ """ PUBLIC STUFF """
+ def getEntries(self, hidden=False):
+ for entry in self.Entries:
+ if hidden == True:
+ yield entry
+ elif entry.Show == True:
+ yield entry
+
+ # FIXME: Add searchEntry/seaqrchMenu function
+ # search for name/comment/genericname/desktopfileide
+ # return multiple items
+
+ def getMenuEntry(self, desktopfileid, deep = False):
+ for menuentry in self.MenuEntries:
+ if menuentry.DesktopFileID == desktopfileid:
+ return menuentry
+ if deep == True:
+ for submenu in self.Submenus:
+ submenu.getMenuEntry(desktopfileid, deep)
+
+ def getMenu(self, path):
+ array = path.split("/", 1)
+ for submenu in self.Submenus:
+ if submenu.Name == array[0]:
+ if len(array) > 1:
+ return submenu.getMenu(array[1])
+ else:
+ return submenu
+
+ def getPath(self, org=False, toplevel=False):
+ parent = self
+ names=[]
+ while 1:
+ if org:
+ names.append(parent.Name)
+ else:
+ names.append(parent.getName())
+ if parent.Depth > 0:
+ parent = parent.Parent
+ else:
+ break
+ names.reverse()
+ path = ""
+ if toplevel == False:
+ names.pop(0)
+ for name in names:
+ path = os.path.join(path, name)
+ return path
+
+ def getName(self):
+ try:
+ return self.Directory.DesktopEntry.getName()
+ except AttributeError:
+ return self.Name
+
+ def getGenericName(self):
+ try:
+ return self.Directory.DesktopEntry.getGenericName()
+ except AttributeError:
+ return ""
+
+ def getComment(self):
+ try:
+ return self.Directory.DesktopEntry.getComment()
+ except AttributeError:
+ return ""
+
+ def getIcon(self):
+ try:
+ return self.Directory.DesktopEntry.getIcon()
+ except AttributeError:
+ return ""
+
+ """ PRIVATE STUFF """
+ def addSubmenu(self, newmenu):
+ for submenu in self.Submenus:
+ if submenu == newmenu:
+ submenu += newmenu
+ break
+ else:
+ self.Submenus.append(newmenu)
+ newmenu.Parent = self
+ newmenu.Depth = self.Depth + 1
class Move:
- "A move operation"
- def __init__(self, node=None):
- if node:
- self.parseNode(node)
- else:
- self.Old = ""
- self.New = ""
-
- def __cmp__(self, other):
- return cmp(self.Old, other.Old)
-
- def parseNode(self, node):
- for child in node.childNodes:
- if child.nodeType == ELEMENT_NODE:
- if child.tagName == "Old":
- try:
- self.parseOld(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('Old cannot be empty', '??')
- elif child.tagName == "New":
- try:
- self.parseNew(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('New cannot be empty', '??')
-
- def parseOld(self, value):
- self.Old = value
- def parseNew(self, value):
- self.New = value
+ "A move operation"
+ def __init__(self, node=None):
+ if node:
+ self.parseNode(node)
+ else:
+ self.Old = ""
+ self.New = ""
+
+ def __cmp__(self, other):
+ return cmp(self.Old, other.Old)
+
+ def parseNode(self, node):
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if child.tagName == "Old":
+ try:
+ self.parseOld(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('Old cannot be empty', '??')
+ elif child.tagName == "New":
+ try:
+ self.parseNew(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('New cannot be empty', '??')
+
+ def parseOld(self, value):
+ self.Old = value
+ def parseNew(self, value):
+ self.New = value
class Layout:
- "Menu Layout class"
- def __init__(self, node=None):
- self.order = []
- if node:
- self.show_empty = node.getAttribute("show_empty") or "false"
- self.inline = node.getAttribute("inline") or "false"
- self.inline_limit = node.getAttribute("inline_limit") or 4
- self.inline_header = node.getAttribute("inline_header") or "true"
- self.inline_alias = node.getAttribute("inline_alias") or "false"
- self.inline_limit = int(self.inline_limit)
- self.parseNode(node)
- else:
- self.show_empty = "false"
- self.inline = "false"
- self.inline_limit = 4
- self.inline_header = "true"
- self.inline_alias = "false"
- self.order.append(["Merge", "menus"])
- self.order.append(["Merge", "files"])
-
- def parseNode(self, node):
- for child in node.childNodes:
- if child.nodeType == ELEMENT_NODE:
- if child.tagName == "Menuname":
- try:
- self.parseMenuname(
- child.childNodes[0].nodeValue,
- child.getAttribute("show_empty") or "false",
- child.getAttribute("inline") or "false",
- child.getAttribute("inline_limit") or 4,
- child.getAttribute("inline_header") or "true",
- child.getAttribute("inline_alias") or "false" )
- except IndexError:
- raise ValidationError('Menuname cannot be empty', "")
- elif child.tagName == "Separator":
- self.parseSeparator()
- elif child.tagName == "Filename":
- try:
- self.parseFilename(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('Filename cannot be empty', "")
- elif child.tagName == "Merge":
- self.parseMerge(child.getAttribute("type") or "all")
-
- def parseMenuname(self, value, empty="false", inline="false", inline_limit=4, inline_header="true", inline_alias="false"):
- self.order.append(["Menuname", value, empty, inline, inline_limit, inline_header, inline_alias])
- self.order[-1][4] = int(self.order[-1][4])
-
- def parseSeparator(self):
- self.order.append(["Separator"])
-
- def parseFilename(self, value):
- self.order.append(["Filename", value])
-
- def parseMerge(self, type="all"):
- self.order.append(["Merge", type])
+ "Menu Layout class"
+ def __init__(self, node=None):
+ self.order = []
+ if node:
+ self.show_empty = node.getAttribute("show_empty") or "false"
+ self.inline = node.getAttribute("inline") or "false"
+ self.inline_limit = node.getAttribute("inline_limit") or 4
+ self.inline_header = node.getAttribute("inline_header") or "true"
+ self.inline_alias = node.getAttribute("inline_alias") or "false"
+ self.inline_limit = int(self.inline_limit)
+ self.parseNode(node)
+ else:
+ self.show_empty = "false"
+ self.inline = "false"
+ self.inline_limit = 4
+ self.inline_header = "true"
+ self.inline_alias = "false"
+ self.order.append(["Merge", "menus"])
+ self.order.append(["Merge", "files"])
+
+ def parseNode(self, node):
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if child.tagName == "Menuname":
+ try:
+ self.parseMenuname(
+ child.childNodes[0].nodeValue,
+ child.getAttribute("show_empty") or "false",
+ child.getAttribute("inline") or "false",
+ child.getAttribute("inline_limit") or 4,
+ child.getAttribute("inline_header") or "true",
+ child.getAttribute("inline_alias") or "false" )
+ except IndexError:
+ raise ValidationError('Menuname cannot be empty', "")
+ elif child.tagName == "Separator":
+ self.parseSeparator()
+ elif child.tagName == "Filename":
+ try:
+ self.parseFilename(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('Filename cannot be empty', "")
+ elif child.tagName == "Merge":
+ self.parseMerge(child.getAttribute("type") or "all")
+
+ def parseMenuname(self, value, empty="false", inline="false", inline_limit=4, inline_header="true", inline_alias="false"):
+ self.order.append(["Menuname", value, empty, inline, inline_limit, inline_header, inline_alias])
+ self.order[-1][4] = int(self.order[-1][4])
+
+ def parseSeparator(self):
+ self.order.append(["Separator"])
+
+ def parseFilename(self, value):
+ self.order.append(["Filename", value])
+
+ def parseMerge(self, type="all"):
+ self.order.append(["Merge", type])
class Rule:
- "Inlcude / Exclude Rules Class"
- def __init__(self, type, node=None):
- # Type is Include or Exclude
- self.Type = type
- # Rule is a python expression
- self.Rule = ""
-
- # Private attributes, only needed for parsing
- self.Depth = 0
- self.Expr = [ "or" ]
- self.New = True
-
- # Begin parsing
- if node:
- self.parseNode(node)
- self.compile()
-
- def __str__(self):
- return self.Rule
-
- def compile(self):
- exec("""
+ "Inlcude / Exclude Rules Class"
+ def __init__(self, type, node=None):
+ # Type is Include or Exclude
+ self.Type = type
+ # Rule is a python expression
+ self.Rule = ""
+
+ # Private attributes, only needed for parsing
+ self.Depth = 0
+ self.Expr = [ "or" ]
+ self.New = True
+
+ # Begin parsing
+ if node:
+ self.parseNode(node)
+ self.compile()
+
+ def __str__(self):
+ return self.Rule
+
+ def compile(self):
+ exec("""
def do(menuentries, type, run):
for menuentry in menuentries:
- if run == 2 and ( menuentry.MatchedInclude == True \
- or menuentry.Allocated == True ):
- continue
- elif %s:
- if type == "Include":
- menuentry.Add = True
- menuentry.MatchedInclude = True
- else:
- menuentry.Add = False
+ if run == 2 and ( menuentry.MatchedInclude == True \
+ or menuentry.Allocated == True ):
+ continue
+ elif %s:
+ if type == "Include":
+ menuentry.Add = True
+ menuentry.MatchedInclude = True
+ else:
+ menuentry.Add = False
return menuentries
""" % self.Rule) in self.__dict__
- def parseNode(self, node):
- for child in node.childNodes:
- if child.nodeType == ELEMENT_NODE:
- if child.tagName == 'Filename':
- try:
- self.parseFilename(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('Filename cannot be empty', "???")
- elif child.tagName == 'Category':
- try:
- self.parseCategory(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('Category cannot be empty', "???")
- elif child.tagName == 'All':
- self.parseAll()
- elif child.tagName == 'And':
- self.parseAnd(child)
- elif child.tagName == 'Or':
- self.parseOr(child)
- elif child.tagName == 'Not':
- self.parseNot(child)
-
- def parseNew(self, set=True):
- if not self.New:
- self.Rule += " " + self.Expr[self.Depth] + " "
- if not set:
- self.New = True
- elif set:
- self.New = False
-
- def parseFilename(self, value):
- self.parseNew()
- self.Rule += "menuentry.DesktopFileID == '%s'" % value.strip().replace("\\", r"\\").replace("'", r"\'")
-
- def parseCategory(self, value):
- self.parseNew()
- self.Rule += "'%s' in menuentry.Categories" % value.strip()
-
- def parseAll(self):
- self.parseNew()
- self.Rule += "True"
-
- def parseAnd(self, node):
- self.parseNew(False)
- self.Rule += "("
- self.Depth += 1
- self.Expr.append("and")
- self.parseNode(node)
- self.Depth -= 1
- self.Expr.pop()
- self.Rule += ")"
-
- def parseOr(self, node):
- self.parseNew(False)
- self.Rule += "("
- self.Depth += 1
- self.Expr.append("or")
- self.parseNode(node)
- self.Depth -= 1
- self.Expr.pop()
- self.Rule += ")"
-
- def parseNot(self, node):
- self.parseNew(False)
- self.Rule += "not ("
- self.Depth += 1
- self.Expr.append("or")
- self.parseNode(node)
- self.Depth -= 1
- self.Expr.pop()
- self.Rule += ")"
+ def parseNode(self, node):
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if child.tagName == 'Filename':
+ try:
+ self.parseFilename(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('Filename cannot be empty', "???")
+ elif child.tagName == 'Category':
+ try:
+ self.parseCategory(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('Category cannot be empty', "???")
+ elif child.tagName == 'All':
+ self.parseAll()
+ elif child.tagName == 'And':
+ self.parseAnd(child)
+ elif child.tagName == 'Or':
+ self.parseOr(child)
+ elif child.tagName == 'Not':
+ self.parseNot(child)
+
+ def parseNew(self, set=True):
+ if not self.New:
+ self.Rule += " " + self.Expr[self.Depth] + " "
+ if not set:
+ self.New = True
+ elif set:
+ self.New = False
+
+ def parseFilename(self, value):
+ self.parseNew()
+ self.Rule += "menuentry.DesktopFileID == '%s'" % value.strip().replace("\\", r"\\").replace("'", r"\'")
+
+ def parseCategory(self, value):
+ self.parseNew()
+ self.Rule += "'%s' in menuentry.Categories" % value.strip()
+
+ def parseAll(self):
+ self.parseNew()
+ self.Rule += "True"
+
+ def parseAnd(self, node):
+ self.parseNew(False)
+ self.Rule += "("
+ self.Depth += 1
+ self.Expr.append("and")
+ self.parseNode(node)
+ self.Depth -= 1
+ self.Expr.pop()
+ self.Rule += ")"
+
+ def parseOr(self, node):
+ self.parseNew(False)
+ self.Rule += "("
+ self.Depth += 1
+ self.Expr.append("or")
+ self.parseNode(node)
+ self.Depth -= 1
+ self.Expr.pop()
+ self.Rule += ")"
+
+ def parseNot(self, node):
+ self.parseNew(False)
+ self.Rule += "not ("
+ self.Depth += 1
+ self.Expr.append("or")
+ self.parseNode(node)
+ self.Depth -= 1
+ self.Expr.pop()
+ self.Rule += ")"
class MenuEntry:
- "Wrapper for 'Menu Style' Desktop Entries"
- def __init__(self, filename, dir="", prefix=""):
- # Create entry
- self.DesktopEntry = DesktopEntry(os.path.join(dir,filename))
- self.setAttributes(filename, dir, prefix)
-
- # Can be one of Deleted/Hidden/Empty/NotShowIn/NoExec or True
- self.Show = True
-
- # Semi-Private
- self.Original = None
- self.Parents = []
-
- # Private Stuff
- self.Allocated = False
- self.Add = False
- self.MatchedInclude = False
-
- # Caching
- self.Categories = self.DesktopEntry.getCategories()
-
- def save(self):
- if self.DesktopEntry.tainted == True:
- self.DesktopEntry.write()
-
- def getDir(self):
- return self.DesktopEntry.filename.replace(self.Filename, '')
-
- def getType(self):
- # Can be one of System/User/Both
- if xdg.Config.root_mode == False:
- if self.Original:
- return "Both"
- elif xdg_data_dirs[0] in self.DesktopEntry.filename:
- return "User"
- else:
- return "System"
- else:
- return "User"
-
- def setAttributes(self, filename, dir="", prefix=""):
- self.Filename = filename
- self.Prefix = prefix
- self.DesktopFileID = os.path.join(prefix,filename).replace("/", "-")
-
- if not os.path.isabs(self.DesktopEntry.filename):
- self.__setFilename()
-
- def updateAttributes(self):
- if self.getType() == "System":
- self.Original = MenuEntry(self.Filename, self.getDir(), self.Prefix)
- self.__setFilename()
-
- def __setFilename(self):
- if xdg.Config.root_mode == False:
- path = xdg_data_dirs[0]
- else:
- path= xdg_data_dirs[1]
-
- if self.DesktopEntry.getType() == "Application":
- dir = os.path.join(path, "applications")
- else:
- dir = os.path.join(path, "desktop-directories")
-
- self.DesktopEntry.filename = os.path.join(dir, self.Filename)
-
- def __cmp__(self, other):
- return locale.strcoll(self.DesktopEntry.getName(), other.DesktopEntry.getName())
-
- def __eq__(self,other):
- if self.DesktopFileID == str(other):
- return True
- else:
- return False
-
- def __repr__(self):
- return self.DesktopFileID
+ "Wrapper for 'Menu Style' Desktop Entries"
+ def __init__(self, filename, dir="", prefix=""):
+ # Create entry
+ self.DesktopEntry = DesktopEntry(os.path.join(dir,filename))
+ self.setAttributes(filename, dir, prefix)
+
+ # Can be one of Deleted/Hidden/Empty/NotShowIn/NoExec or True
+ self.Show = True
+
+ # Semi-Private
+ self.Original = None
+ self.Parents = []
+
+ # Private Stuff
+ self.Allocated = False
+ self.Add = False
+ self.MatchedInclude = False
+
+ # Caching
+ self.Categories = self.DesktopEntry.getCategories()
+
+ def save(self):
+ if self.DesktopEntry.tainted == True:
+ self.DesktopEntry.write()
+
+ def getDir(self):
+ return self.DesktopEntry.filename.replace(self.Filename, '')
+
+ def getType(self):
+ # Can be one of System/User/Both
+ if xdg.Config.root_mode == False:
+ if self.Original:
+ return "Both"
+ elif xdg_data_dirs[0] in self.DesktopEntry.filename:
+ return "User"
+ else:
+ return "System"
+ else:
+ return "User"
+
+ def setAttributes(self, filename, dir="", prefix=""):
+ self.Filename = filename
+ self.Prefix = prefix
+ self.DesktopFileID = os.path.join(prefix,filename).replace("/", "-")
+
+ if not os.path.isabs(self.DesktopEntry.filename):
+ self.__setFilename()
+
+ def updateAttributes(self):
+ if self.getType() == "System":
+ self.Original = MenuEntry(self.Filename, self.getDir(), self.Prefix)
+ self.__setFilename()
+
+ def __setFilename(self):
+ if xdg.Config.root_mode == False:
+ path = xdg_data_dirs[0]
+ else:
+ path= xdg_data_dirs[1]
+
+ if self.DesktopEntry.getType() == "Application":
+ dir = os.path.join(path, "applications")
+ else:
+ dir = os.path.join(path, "desktop-directories")
+
+ self.DesktopEntry.filename = os.path.join(dir, self.Filename)
+
+ def __cmp__(self, other):
+ return locale.strcoll(self.DesktopEntry.getName(), other.DesktopEntry.getName())
+
+ def __eq__(self,other):
+ if self.DesktopFileID == str(other):
+ return True
+ else:
+ return False
+
+ def __repr__(self):
+ return self.DesktopFileID
class Separator:
- "Just a dummy class for Separators"
- def __init__(self, parent):
- self.Parent = parent
- self.Show = True
+ "Just a dummy class for Separators"
+ def __init__(self, parent):
+ self.Parent = parent
+ self.Show = True
class Header:
- "Class for Inline Headers"
- def __init__(self, name, generic_name, comment):
- self.Name = name
- self.GenericName = generic_name
- self.Comment = comment
+ "Class for Inline Headers"
+ def __init__(self, name, generic_name, comment):
+ self.Name = name
+ self.GenericName = generic_name
+ self.Comment = comment
- def __str__(self):
- return self.Name
+ def __str__(self):
+ return self.Name
tmp = {}
def __getFileName(filename):
- dirs = xdg_config_dirs[:]
- if xdg.Config.root_mode == True:
- dirs.pop(0)
+ dirs = xdg_config_dirs[:]
+ if xdg.Config.root_mode == True:
+ dirs.pop(0)
- for dir in dirs:
- menuname = os.path.join (dir, "menus" , filename)
- if os.path.isdir(dir) and os.path.isfile(menuname):
- return menuname
+ for dir in dirs:
+ menuname = os.path.join (dir, "menus" , filename)
+ if os.path.isdir(dir) and os.path.isfile(menuname):
+ return menuname
def parse(filename=None):
- # conver to absolute path
- if filename and not os.path.isabs(filename):
- filename = __getFileName(filename)
+ # conver to absolute path
+ if filename and not os.path.isabs(filename):
+ filename = __getFileName(filename)
- # use default if no filename given
- if not filename:
- filename = __getFileName("applications.menu")
+ # use default if no filename given
+ if not filename:
+ filename = __getFileName("applications.menu")
- if not filename:
- raise ParsingError('File not found', "/etc/xdg/menus/applications.menu")
+ if not filename:
+ raise ParsingError('File not found', "/etc/xdg/menus/applications.menu")
- # check if it is a .menu file
- if not os.path.splitext(filename)[1] == ".menu":
- raise ParsingError('Not a .menu file', filename)
+ # check if it is a .menu file
+ if not os.path.splitext(filename)[1] == ".menu":
+ raise ParsingError('Not a .menu file', filename)
- # create xml parser
- try:
- doc = xml.dom.minidom.parse(filename)
- except xml.parsers.expat.ExpatError:
- raise ParsingError('Not a valid .menu file', filename)
+ # create xml parser
+ try:
+ doc = xml.dom.minidom.parse(filename)
+ except xml.parsers.expat.ExpatError:
+ raise ParsingError('Not a valid .menu file', filename)
- # parse menufile
- tmp["Root"] = ""
- tmp["mergeFiles"] = []
- tmp["DirectoryDirs"] = []
- tmp["cache"] = MenuEntryCache()
+ # parse menufile
+ tmp["Root"] = ""
+ tmp["mergeFiles"] = []
+ tmp["DirectoryDirs"] = []
+ tmp["cache"] = MenuEntryCache()
- __parse(doc, filename, tmp["Root"])
- __parsemove(tmp["Root"])
- __postparse(tmp["Root"])
+ __parse(doc, filename, tmp["Root"])
+ __parsemove(tmp["Root"])
+ __postparse(tmp["Root"])
- tmp["Root"].Doc = doc
- tmp["Root"].Filename = filename
+ tmp["Root"].Doc = doc
+ tmp["Root"].Filename = filename
- # generate the menu
- __genmenuNotOnlyAllocated(tmp["Root"])
- __genmenuOnlyAllocated(tmp["Root"])
+ # generate the menu
+ __genmenuNotOnlyAllocated(tmp["Root"])
+ __genmenuOnlyAllocated(tmp["Root"])
- # and finally sort
- sort(tmp["Root"])
+ # and finally sort
+ sort(tmp["Root"])
- return tmp["Root"]
+ return tmp["Root"]
def __parse(node, filename, parent=None):
- for child in node.childNodes:
- if child.nodeType == ELEMENT_NODE:
- if child.tagName == 'Menu':
- __parseMenu(child, filename, parent)
- elif child.tagName == 'AppDir':
- try:
- __parseAppDir(child.childNodes[0].nodeValue, filename, parent)
- except IndexError:
- raise ValidationError('AppDir cannot be empty', filename)
- elif child.tagName == 'DefaultAppDirs':
- __parseDefaultAppDir(filename, parent)
- elif child.tagName == 'DirectoryDir':
- try:
- __parseDirectoryDir(child.childNodes[0].nodeValue, filename, parent)
- except IndexError:
- raise ValidationError('DirectoryDir cannot be empty', filename)
- elif child.tagName == 'DefaultDirectoryDirs':
- __parseDefaultDirectoryDir(filename, parent)
- elif child.tagName == 'Name' :
- try:
- parent.Name = child.childNodes[0].nodeValue
- except IndexError:
- raise ValidationError('Name cannot be empty', filename)
- elif child.tagName == 'Directory' :
- try:
- parent.Directories.append(child.childNodes[0].nodeValue)
- except IndexError:
- raise ValidationError('Directory cannot be empty', filename)
- elif child.tagName == 'OnlyUnallocated':
- parent.OnlyUnallocated = True
- elif child.tagName == 'NotOnlyUnallocated':
- parent.OnlyUnallocated = False
- elif child.tagName == 'Deleted':
- parent.Deleted = True
- elif child.tagName == 'NotDeleted':
- parent.Deleted = False
- elif child.tagName == 'Include' or child.tagName == 'Exclude':
- parent.Rules.append(Rule(child.tagName, child))
- elif child.tagName == 'MergeFile':
- try:
- if child.getAttribute("type") == "parent":
- __parseMergeFile("applications.menu", child, filename, parent)
- else:
- __parseMergeFile(child.childNodes[0].nodeValue, child, filename, parent)
- except IndexError:
- raise ValidationError('MergeFile cannot be empty', filename)
- elif child.tagName == 'MergeDir':
- try:
- __parseMergeDir(child.childNodes[0].nodeValue, child, filename, parent)
- except IndexError:
- raise ValidationError('MergeDir cannot be empty', filename)
- elif child.tagName == 'DefaultMergeDirs':
- __parseDefaultMergeDirs(child, filename, parent)
- elif child.tagName == 'Move':
- parent.Moves.append(Move(child))
- elif child.tagName == 'Layout':
- if len(child.childNodes) > 1:
- parent.Layout = Layout(child)
- elif child.tagName == 'DefaultLayout':
- if len(child.childNodes) > 1:
- parent.DefaultLayout = Layout(child)
- elif child.tagName == 'LegacyDir':
- try:
- __parseLegacyDir(child.childNodes[0].nodeValue, child.getAttribute("prefix"), filename, parent)
- except IndexError:
- raise ValidationError('LegacyDir cannot be empty', filename)
- elif child.tagName == 'KDELegacyDirs':
- __parseKDELegacyDirs(filename, parent)
+ for child in node.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ if child.tagName == 'Menu':
+ __parseMenu(child, filename, parent)
+ elif child.tagName == 'AppDir':
+ try:
+ __parseAppDir(child.childNodes[0].nodeValue, filename, parent)
+ except IndexError:
+ raise ValidationError('AppDir cannot be empty', filename)
+ elif child.tagName == 'DefaultAppDirs':
+ __parseDefaultAppDir(filename, parent)
+ elif child.tagName == 'DirectoryDir':
+ try:
+ __parseDirectoryDir(child.childNodes[0].nodeValue, filename, parent)
+ except IndexError:
+ raise ValidationError('DirectoryDir cannot be empty', filename)
+ elif child.tagName == 'DefaultDirectoryDirs':
+ __parseDefaultDirectoryDir(filename, parent)
+ elif child.tagName == 'Name' :
+ try:
+ parent.Name = child.childNodes[0].nodeValue
+ except IndexError:
+ raise ValidationError('Name cannot be empty', filename)
+ elif child.tagName == 'Directory' :
+ try:
+ parent.Directories.append(child.childNodes[0].nodeValue)
+ except IndexError:
+ raise ValidationError('Directory cannot be empty', filename)
+ elif child.tagName == 'OnlyUnallocated':
+ parent.OnlyUnallocated = True
+ elif child.tagName == 'NotOnlyUnallocated':
+ parent.OnlyUnallocated = False
+ elif child.tagName == 'Deleted':
+ parent.Deleted = True
+ elif child.tagName == 'NotDeleted':
+ parent.Deleted = False
+ elif child.tagName == 'Include' or child.tagName == 'Exclude':
+ parent.Rules.append(Rule(child.tagName, child))
+ elif child.tagName == 'MergeFile':
+ try:
+ if child.getAttribute("type") == "parent":
+ __parseMergeFile("applications.menu", child, filename, parent)
+ else:
+ __parseMergeFile(child.childNodes[0].nodeValue, child, filename, parent)
+ except IndexError:
+ raise ValidationError('MergeFile cannot be empty', filename)
+ elif child.tagName == 'MergeDir':
+ try:
+ __parseMergeDir(child.childNodes[0].nodeValue, child, filename, parent)
+ except IndexError:
+ raise ValidationError('MergeDir cannot be empty', filename)
+ elif child.tagName == 'DefaultMergeDirs':
+ __parseDefaultMergeDirs(child, filename, parent)
+ elif child.tagName == 'Move':
+ parent.Moves.append(Move(child))
+ elif child.tagName == 'Layout':
+ if len(child.childNodes) > 1:
+ parent.Layout = Layout(child)
+ elif child.tagName == 'DefaultLayout':
+ if len(child.childNodes) > 1:
+ parent.DefaultLayout = Layout(child)
+ elif child.tagName == 'LegacyDir':
+ try:
+ __parseLegacyDir(child.childNodes[0].nodeValue, child.getAttribute("prefix"), filename, parent)
+ except IndexError:
+ raise ValidationError('LegacyDir cannot be empty', filename)
+ elif child.tagName == 'KDELegacyDirs':
+ __parseKDELegacyDirs(filename, parent)
def __parsemove(menu):
- for submenu in menu.Submenus:
- __parsemove(submenu)
-
- # parse move operations
- for move in menu.Moves:
- move_from_menu = menu.getMenu(move.Old)
- if move_from_menu:
- move_to_menu = menu.getMenu(move.New)
-
- menus = move.New.split("/")
- oldparent = None
- while len(menus) > 0:
- if not oldparent:
- oldparent = menu
- newmenu = oldparent.getMenu(menus[0])
- if not newmenu:
- newmenu = Menu()
- newmenu.Name = menus[0]
- if len(menus) > 1:
- newmenu.NotInXml = True
- oldparent.addSubmenu(newmenu)
- oldparent = newmenu
- menus.pop(0)
-
- newmenu += move_from_menu
- move_from_menu.Parent.Submenus.remove(move_from_menu)
+ for submenu in menu.Submenus:
+ __parsemove(submenu)
+
+ # parse move operations
+ for move in menu.Moves:
+ move_from_menu = menu.getMenu(move.Old)
+ if move_from_menu:
+ move_to_menu = menu.getMenu(move.New)
+
+ menus = move.New.split("/")
+ oldparent = None
+ while len(menus) > 0:
+ if not oldparent:
+ oldparent = menu
+ newmenu = oldparent.getMenu(menus[0])
+ if not newmenu:
+ newmenu = Menu()
+ newmenu.Name = menus[0]
+ if len(menus) > 1:
+ newmenu.NotInXml = True
+ oldparent.addSubmenu(newmenu)
+ oldparent = newmenu
+ menus.pop(0)
+
+ newmenu += move_from_menu
+ move_from_menu.Parent.Submenus.remove(move_from_menu)
def __postparse(menu):
- # unallocated / deleted
- if menu.Deleted == "notset":
- menu.Deleted = False
- if menu.OnlyUnallocated == "notset":
- menu.OnlyUnallocated = False
-
- # Layout Tags
- if not menu.Layout or not menu.DefaultLayout:
- if menu.DefaultLayout:
- menu.Layout = menu.DefaultLayout
- elif menu.Layout:
- if menu.Depth > 0:
- menu.DefaultLayout = menu.Parent.DefaultLayout
- else:
- menu.DefaultLayout = Layout()
- else:
- if menu.Depth > 0:
- menu.Layout = menu.Parent.DefaultLayout
- menu.DefaultLayout = menu.Parent.DefaultLayout
- else:
- menu.Layout = Layout()
- menu.DefaultLayout = Layout()
-
- # add parent's app/directory dirs
- if menu.Depth > 0:
- menu.AppDirs = menu.Parent.AppDirs + menu.AppDirs
- menu.DirectoryDirs = menu.Parent.DirectoryDirs + menu.DirectoryDirs
-
- # remove duplicates
- menu.Directories = __removeDuplicates(menu.Directories)
- menu.DirectoryDirs = __removeDuplicates(menu.DirectoryDirs)
- menu.AppDirs = __removeDuplicates(menu.AppDirs)
-
- # go recursive through all menus
- for submenu in menu.Submenus:
- __postparse(submenu)
-
- # reverse so handling is easier
- menu.Directories.reverse()
- menu.DirectoryDirs.reverse()
- menu.AppDirs.reverse()
-
- # get the valid .directory file out of the list
- for directory in menu.Directories:
- for dir in menu.DirectoryDirs:
- if os.path.isfile(os.path.join(dir, directory)):
- menuentry = MenuEntry(directory, dir)
- if not menu.Directory:
- menu.Directory = menuentry
- elif menuentry.getType() == "System":
- if menu.Directory.getType() == "User":
- menu.Directory.Original = menuentry
- if menu.Directory:
- break
+ # unallocated / deleted
+ if menu.Deleted == "notset":
+ menu.Deleted = False
+ if menu.OnlyUnallocated == "notset":
+ menu.OnlyUnallocated = False
+
+ # Layout Tags
+ if not menu.Layout or not menu.DefaultLayout:
+ if menu.DefaultLayout:
+ menu.Layout = menu.DefaultLayout
+ elif menu.Layout:
+ if menu.Depth > 0:
+ menu.DefaultLayout = menu.Parent.DefaultLayout
+ else:
+ menu.DefaultLayout = Layout()
+ else:
+ if menu.Depth > 0:
+ menu.Layout = menu.Parent.DefaultLayout
+ menu.DefaultLayout = menu.Parent.DefaultLayout
+ else:
+ menu.Layout = Layout()
+ menu.DefaultLayout = Layout()
+
+ # add parent's app/directory dirs
+ if menu.Depth > 0:
+ menu.AppDirs = menu.Parent.AppDirs + menu.AppDirs
+ menu.DirectoryDirs = menu.Parent.DirectoryDirs + menu.DirectoryDirs
+
+ # remove duplicates
+ menu.Directories = __removeDuplicates(menu.Directories)
+ menu.DirectoryDirs = __removeDuplicates(menu.DirectoryDirs)
+ menu.AppDirs = __removeDuplicates(menu.AppDirs)
+
+ # go recursive through all menus
+ for submenu in menu.Submenus:
+ __postparse(submenu)
+
+ # reverse so handling is easier
+ menu.Directories.reverse()
+ menu.DirectoryDirs.reverse()
+ menu.AppDirs.reverse()
+
+ # get the valid .directory file out of the list
+ for directory in menu.Directories:
+ for dir in menu.DirectoryDirs:
+ if os.path.isfile(os.path.join(dir, directory)):
+ menuentry = MenuEntry(directory, dir)
+ if not menu.Directory:
+ menu.Directory = menuentry
+ elif menuentry.getType() == "System":
+ if menu.Directory.getType() == "User":
+ menu.Directory.Original = menuentry
+ if menu.Directory:
+ break
# Menu parsing stuff
def __parseMenu(child, filename, parent):
- m = Menu()
- __parse(child, filename, m)
- if parent:
- parent.addSubmenu(m)
- else:
- tmp["Root"] = m
+ m = Menu()
+ __parse(child, filename, m)
+ if parent:
+ parent.addSubmenu(m)
+ else:
+ tmp["Root"] = m
# helper function
def __check(value, filename, type):
- path = os.path.dirname(filename)
+ path = os.path.dirname(filename)
- if not os.path.isabs(value):
- value = os.path.join(path, value)
+ if not os.path.isabs(value):
+ value = os.path.join(path, value)
- value = os.path.abspath(value)
+ value = os.path.abspath(value)
- if type == "dir" and os.path.exists(value) and os.path.isdir(value):
- return value
- elif type == "file" and os.path.exists(value) and os.path.isfile(value):
- return value
- else:
- return False
+ if type == "dir" and os.path.exists(value) and os.path.isdir(value):
+ return value
+ elif type == "file" and os.path.exists(value) and os.path.isfile(value):
+ return value
+ else:
+ return False
# App/Directory Dir Stuff
def __parseAppDir(value, filename, parent):
- value = __check(value, filename, "dir")
- if value:
- parent.AppDirs.append(value)
+ value = __check(value, filename, "dir")
+ if value:
+ parent.AppDirs.append(value)
def __parseDefaultAppDir(filename, parent):
- for dir in reversed(xdg_data_dirs):
- __parseAppDir(os.path.join(dir, "applications"), filename, parent)
+ for dir in reversed(xdg_data_dirs):
+ __parseAppDir(os.path.join(dir, "applications"), filename, parent)
def __parseDirectoryDir(value, filename, parent):
- value = __check(value, filename, "dir")
- if value:
- parent.DirectoryDirs.append(value)
+ value = __check(value, filename, "dir")
+ if value:
+ parent.DirectoryDirs.append(value)
def __parseDefaultDirectoryDir(filename, parent):
- for dir in reversed(xdg_data_dirs):
- __parseDirectoryDir(os.path.join(dir, "desktop-directories"), filename, parent)
+ for dir in reversed(xdg_data_dirs):
+ __parseDirectoryDir(os.path.join(dir, "desktop-directories"), filename, parent)
# Merge Stuff
def __parseMergeFile(value, child, filename, parent):
- if child.getAttribute("type") == "parent":
- for dir in xdg_config_dirs:
- rel_file = filename.replace(dir, "").strip("/")
- if rel_file != filename:
- for p in xdg_config_dirs:
- if dir == p:
- continue
- if os.path.isfile(os.path.join(p,rel_file)):
- __mergeFile(os.path.join(p,rel_file),child,parent)
- break
- else:
- value = __check(value, filename, "file")
- if value:
- __mergeFile(value, child, parent)
+ if child.getAttribute("type") == "parent":
+ for dir in xdg_config_dirs:
+ rel_file = filename.replace(dir, "").strip("/")
+ if rel_file != filename:
+ for p in xdg_config_dirs:
+ if dir == p:
+ continue
+ if os.path.isfile(os.path.join(p,rel_file)):
+ __mergeFile(os.path.join(p,rel_file),child,parent)
+ break
+ else:
+ value = __check(value, filename, "file")
+ if value:
+ __mergeFile(value, child, parent)
def __parseMergeDir(value, child, filename, parent):
- value = __check(value, filename, "dir")
- if value:
- for item in os.listdir(value):
- try:
- if os.path.splitext(item)[1] == ".menu":
- __mergeFile(os.path.join(value, item), child, parent)
- except UnicodeDecodeError:
- continue
+ value = __check(value, filename, "dir")
+ if value:
+ for item in os.listdir(value):
+ try:
+ if os.path.splitext(item)[1] == ".menu":
+ __mergeFile(os.path.join(value, item), child, parent)
+ except UnicodeDecodeError:
+ continue
def __parseDefaultMergeDirs(child, filename, parent):
- basename = os.path.splitext(os.path.basename(filename))[0]
- for dir in reversed(xdg_config_dirs):
- __parseMergeDir(os.path.join(dir, "menus", basename + "-merged"), child, filename, parent)
+ basename = os.path.splitext(os.path.basename(filename))[0]
+ for dir in reversed(xdg_config_dirs):
+ __parseMergeDir(os.path.join(dir, "menus", basename + "-merged"), child, filename, parent)
def __mergeFile(filename, child, parent):
- # check for infinite loops
- if filename in tmp["mergeFiles"]:
- if debug:
- raise ParsingError('Infinite MergeFile loop detected', filename)
- else:
- return
-
- tmp["mergeFiles"].append(filename)
-
- # load file
- try:
- doc = xml.dom.minidom.parse(filename)
- except IOError:
- if debug:
- raise ParsingError('File not found', filename)
- else:
- return
- except xml.parsers.expat.ExpatError:
- if debug:
- raise ParsingError('Not a valid .menu file', filename)
- else:
- return
-
- # append file
- for child in doc.childNodes:
- if child.nodeType == ELEMENT_NODE:
- __parse(child,filename,parent)
- break
+ # check for infinite loops
+ if filename in tmp["mergeFiles"]:
+ if debug:
+ raise ParsingError('Infinite MergeFile loop detected', filename)
+ else:
+ return
+
+ tmp["mergeFiles"].append(filename)
+
+ # load file
+ try:
+ doc = xml.dom.minidom.parse(filename)
+ except IOError:
+ if debug:
+ raise ParsingError('File not found', filename)
+ else:
+ return
+ except xml.parsers.expat.ExpatError:
+ if debug:
+ raise ParsingError('Not a valid .menu file', filename)
+ else:
+ return
+
+ # append file
+ for child in doc.childNodes:
+ if child.nodeType == ELEMENT_NODE:
+ __parse(child,filename,parent)
+ break
# Legacy Dir Stuff
def __parseLegacyDir(dir, prefix, filename, parent):
- m = __mergeLegacyDir(dir,prefix,filename,parent)
- if m:
- parent += m
+ m = __mergeLegacyDir(dir,prefix,filename,parent)
+ if m:
+ parent += m
def __mergeLegacyDir(dir, prefix, filename, parent):
- dir = __check(dir,filename,"dir")
- if dir and dir not in tmp["DirectoryDirs"]:
- tmp["DirectoryDirs"].append(dir)
-
- m = Menu()
- m.AppDirs.append(dir)
- m.DirectoryDirs.append(dir)
- m.Name = os.path.basename(dir)
- m.NotInXml = True
-
- for item in os.listdir(dir):
- try:
- if item == ".directory":
- m.Directories.append(item)
- elif os.path.isdir(os.path.join(dir,item)):
- m.addSubmenu(__mergeLegacyDir(os.path.join(dir,item), prefix, filename, parent))
- except UnicodeDecodeError:
- continue
-
- tmp["cache"].addMenuEntries([dir],prefix, True)
- menuentries = tmp["cache"].getMenuEntries([dir], False)
-
- for menuentry in menuentries:
- categories = menuentry.Categories
- if len(categories) == 0:
- r = Rule("Include")
- r.parseFilename(menuentry.DesktopFileID)
- r.compile()
- m.Rules.append(r)
- if not dir in parent.AppDirs:
- categories.append("Legacy")
- menuentry.Categories = categories
-
- return m
+ dir = __check(dir,filename,"dir")
+ if dir and dir not in tmp["DirectoryDirs"]:
+ tmp["DirectoryDirs"].append(dir)
+
+ m = Menu()
+ m.AppDirs.append(dir)
+ m.DirectoryDirs.append(dir)
+ m.Name = os.path.basename(dir)
+ m.NotInXml = True
+
+ for item in os.listdir(dir):
+ try:
+ if item == ".directory":
+ m.Directories.append(item)
+ elif os.path.isdir(os.path.join(dir,item)):
+ m.addSubmenu(__mergeLegacyDir(os.path.join(dir,item), prefix, filename, parent))
+ except UnicodeDecodeError:
+ continue
+
+ tmp["cache"].addMenuEntries([dir],prefix, True)
+ menuentries = tmp["cache"].getMenuEntries([dir], False)
+
+ for menuentry in menuentries:
+ categories = menuentry.Categories
+ if len(categories) == 0:
+ r = Rule("Include")
+ r.parseFilename(menuentry.DesktopFileID)
+ r.compile()
+ m.Rules.append(r)
+ if not dir in parent.AppDirs:
+ categories.append("Legacy")
+ menuentry.Categories = categories
+
+ return m
def __parseKDELegacyDirs(filename, parent):
- f=os.popen3("kde-config --path apps")
- output = f[1].readlines()
- try:
- for dir in output[0].split(":"):
- __parseLegacyDir(dir,"kde", filename, parent)
- except IndexError:
- pass
+ f=os.popen3("kde-config --path apps")
+ output = f[1].readlines()
+ try:
+ for dir in output[0].split(":"):
+ __parseLegacyDir(dir,"kde", filename, parent)
+ except IndexError:
+ pass
# remove duplicate entries from a list
def __removeDuplicates(list):
- set = {}
- list.reverse()
- list = [set.setdefault(e,e) for e in list if e not in set]
- list.reverse()
- return list
+ set = {}
+ list.reverse()
+ list = [set.setdefault(e,e) for e in list if e not in set]
+ list.reverse()
+ return list
# Finally generate the menu
def __genmenuNotOnlyAllocated(menu):
- for submenu in menu.Submenus:
- __genmenuNotOnlyAllocated(submenu)
-
- if menu.OnlyUnallocated == False:
- tmp["cache"].addMenuEntries(menu.AppDirs)
- menuentries = []
- for rule in menu.Rules:
- menuentries = rule.do(tmp["cache"].getMenuEntries(menu.AppDirs), rule.Type, 1)
- for menuentry in menuentries:
- if menuentry.Add == True:
- menuentry.Parents.append(menu)
- menuentry.Add = False
- menuentry.Allocated = True
- menu.MenuEntries.append(menuentry)
+ for submenu in menu.Submenus:
+ __genmenuNotOnlyAllocated(submenu)
+
+ if menu.OnlyUnallocated == False:
+ tmp["cache"].addMenuEntries(menu.AppDirs)
+ menuentries = []
+ for rule in menu.Rules:
+ menuentries = rule.do(tmp["cache"].getMenuEntries(menu.AppDirs), rule.Type, 1)
+ for menuentry in menuentries:
+ if menuentry.Add == True:
+ menuentry.Parents.append(menu)
+ menuentry.Add = False
+ menuentry.Allocated = True
+ menu.MenuEntries.append(menuentry)
def __genmenuOnlyAllocated(menu):
- for submenu in menu.Submenus:
- __genmenuOnlyAllocated(submenu)
-
- if menu.OnlyUnallocated == True:
- tmp["cache"].addMenuEntries(menu.AppDirs)
- menuentries = []
- for rule in menu.Rules:
- menuentries = rule.do(tmp["cache"].getMenuEntries(menu.AppDirs), rule.Type, 2)
- for menuentry in menuentries:
- if menuentry.Add == True:
- menuentry.Parents.append(menu)
- # menuentry.Add = False
- # menuentry.Allocated = True
- menu.MenuEntries.append(menuentry)
+ for submenu in menu.Submenus:
+ __genmenuOnlyAllocated(submenu)
+
+ if menu.OnlyUnallocated == True:
+ tmp["cache"].addMenuEntries(menu.AppDirs)
+ menuentries = []
+ for rule in menu.Rules:
+ menuentries = rule.do(tmp["cache"].getMenuEntries(menu.AppDirs), rule.Type, 2)
+ for menuentry in menuentries:
+ if menuentry.Add == True:
+ menuentry.Parents.append(menu)
+ # menuentry.Add = False
+ # menuentry.Allocated = True
+ menu.MenuEntries.append(menuentry)
# And sorting ...
def sort(menu):
- menu.Entries = []
- menu.Visible = 0
-
- for submenu in menu.Submenus:
- sort(submenu)
-
- tmp_s = []
- tmp_e = []
-
- for order in menu.Layout.order:
- if order[0] == "Filename":
- tmp_e.append(order[1])
- elif order[0] == "Menuname":
- tmp_s.append(order[1])
-
- for order in menu.Layout.order:
- if order[0] == "Separator":
- separator = Separator(menu)
- if len(menu.Entries) > 0 and isinstance(menu.Entries[-1], Separator):
- separator.Show = False
- menu.Entries.append(separator)
- elif order[0] == "Filename":
- menuentry = menu.getMenuEntry(order[1])
- if menuentry:
- menu.Entries.append(menuentry)
- elif order[0] == "Menuname":
- submenu = menu.getMenu(order[1])
- if submenu:
- __parse_inline(submenu, menu)
- elif order[0] == "Merge":
- if order[1] == "files" or order[1] == "all":
- menu.MenuEntries.sort()
- for menuentry in menu.MenuEntries:
- if menuentry not in tmp_e:
- menu.Entries.append(menuentry)
- elif order[1] == "menus" or order[1] == "all":
- menu.Submenus.sort()
- for submenu in menu.Submenus:
- if submenu.Name not in tmp_s:
- __parse_inline(submenu, menu)
-
- # getHidden / NoDisplay / OnlyShowIn / NotOnlyShowIn / Deleted / NoExec
- for entry in menu.Entries:
- entry.Show = True
- menu.Visible += 1
- if isinstance(entry, Menu):
- if entry.Deleted == True:
- entry.Show = "Deleted"
- menu.Visible -= 1
- elif isinstance(entry.Directory, MenuEntry):
- if entry.Directory.DesktopEntry.getNoDisplay() == True:
- entry.Show = "NoDisplay"
- menu.Visible -= 1
- elif entry.Directory.DesktopEntry.getHidden() == True:
- entry.Show = "Hidden"
- menu.Visible -= 1
- elif isinstance(entry, MenuEntry):
- if entry.DesktopEntry.getNoDisplay() == True:
- entry.Show = "NoDisplay"
- menu.Visible -= 1
- elif entry.DesktopEntry.getHidden() == True:
- entry.Show = "Hidden"
- menu.Visible -= 1
- elif entry.DesktopEntry.getTryExec() and not __try_exec(entry.DesktopEntry.getTryExec()):
- entry.Show = "NoExec"
- menu.Visible -= 1
- elif xdg.Config.windowmanager:
- if ( entry.DesktopEntry.getOnlyShowIn() != [] and xdg.Config.windowmanager not in entry.DesktopEntry.getOnlyShowIn() ) \
- or xdg.Config.windowmanager in entry.DesktopEntry.getNotShowIn():
- entry.Show = "NotShowIn"
- menu.Visible -= 1
- elif isinstance(entry,Separator):
- menu.Visible -= 1
-
- # remove separators at the beginning and at the end
- if len(menu.Entries) > 0:
- if isinstance(menu.Entries[0], Separator):
- menu.Entries[0].Show = False
- if len(menu.Entries) > 1:
- if isinstance(menu.Entries[-1], Separator):
- menu.Entries[-1].Show = False
-
- # show_empty tag
- for entry in menu.Entries:
- if isinstance(entry,Menu) and entry.Layout.show_empty == "false" and entry.Visible == 0:
- entry.Show = "Empty"
- menu.Visible -= 1
- if entry.NotInXml == True:
- menu.Entries.remove(entry)
+ menu.Entries = []
+ menu.Visible = 0
+
+ for submenu in menu.Submenus:
+ sort(submenu)
+
+ tmp_s = []
+ tmp_e = []
+
+ for order in menu.Layout.order:
+ if order[0] == "Filename":
+ tmp_e.append(order[1])
+ elif order[0] == "Menuname":
+ tmp_s.append(order[1])
+
+ for order in menu.Layout.order:
+ if order[0] == "Separator":
+ separator = Separator(menu)
+ if len(menu.Entries) > 0 and isinstance(menu.Entries[-1], Separator):
+ separator.Show = False
+ menu.Entries.append(separator)
+ elif order[0] == "Filename":
+ menuentry = menu.getMenuEntry(order[1])
+ if menuentry:
+ menu.Entries.append(menuentry)
+ elif order[0] == "Menuname":
+ submenu = menu.getMenu(order[1])
+ if submenu:
+ __parse_inline(submenu, menu)
+ elif order[0] == "Merge":
+ if order[1] == "files" or order[1] == "all":
+ menu.MenuEntries.sort()
+ for menuentry in menu.MenuEntries:
+ if menuentry not in tmp_e:
+ menu.Entries.append(menuentry)
+ elif order[1] == "menus" or order[1] == "all":
+ menu.Submenus.sort()
+ for submenu in menu.Submenus:
+ if submenu.Name not in tmp_s:
+ __parse_inline(submenu, menu)
+
+ # getHidden / NoDisplay / OnlyShowIn / NotOnlyShowIn / Deleted / NoExec
+ for entry in menu.Entries:
+ entry.Show = True
+ menu.Visible += 1
+ if isinstance(entry, Menu):
+ if entry.Deleted == True:
+ entry.Show = "Deleted"
+ menu.Visible -= 1
+ elif isinstance(entry.Directory, MenuEntry):
+ if entry.Directory.DesktopEntry.getNoDisplay() == True:
+ entry.Show = "NoDisplay"
+ menu.Visible -= 1
+ elif entry.Directory.DesktopEntry.getHidden() == True:
+ entry.Show = "Hidden"
+ menu.Visible -= 1
+ elif isinstance(entry, MenuEntry):
+ if entry.DesktopEntry.getNoDisplay() == True:
+ entry.Show = "NoDisplay"
+ menu.Visible -= 1
+ elif entry.DesktopEntry.getHidden() == True:
+ entry.Show = "Hidden"
+ menu.Visible -= 1
+ elif entry.DesktopEntry.getTryExec() and not __try_exec(entry.DesktopEntry.getTryExec()):
+ entry.Show = "NoExec"
+ menu.Visible -= 1
+ elif xdg.Config.windowmanager:
+ if ( entry.DesktopEntry.getOnlyShowIn() != [] and xdg.Config.windowmanager not in entry.DesktopEntry.getOnlyShowIn() ) \
+ or xdg.Config.windowmanager in entry.DesktopEntry.getNotShowIn():
+ entry.Show = "NotShowIn"
+ menu.Visible -= 1
+ elif isinstance(entry,Separator):
+ menu.Visible -= 1
+
+ # remove separators at the beginning and at the end
+ if len(menu.Entries) > 0:
+ if isinstance(menu.Entries[0], Separator):
+ menu.Entries[0].Show = False
+ if len(menu.Entries) > 1:
+ if isinstance(menu.Entries[-1], Separator):
+ menu.Entries[-1].Show = False
+
+ # show_empty tag
+ for entry in menu.Entries:
+ if isinstance(entry,Menu) and entry.Layout.show_empty == "false" and entry.Visible == 0:
+ entry.Show = "Empty"
+ menu.Visible -= 1
+ if entry.NotInXml == True:
+ menu.Entries.remove(entry)
def __try_exec(executable):
- paths = os.environ['PATH'].split(os.pathsep)
- if not os.path.isfile(executable):
- for p in paths:
- f = os.path.join(p, executable)
- if os.path.isfile(f):
- if os.access(f, os.X_OK):
- return True
- else:
- if os.access(executable, os.X_OK):
- return True
- return False
+ paths = os.environ['PATH'].split(os.pathsep)
+ if not os.path.isfile(executable):
+ for p in paths:
+ f = os.path.join(p, executable)
+ if os.path.isfile(f):
+ if os.access(f, os.X_OK):
+ return True
+ else:
+ if os.access(executable, os.X_OK):
+ return True
+ return False
# inline tags
def __parse_inline(submenu, menu):
- if submenu.Layout.inline == "true":
- if len(submenu.Entries) == 1 and submenu.Layout.inline_alias == "true":
- menuentry = submenu.Entries[0]
- menuentry.DesktopEntry.set("Name", submenu.getName(), locale = True)
- menuentry.DesktopEntry.set("GenericName", submenu.getGenericName(), locale = True)
- menuentry.DesktopEntry.set("Comment", submenu.getComment(), locale = True)
- menu.Entries.append(menuentry)
- elif len(submenu.Entries) <= submenu.Layout.inline_limit or submenu.Layout.inline_limit == 0:
- if submenu.Layout.inline_header == "true":
- header = Header(submenu.getName(), submenu.getGenericName(), submenu.getComment())
- menu.Entries.append(header)
- for entry in submenu.Entries:
- menu.Entries.append(entry)
- else:
- menu.Entries.append(submenu)
- else:
- menu.Entries.append(submenu)
+ if submenu.Layout.inline == "true":
+ if len(submenu.Entries) == 1 and submenu.Layout.inline_alias == "true":
+ menuentry = submenu.Entries[0]
+ menuentry.DesktopEntry.set("Name", submenu.getName(), locale = True)
+ menuentry.DesktopEntry.set("GenericName", submenu.getGenericName(), locale = True)
+ menuentry.DesktopEntry.set("Comment", submenu.getComment(), locale = True)
+ menu.Entries.append(menuentry)
+ elif len(submenu.Entries) <= submenu.Layout.inline_limit or submenu.Layout.inline_limit == 0:
+ if submenu.Layout.inline_header == "true":
+ header = Header(submenu.getName(), submenu.getGenericName(), submenu.getComment())
+ menu.Entries.append(header)
+ for entry in submenu.Entries:
+ menu.Entries.append(entry)
+ else:
+ menu.Entries.append(submenu)
+ else:
+ menu.Entries.append(submenu)
class MenuEntryCache:
- "Class to cache Desktop Entries"
- def __init__(self):
- self.cacheEntries = {}
- self.cacheEntries['legacy'] = []
- self.cache = {}
-
- def addMenuEntries(self, dirs, prefix="", legacy=False):
- for dir in dirs:
- if not self.cacheEntries.has_key(dir):
- self.cacheEntries[dir] = []
- self.__addFiles(dir, "", prefix, legacy)
-
- def __addFiles(self, dir, subdir, prefix, legacy):
- for item in os.listdir(os.path.join(dir,subdir)):
- if os.path.splitext(item)[1] == ".desktop":
- try:
- menuentry = MenuEntry(os.path.join(subdir,item), dir, prefix)
- except ParsingError:
- continue
-
- self.cacheEntries[dir].append(menuentry)
- if legacy == True:
- self.cacheEntries['legacy'].append(menuentry)
- elif os.path.isdir(os.path.join(dir,subdir,item)) and legacy == False:
- self.__addFiles(dir, os.path.join(subdir,item), prefix, legacy)
-
- def getMenuEntries(self, dirs, legacy=True):
- list = []
- ids = []
- # handle legacy items
- appdirs = dirs[:]
- if legacy == True:
- appdirs.append("legacy")
- # cache the results again
- key = "".join(appdirs)
- try:
- return self.cache[key]
- except KeyError:
- pass
- for dir in appdirs:
- for menuentry in self.cacheEntries[dir]:
- try:
- if menuentry.DesktopFileID not in ids:
- ids.append(menuentry.DesktopFileID)
- list.append(menuentry)
- elif menuentry.getType() == "System":
- # FIXME: This is only 99% correct, but still...
- i = list.index(menuentry)
- e = list[i]
- if e.getType() == "User":
- e.Original = menuentry
- except UnicodeDecodeError:
- continue
- self.cache[key] = list
- return list
+ "Class to cache Desktop Entries"
+ def __init__(self):
+ self.cacheEntries = {}
+ self.cacheEntries['legacy'] = []
+ self.cache = {}
+
+ def addMenuEntries(self, dirs, prefix="", legacy=False):
+ for dir in dirs:
+ if not self.cacheEntries.has_key(dir):
+ self.cacheEntries[dir] = []
+ self.__addFiles(dir, "", prefix, legacy)
+
+ def __addFiles(self, dir, subdir, prefix, legacy):
+ for item in os.listdir(os.path.join(dir,subdir)):
+ if os.path.splitext(item)[1] == ".desktop":
+ try:
+ menuentry = MenuEntry(os.path.join(subdir,item), dir, prefix)
+ except ParsingError:
+ continue
+
+ self.cacheEntries[dir].append(menuentry)
+ if legacy == True:
+ self.cacheEntries['legacy'].append(menuentry)
+ elif os.path.isdir(os.path.join(dir,subdir,item)) and legacy == False:
+ self.__addFiles(dir, os.path.join(subdir,item), prefix, legacy)
+
+ def getMenuEntries(self, dirs, legacy=True):
+ list = []
+ ids = []
+ # handle legacy items
+ appdirs = dirs[:]
+ if legacy == True:
+ appdirs.append("legacy")
+ # cache the results again
+ key = "".join(appdirs)
+ try:
+ return self.cache[key]
+ except KeyError:
+ pass
+ for dir in appdirs:
+ for menuentry in self.cacheEntries[dir]:
+ try:
+ if menuentry.DesktopFileID not in ids:
+ ids.append(menuentry.DesktopFileID)
+ list.append(menuentry)
+ elif menuentry.getType() == "System":
+ # FIXME: This is only 99% correct, but still...
+ i = list.index(menuentry)
+ e = list[i]
+ if e.getType() == "User":
+ e.Original = menuentry
+ except UnicodeDecodeError:
+ continue
+ self.cache[key] = list
+ return list
diff --git a/xdg/MenuEditor.py b/xdg/MenuEditor.py
index ce1d271..cc5ce54 100644
--- a/xdg/MenuEditor.py
+++ b/xdg/MenuEditor.py
@@ -21,491 +21,491 @@ import re
# Complex Rules/Deleted/OnlyAllocated/AppDirs/DirectoryDirs
class MenuEditor:
- def __init__(self, menu=None, filename=None, root=False):
- self.menu = None
- self.filename = None
- self.doc = None
- self.parse(menu, filename, root)
+ def __init__(self, menu=None, filename=None, root=False):
+ self.menu = None
+ self.filename = None
+ self.doc = None
+ self.parse(menu, filename, root)
- # fix for creating two menus with the same name on the fly
- self.filenames = []
+ # fix for creating two menus with the same name on the fly
+ self.filenames = []
- def parse(self, menu=None, filename=None, root=False):
- if root == True:
- setRootMode(True)
+ def parse(self, menu=None, filename=None, root=False):
+ if root == True:
+ setRootMode(True)
- if isinstance(menu, Menu):
- self.menu = menu
- elif menu:
- self.menu = parse(menu)
- else:
- self.menu = parse()
+ if isinstance(menu, Menu):
+ self.menu = menu
+ elif menu:
+ self.menu = parse(menu)
+ else:
+ self.menu = parse()
- if root == True:
- self.filename = self.menu.Filename
- elif filename:
- self.filename = filename
- else:
- self.filename = os.path.join(xdg_config_dirs[0], "menus", os.path.split(self.menu.Filename)[1])
+ if root == True:
+ self.filename = self.menu.Filename
+ elif filename:
+ self.filename = filename
+ else:
+ self.filename = os.path.join(xdg_config_dirs[0], "menus", os.path.split(self.menu.Filename)[1])
- try:
- self.doc = xml.dom.minidom.parse(self.filename)
- except IOError:
- self.doc = xml.dom.minidom.parseString('<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://standards.freedesktop.org/menu-spec/menu-1.0.dtd"><Menu><Name>Applications</Name><MergeFile type="parent">'+self.menu.Filename+'</MergeFile></Menu>')
- except xml.parsers.expat.ExpatError:
- raise ParsingError('Not a valid .menu file', self.filename)
+ try:
+ self.doc = xml.dom.minidom.parse(self.filename)
+ except IOError:
+ self.doc = xml.dom.minidom.parseString('<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://standards.freedesktop.org/menu-spec/menu-1.0.dtd"><Menu><Name>Applications</Name><MergeFile type="parent">'+self.menu.Filename+'</MergeFile></Menu>')
+ except xml.parsers.expat.ExpatError:
+ raise ParsingError('Not a valid .menu file', self.filename)
- self.__remove_whilespace_nodes(self.doc)
+ self.__remove_whilespace_nodes(self.doc)
- def save(self):
- self.__saveEntries(self.menu)
- self.__saveMenu()
+ def save(self):
+ self.__saveEntries(self.menu)
+ self.__saveMenu()
- def createMenuEntry(self, parent, name, command=None, genericname=None, comment=None, icon=None, terminal=None, after=None, before=None):
- menuentry = MenuEntry(self.__getFileName(name, ".desktop"))
- menuentry = self.editMenuEntry(menuentry, name, genericname, comment, command, icon, terminal)
+ def createMenuEntry(self, parent, name, command=None, genericname=None, comment=None, icon=None, terminal=None, after=None, before=None):
+ menuentry = MenuEntry(self.__getFileName(name, ".desktop"))
+ menuentry = self.editMenuEntry(menuentry, name, genericname, comment, command, icon, terminal)
- self.__addEntry(parent, menuentry, after, before)
+ self.__addEntry(parent, menuentry, after, before)
- sort(self.menu)
+ sort(self.menu)
- return menuentry
+ return menuentry
- def createMenu(self, parent, name, genericname=None, comment=None, icon=None, after=None, before=None):
- menu = Menu()
+ def createMenu(self, parent, name, genericname=None, comment=None, icon=None, after=None, before=None):
+ menu = Menu()
- menu.Parent = parent
- menu.Depth = parent.Depth + 1
- menu.Layout = parent.DefaultLayout
- menu.DefaultLayout = parent.DefaultLayout
+ menu.Parent = parent
+ menu.Depth = parent.Depth + 1
+ menu.Layout = parent.DefaultLayout
+ menu.DefaultLayout = parent.DefaultLayout
- menu = self.editMenu(menu, name, genericname, comment, icon)
+ menu = self.editMenu(menu, name, genericname, comment, icon)
- self.__addEntry(parent, menu, after, before)
+ self.__addEntry(parent, menu, after, before)
- sort(self.menu)
+ sort(self.menu)
- return menu
+ return menu
- def createSeparator(self, parent, after=None, before=None):
- separator = Separator(parent)
+ def createSeparator(self, parent, after=None, before=None):
+ separator = Separator(parent)
- self.__addEntry(parent, separator, after, before)
+ self.__addEntry(parent, separator, after, before)
- sort(self.menu)
+ sort(self.menu)
- return separator
+ return separator
- def moveMenuEntry(self, menuentry, oldparent, newparent, after=None, before=None):
- self.__deleteEntry(oldparent, menuentry, after, before)
- self.__addEntry(newparent, menuentry, after, before)
+ def moveMenuEntry(self, menuentry, oldparent, newparent, after=None, before=None):
+ self.__deleteEntry(oldparent, menuentry, after, before)
+ self.__addEntry(newparent, menuentry, after, before)
- sort(self.menu)
+ sort(self.menu)
- return menuentry
+ return menuentry
- def moveMenu(self, menu, oldparent, newparent, after=None, before=None):
- self.__deleteEntry(oldparent, menu, after, before)
- self.__addEntry(newparent, menu, after, before)
+ def moveMenu(self, menu, oldparent, newparent, after=None, before=None):
+ self.__deleteEntry(oldparent, menu, after, before)
+ self.__addEntry(newparent, menu, after, before)
- root_menu = self.__getXmlMenu(self.menu.Name)
- if oldparent.getPath(True) != newparent.getPath(True):
- self.__addXmlMove(root_menu, os.path.join(oldparent.getPath(True), menu.Name), os.path.join(newparent.getPath(True), menu.Name))
+ root_menu = self.__getXmlMenu(self.menu.Name)
+ if oldparent.getPath(True) != newparent.getPath(True):
+ self.__addXmlMove(root_menu, os.path.join(oldparent.getPath(True), menu.Name), os.path.join(newparent.getPath(True), menu.Name))
- sort(self.menu)
-
- return menu
-
- def moveSeparator(self, separator, parent, after=None, before=None):
- self.__deleteEntry(parent, separator, after, before)
- self.__addEntry(parent, separator, after, before)
+ sort(self.menu)
+
+ return menu
+
+ def moveSeparator(self, separator, parent, after=None, before=None):
+ self.__deleteEntry(parent, separator, after, before)
+ self.__addEntry(parent, separator, after, before)
- sort(self.menu)
+ sort(self.menu)
- return separator
+ return separator
- def copyMenuEntry(self, menuentry, oldparent, newparent, after=None, before=None):
- self.__addEntry(newparent, menuentry, after, before)
+ def copyMenuEntry(self, menuentry, oldparent, newparent, after=None, before=None):
+ self.__addEntry(newparent, menuentry, after, before)
- sort(self.menu)
+ sort(self.menu)
- return menuentry
+ return menuentry
- def editMenuEntry(self, menuentry, name=None, genericname=None, comment=None, command=None, icon=None, terminal=None, nodisplay=None, hidden=None):
- deskentry = menuentry.DesktopEntry
+ def editMenuEntry(self, menuentry, name=None, genericname=None, comment=None, command=None, icon=None, terminal=None, nodisplay=None, hidden=None):
+ deskentry = menuentry.DesktopEntry
- if name:
- if not deskentry.hasKey("Name"):
- deskentry.set("Name", name)
- deskentry.set("Name", name, locale = True)
- if comment:
- if not deskentry.hasKey("Comment"):
- deskentry.set("Comment", comment)
- deskentry.set("Comment", comment, locale = True)
- if genericname:
- if not deskentry.hasKey("GnericNe"):
- deskentry.set("GenericName", genericname)
- deskentry.set("GenericName", genericname, locale = True)
- if command:
- deskentry.set("Exec", command)
- if icon:
- deskentry.set("Icon", icon)
+ if name:
+ if not deskentry.hasKey("Name"):
+ deskentry.set("Name", name)
+ deskentry.set("Name", name, locale = True)
+ if comment:
+ if not deskentry.hasKey("Comment"):
+ deskentry.set("Comment", comment)
+ deskentry.set("Comment", comment, locale = True)
+ if genericname:
+ if not deskentry.hasKey("GnericNe"):
+ deskentry.set("GenericName", genericname)
+ deskentry.set("GenericName", genericname, locale = True)
+ if command:
+ deskentry.set("Exec", command)
+ if icon:
+ deskentry.set("Icon", icon)
- if terminal == True:
- deskentry.set("Terminal", "true")
- elif terminal == False:
- deskentry.set("Terminal", "false")
+ if terminal == True:
+ deskentry.set("Terminal", "true")
+ elif terminal == False:
+ deskentry.set("Terminal", "false")
- if nodisplay == True:
- deskentry.set("NoDisplay", "true")
- elif nodisplay == False:
- deskentry.set("NoDisplay", "false")
+ if nodisplay == True:
+ deskentry.set("NoDisplay", "true")
+ elif nodisplay == False:
+ deskentry.set("NoDisplay", "false")
- if hidden == True:
- deskentry.set("Hidden", "true")
- elif hidden == False:
- deskentry.set("Hidden", "false")
-
- menuentry.updateAttributes()
-
- if len(menuentry.Parents) > 0:
- sort(self.menu)
-
- return menuentry
-
- def editMenu(self, menu, name=None, genericname=None, comment=None, icon=None, nodisplay=None, hidden=None):
- # Hack for legacy dirs
- if isinstance(menu.Directory, MenuEntry) and menu.Directory.Filename == ".directory":
- xml_menu = self.__getXmlMenu(menu.getPath(True, True))
- self.__addXmlTextElement(xml_menu, 'Directory', menu.Name + ".directory")
- menu.Directory.setAttributes(menu.Name + ".directory")
- # Hack for New Entries
- elif not isinstance(menu.Directory, MenuEntry):
- if not name:
- name = menu.Name
- filename = self.__getFileName(name, ".directory").replace("/", "")
- if not menu.Name:
- menu.Name = filename.replace(".directory", "")
- xml_menu = self.__getXmlMenu(menu.getPath(True, True))
- self.__addXmlTextElement(xml_menu, 'Directory', filename)
- menu.Directory = MenuEntry(filename)
-
- deskentry = menu.Directory.DesktopEntry
-
- if name:
- if not deskentry.hasKey("Name"):
- deskentry.set("Name", name)
- deskentry.set("Name", name, locale = True)
- if genericname:
- if not deskentry.hasKey("GenericName"):
- deskentry.set("GenericName", genericname)
- deskentry.set("GenericName", genericname, locale = True)
- if comment:
- if not deskentry.hasKey("Comment"):
- deskentry.set("Comment", comment)
- deskentry.set("Comment", comment, locale = True)
- if icon:
- deskentry.set("Icon", icon)
-
- if nodisplay == True:
- deskentry.set("NoDisplay", "true")
- elif nodisplay == False:
- deskentry.set("NoDisplay", "false")
-
- if hidden == True:
- deskentry.set("Hidden", "true")
- elif hidden == False:
- deskentry.set("Hidden", "false")
-
- menu.Directory.updateAttributes()
-
- if isinstance(menu.Parent, Menu):
- sort(self.menu)
-
- return menu
-
- def hideMenuEntry(self, menuentry):
- self.editMenuEntry(menuentry, nodisplay = True)
-
- def unhideMenuEntry(self, menuentry):
- self.editMenuEntry(menuentry, nodisplay = False, hidden = False)
-
- def hideMenu(self, menu):
- self.editMenu(menu, nodisplay = True)
-
- def unhideMenu(self, menu):
- self.editMenu(menu, nodisplay = False, hidden = False)
- xml_menu = self.__getXmlMenu(menu.getPath(True,True), False)
- for node in self.__getXmlNodesByName(["Deleted", "NotDeleted"], xml_menu):
- node.parentNode.removeChild(node)
-
- def deleteMenuEntry(self, menuentry):
- if self.getAction(menuentry) == "delete":
- self.__deleteFile(menuentry.DesktopEntry.filename)
- for parent in menuentry.Parents:
- self.__deleteEntry(parent, menuentry)
- sort(self.menu)
- return menuentry
-
- def revertMenuEntry(self, menuentry):
- if self.getAction(menuentry) == "revert":
- self.__deleteFile(menuentry.DesktopEntry.filename)
- menuentry.Original.Parents = []
- for parent in menuentry.Parents:
- index = parent.Entries.index(menuentry)
- parent.Entries[index] = menuentry.Original
- index = parent.MenuEntries.index(menuentry)
- parent.MenuEntries[index] = menuentry.Original
- menuentry.Original.Parents.append(parent)
- sort(self.menu)
- return menuentry
-
- def deleteMenu(self, menu):
- if self.getAction(menu) == "delete":
- self.__deleteFile(menu.Directory.DesktopEntry.filename)
- self.__deleteEntry(menu.Parent, menu)
- xml_menu = self.__getXmlMenu(menu.getPath(True, True))
- xml_menu.parentNode.removeChild(xml_menu)
- sort(self.menu)
- return menu
-
- def revertMenu(self, menu):
- if self.getAction(menu) == "revert":
- self.__deleteFile(menu.Directory.DesktopEntry.filename)
- menu.Directory = menu.Directory.Original
- sort(self.menu)
- return menu
-
- def deleteSeparator(self, separator):
- self.__deleteEntry(separator.Parent, separator, after=True)
-
- sort(self.menu)
-
- return separator
-
- """ Private Stuff """
- def getAction(self, entry):
- if isinstance(entry, Menu):
- if not isinstance(entry.Directory, MenuEntry):
- return "none"
- elif entry.Directory.getType() == "Both":
- return "revert"
- elif entry.Directory.getType() == "User" \
- and (len(entry.Submenus) + len(entry.MenuEntries)) == 0:
- return "delete"
-
- elif isinstance(entry, MenuEntry):
- if entry.getType() == "Both":
- return "revert"
- elif entry.getType() == "User":
- return "delete"
- else:
- return "none"
-
- return "none"
-
- def __saveEntries(self, menu):
- if not menu:
- menu = self.menu
- if isinstance(menu.Directory, MenuEntry):
- menu.Directory.save()
- for entry in menu.getEntries(hidden=True):
- if isinstance(entry, MenuEntry):
- entry.save()
- elif isinstance(entry, Menu):
- self.__saveEntries(entry)
-
- def __saveMenu(self):
- if not os.path.isdir(os.path.dirname(self.filename)):
- os.makedirs(os.path.dirname(self.filename))
- fd = open(self.filename, 'w')
- fd.write(re.sub("\n[\s]*([^\n<]*)\n[\s]*</", "\\1</", self.doc.toprettyxml().replace('<?xml version="1.0" ?>\n', '')))
- fd.close()
-
- def __getFileName(self, name, extension):
- postfix = 0
- while 1:
- if postfix == 0:
- filename = name + extension
- else:
- filename = name + "-" + str(postfix) + extension
- if extension == ".desktop":
- dir = "applications"
- elif extension == ".directory":
- dir = "desktop-directories"
- if not filename in self.filenames and not \
- os.path.isfile(os.path.join(xdg_data_dirs[0], dir, filename)):
- self.filenames.append(filename)
- break
- else:
- postfix += 1
-
- return filename
-
- def __getXmlMenu(self, path, create=True, element=None):
- if not element:
- element = self.doc
-
- if "/" in path:
- (name, path) = path.split("/", 1)
- else:
- name = path
- path = ""
-
- found = None
- for node in self.__getXmlNodesByName("Menu", element):
- for child in self.__getXmlNodesByName("Name", node):
- if child.childNodes[0].nodeValue == name:
- if path:
- found = self.__getXmlMenu(path, create, node)
- else:
- found = node
- break
- if found:
- break
- if not found and create == True:
- node = self.__addXmlMenuElement(element, name)
- if path:
- found = self.__getXmlMenu(path, create, node)
- else:
- found = node
-
- return found
-
- def __addXmlMenuElement(self, element, name):
- node = self.doc.createElement('Menu')
- self.__addXmlTextElement(node, 'Name', name)
- return element.appendChild(node)
-
- def __addXmlTextElement(self, element, name, text):
- node = self.doc.createElement(name)
- text = self.doc.createTextNode(text)
- node.appendChild(text)
- return element.appendChild(node)
-
- def __addXmlFilename(self, element, filename, type = "Include"):
- # remove old filenames
- for node in self.__getXmlNodesByName(["Include", "Exclude"], element):
- if node.childNodes[0].nodeName == "Filename" and node.childNodes[0].childNodes[0].nodeValue == filename:
- element.removeChild(node)
-
- # add new filename
- node = self.doc.createElement(type)
- node.appendChild(self.__addXmlTextElement(node, 'Filename', filename))
- return element.appendChild(node)
-
- def __addXmlMove(self, element, old, new):
- node = self.doc.createElement("Move")
- node.appendChild(self.__addXmlTextElement(node, 'Old', old))
- node.appendChild(self.__addXmlTextElement(node, 'New', new))
- return element.appendChild(node)
-
- def __addXmlLayout(self, element, layout):
- # remove old layout
- for node in self.__getXmlNodesByName("Layout", element):
- element.removeChild(node)
-
- # add new layout
- node = self.doc.createElement("Layout")
- for order in layout.order:
- if order[0] == "Separator":
- child = self.doc.createElement("Separator")
- node.appendChild(child)
- elif order[0] == "Filename":
- child = self.__addXmlTextElement(node, "Filename", order[1])
- elif order[0] == "Menuname":
- child = self.__addXmlTextElement(node, "Menuname", order[1])
- elif order[0] == "Merge":
- child = self.doc.createElement("Merge")
- child.setAttribute("type", order[1])
- node.appendChild(child)
- return element.appendChild(node)
-
- def __getXmlNodesByName(self, name, element):
- for child in element.childNodes:
- if child.nodeType == xml.dom.Node.ELEMENT_NODE and child.nodeName in name:
- yield child
-
- def __addLayout(self, parent):
- layout = Layout()
- layout.order = []
- layout.show_empty = parent.Layout.show_empty
- layout.inline = parent.Layout.inline
- layout.inline_header = parent.Layout.inline_header
- layout.inline_alias = parent.Layout.inline_alias
- layout.inline_limit = parent.Layout.inline_limit
-
- layout.order.append(["Merge", "menus"])
- for entry in parent.Entries:
- if isinstance(entry, Menu):
- layout.parseMenuname(entry.Name)
- elif isinstance(entry, MenuEntry):
- layout.parseFilename(entry.DesktopFileID)
- elif isinstance(entry, Separator):
- layout.parseSeparator()
- layout.order.append(["Merge", "files"])
-
- parent.Layout = layout
-
- return layout
-
- def __addEntry(self, parent, entry, after=None, before=None):
- if after or before:
- if after:
- index = parent.Entries.index(after) + 1
- elif before:
- index = parent.Entries.index(before)
- parent.Entries.insert(index, entry)
- else:
- parent.Entries.append(entry)
-
- xml_parent = self.__getXmlMenu(parent.getPath(True, True))
-
- if isinstance(entry, MenuEntry):
- parent.MenuEntries.append(entry)
- entry.Parents.append(parent)
- self.__addXmlFilename(xml_parent, entry.DesktopFileID, "Include")
- elif isinstance(entry, Menu):
- parent.addSubmenu(entry)
-
- if after or before:
- self.__addLayout(parent)
- self.__addXmlLayout(xml_parent, parent.Layout)
-
- def __deleteEntry(self, parent, entry, after=None, before=None):
- parent.Entries.remove(entry)
-
- xml_parent = self.__getXmlMenu(parent.getPath(True, True))
-
- if isinstance(entry, MenuEntry):
- entry.Parents.remove(parent)
- parent.MenuEntries.remove(entry)
- self.__addXmlFilename(xml_parent, entry.DesktopFileID, "Exclude")
- elif isinstance(entry, Menu):
- parent.Submenus.remove(entry)
-
- if after or before:
- self.__addLayout(parent)
- self.__addXmlLayout(xml_parent, parent.Layout)
-
- def __deleteFile(self, filename):
- try:
- os.remove(filename)
- except OSError:
- pass
- try:
- self.filenames.remove(filename)
- except ValueError:
- pass
-
- def __remove_whilespace_nodes(self, node):
- remove_list = []
- for child in node.childNodes:
- if child.nodeType == xml.dom.minidom.Node.TEXT_NODE:
- child.data = child.data.strip()
- if not child.data.strip():
- remove_list.append(child)
- elif child.hasChildNodes():
- self.__remove_whilespace_nodes(child)
- for node in remove_list:
- node.parentNode.removeChild(node)
+ if hidden == True:
+ deskentry.set("Hidden", "true")
+ elif hidden == False:
+ deskentry.set("Hidden", "false")
+
+ menuentry.updateAttributes()
+
+ if len(menuentry.Parents) > 0:
+ sort(self.menu)
+
+ return menuentry
+
+ def editMenu(self, menu, name=None, genericname=None, comment=None, icon=None, nodisplay=None, hidden=None):
+ # Hack for legacy dirs
+ if isinstance(menu.Directory, MenuEntry) and menu.Directory.Filename == ".directory":
+ xml_menu = self.__getXmlMenu(menu.getPath(True, True))
+ self.__addXmlTextElement(xml_menu, 'Directory', menu.Name + ".directory")
+ menu.Directory.setAttributes(menu.Name + ".directory")
+ # Hack for New Entries
+ elif not isinstance(menu.Directory, MenuEntry):
+ if not name:
+ name = menu.Name
+ filename = self.__getFileName(name, ".directory").replace("/", "")
+ if not menu.Name:
+ menu.Name = filename.replace(".directory", "")
+ xml_menu = self.__getXmlMenu(menu.getPath(True, True))
+ self.__addXmlTextElement(xml_menu, 'Directory', filename)
+ menu.Directory = MenuEntry(filename)
+
+ deskentry = menu.Directory.DesktopEntry
+
+ if name:
+ if not deskentry.hasKey("Name"):
+ deskentry.set("Name", name)
+ deskentry.set("Name", name, locale = True)
+ if genericname:
+ if not deskentry.hasKey("GenericName"):
+ deskentry.set("GenericName", genericname)
+ deskentry.set("GenericName", genericname, locale = True)
+ if comment:
+ if not deskentry.hasKey("Comment"):
+ deskentry.set("Comment", comment)
+ deskentry.set("Comment", comment, locale = True)
+ if icon:
+ deskentry.set("Icon", icon)
+
+ if nodisplay == True:
+ deskentry.set("NoDisplay", "true")
+ elif nodisplay == False:
+ deskentry.set("NoDisplay", "false")
+
+ if hidden == True:
+ deskentry.set("Hidden", "true")
+ elif hidden == False:
+ deskentry.set("Hidden", "false")
+
+ menu.Directory.updateAttributes()
+
+ if isinstance(menu.Parent, Menu):
+ sort(self.menu)
+
+ return menu
+
+ def hideMenuEntry(self, menuentry):
+ self.editMenuEntry(menuentry, nodisplay = True)
+
+ def unhideMenuEntry(self, menuentry):
+ self.editMenuEntry(menuentry, nodisplay = False, hidden = False)
+
+ def hideMenu(self, menu):
+ self.editMenu(menu, nodisplay = True)
+
+ def unhideMenu(self, menu):
+ self.editMenu(menu, nodisplay = False, hidden = False)
+ xml_menu = self.__getXmlMenu(menu.getPath(True,True), False)
+ for node in self.__getXmlNodesByName(["Deleted", "NotDeleted"], xml_menu):
+ node.parentNode.removeChild(node)
+
+ def deleteMenuEntry(self, menuentry):
+ if self.getAction(menuentry) == "delete":
+ self.__deleteFile(menuentry.DesktopEntry.filename)
+ for parent in menuentry.Parents:
+ self.__deleteEntry(parent, menuentry)
+ sort(self.menu)
+ return menuentry
+
+ def revertMenuEntry(self, menuentry):
+ if self.getAction(menuentry) == "revert":
+ self.__deleteFile(menuentry.DesktopEntry.filename)
+ menuentry.Original.Parents = []
+ for parent in menuentry.Parents:
+ index = parent.Entries.index(menuentry)
+ parent.Entries[index] = menuentry.Original
+ index = parent.MenuEntries.index(menuentry)
+ parent.MenuEntries[index] = menuentry.Original
+ menuentry.Original.Parents.append(parent)
+ sort(self.menu)
+ return menuentry
+
+ def deleteMenu(self, menu):
+ if self.getAction(menu) == "delete":
+ self.__deleteFile(menu.Directory.DesktopEntry.filename)
+ self.__deleteEntry(menu.Parent, menu)
+ xml_menu = self.__getXmlMenu(menu.getPath(True, True))
+ xml_menu.parentNode.removeChild(xml_menu)
+ sort(self.menu)
+ return menu
+
+ def revertMenu(self, menu):
+ if self.getAction(menu) == "revert":
+ self.__deleteFile(menu.Directory.DesktopEntry.filename)
+ menu.Directory = menu.Directory.Original
+ sort(self.menu)
+ return menu
+
+ def deleteSeparator(self, separator):
+ self.__deleteEntry(separator.Parent, separator, after=True)
+
+ sort(self.menu)
+
+ return separator
+
+ """ Private Stuff """
+ def getAction(self, entry):
+ if isinstance(entry, Menu):
+ if not isinstance(entry.Directory, MenuEntry):
+ return "none"
+ elif entry.Directory.getType() == "Both":
+ return "revert"
+ elif entry.Directory.getType() == "User" \
+ and (len(entry.Submenus) + len(entry.MenuEntries)) == 0:
+ return "delete"
+
+ elif isinstance(entry, MenuEntry):
+ if entry.getType() == "Both":
+ return "revert"
+ elif entry.getType() == "User":
+ return "delete"
+ else:
+ return "none"
+
+ return "none"
+
+ def __saveEntries(self, menu):
+ if not menu:
+ menu = self.menu
+ if isinstance(menu.Directory, MenuEntry):
+ menu.Directory.save()
+ for entry in menu.getEntries(hidden=True):
+ if isinstance(entry, MenuEntry):
+ entry.save()
+ elif isinstance(entry, Menu):
+ self.__saveEntries(entry)
+
+ def __saveMenu(self):
+ if not os.path.isdir(os.path.dirname(self.filename)):
+ os.makedirs(os.path.dirname(self.filename))
+ fd = open(self.filename, 'w')
+ fd.write(re.sub("\n[\s]*([^\n<]*)\n[\s]*</", "\\1</", self.doc.toprettyxml().replace('<?xml version="1.0" ?>\n', '')))
+ fd.close()
+
+ def __getFileName(self, name, extension):
+ postfix = 0
+ while 1:
+ if postfix == 0:
+ filename = name + extension
+ else:
+ filename = name + "-" + str(postfix) + extension
+ if extension == ".desktop":
+ dir = "applications"
+ elif extension == ".directory":
+ dir = "desktop-directories"
+ if not filename in self.filenames and not \
+ os.path.isfile(os.path.join(xdg_data_dirs[0], dir, filename)):
+ self.filenames.append(filename)
+ break
+ else:
+ postfix += 1
+
+ return filename
+
+ def __getXmlMenu(self, path, create=True, element=None):
+ if not element:
+ element = self.doc
+
+ if "/" in path:
+ (name, path) = path.split("/", 1)
+ else:
+ name = path
+ path = ""
+
+ found = None
+ for node in self.__getXmlNodesByName("Menu", element):
+ for child in self.__getXmlNodesByName("Name", node):
+ if child.childNodes[0].nodeValue == name:
+ if path:
+ found = self.__getXmlMenu(path, create, node)
+ else:
+ found = node
+ break
+ if found:
+ break
+ if not found and create == True:
+ node = self.__addXmlMenuElement(element, name)
+ if path:
+ found = self.__getXmlMenu(path, create, node)
+ else:
+ found = node
+
+ return found
+
+ def __addXmlMenuElement(self, element, name):
+ node = self.doc.createElement('Menu')
+ self.__addXmlTextElement(node, 'Name', name)
+ return element.appendChild(node)
+
+ def __addXmlTextElement(self, element, name, text):
+ node = self.doc.createElement(name)
+ text = self.doc.createTextNode(text)
+ node.appendChild(text)
+ return element.appendChild(node)
+
+ def __addXmlFilename(self, element, filename, type = "Include"):
+ # remove old filenames
+ for node in self.__getXmlNodesByName(["Include", "Exclude"], element):
+ if node.childNodes[0].nodeName == "Filename" and node.childNodes[0].childNodes[0].nodeValue == filename:
+ element.removeChild(node)
+
+ # add new filename
+ node = self.doc.createElement(type)
+ node.appendChild(self.__addXmlTextElement(node, 'Filename', filename))
+ return element.appendChild(node)
+
+ def __addXmlMove(self, element, old, new):
+ node = self.doc.createElement("Move")
+ node.appendChild(self.__addXmlTextElement(node, 'Old', old))
+ node.appendChild(self.__addXmlTextElement(node, 'New', new))
+ return element.appendChild(node)
+
+ def __addXmlLayout(self, element, layout):
+ # remove old layout
+ for node in self.__getXmlNodesByName("Layout", element):
+ element.removeChild(node)
+
+ # add new layout
+ node = self.doc.createElement("Layout")
+ for order in layout.order:
+ if order[0] == "Separator":
+ child = self.doc.createElement("Separator")
+ node.appendChild(child)
+ elif order[0] == "Filename":
+ child = self.__addXmlTextElement(node, "Filename", order[1])
+ elif order[0] == "Menuname":
+ child = self.__addXmlTextElement(node, "Menuname", order[1])
+ elif order[0] == "Merge":
+ child = self.doc.createElement("Merge")
+ child.setAttribute("type", order[1])
+ node.appendChild(child)
+ return element.appendChild(node)
+
+ def __getXmlNodesByName(self, name, element):
+ for child in element.childNodes:
+ if child.nodeType == xml.dom.Node.ELEMENT_NODE and child.nodeName in name:
+ yield child
+
+ def __addLayout(self, parent):
+ layout = Layout()
+ layout.order = []
+ layout.show_empty = parent.Layout.show_empty
+ layout.inline = parent.Layout.inline
+ layout.inline_header = parent.Layout.inline_header
+ layout.inline_alias = parent.Layout.inline_alias
+ layout.inline_limit = parent.Layout.inline_limit
+
+ layout.order.append(["Merge", "menus"])
+ for entry in parent.Entries:
+ if isinstance(entry, Menu):
+ layout.parseMenuname(entry.Name)
+ elif isinstance(entry, MenuEntry):
+ layout.parseFilename(entry.DesktopFileID)
+ elif isinstance(entry, Separator):
+ layout.parseSeparator()
+ layout.order.append(["Merge", "files"])
+
+ parent.Layout = layout
+
+ return layout
+
+ def __addEntry(self, parent, entry, after=None, before=None):
+ if after or before:
+ if after:
+ index = parent.Entries.index(after) + 1
+ elif before:
+ index = parent.Entries.index(before)
+ parent.Entries.insert(index, entry)
+ else:
+ parent.Entries.append(entry)
+
+ xml_parent = self.__getXmlMenu(parent.getPath(True, True))
+
+ if isinstance(entry, MenuEntry):
+ parent.MenuEntries.append(entry)
+ entry.Parents.append(parent)
+ self.__addXmlFilename(xml_parent, entry.DesktopFileID, "Include")
+ elif isinstance(entry, Menu):
+ parent.addSubmenu(entry)
+
+ if after or before:
+ self.__addLayout(parent)
+ self.__addXmlLayout(xml_parent, parent.Layout)
+
+ def __deleteEntry(self, parent, entry, after=None, before=None):
+ parent.Entries.remove(entry)
+
+ xml_parent = self.__getXmlMenu(parent.getPath(True, True))
+
+ if isinstance(entry, MenuEntry):
+ entry.Parents.remove(parent)
+ parent.MenuEntries.remove(entry)
+ self.__addXmlFilename(xml_parent, entry.DesktopFileID, "Exclude")
+ elif isinstance(entry, Menu):
+ parent.Submenus.remove(entry)
+
+ if after or before:
+ self.__addLayout(parent)
+ self.__addXmlLayout(xml_parent, parent.Layout)
+
+ def __deleteFile(self, filename):
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+ try:
+ self.filenames.remove(filename)
+ except ValueError:
+ pass
+
+ def __remove_whilespace_nodes(self, node):
+ remove_list = []
+ for child in node.childNodes:
+ if child.nodeType == xml.dom.minidom.Node.TEXT_NODE:
+ child.data = child.data.strip()
+ if not child.data.strip():
+ remove_list.append(child)
+ elif child.hasChildNodes():
+ self.__remove_whilespace_nodes(child)
+ for node in remove_list:
+ node.parentNode.removeChild(node)
diff --git a/xdg/Mime.py b/xdg/Mime.py
index d92f57d..73ff914 100644
--- a/xdg/Mime.py
+++ b/xdg/Mime.py
@@ -30,273 +30,273 @@ from xml.dom import Node, minidom, XML_NAMESPACE
FREE_NS = 'http://www.freedesktop.org/standards/shared-mime-info'
-types = {} # Maps MIME names to type objects
+types = {} # Maps MIME names to type objects
-exts = None # Maps extensions to types
-globs = None # List of (glob, type) pairs
-literals = None # Maps liternal names to types
+exts = None # Maps extensions to types
+globs = None # List of (glob, type) pairs
+literals = None # Maps liternal names to types
magic = None
def _get_node_data(node):
- """Get text of XML node"""
- return ''.join([n.nodeValue for n in node.childNodes]).strip()
+ """Get text of XML node"""
+ return ''.join([n.nodeValue for n in node.childNodes]).strip()
def lookup(media, subtype = None):
- "Get the MIMEtype object for this type, creating a new one if needed."
- if subtype is None and '/' in media:
- media, subtype = media.split('/', 1)
- if (media, subtype) not in types:
- types[(media, subtype)] = MIMEtype(media, subtype)
- return types[(media, subtype)]
+ "Get the MIMEtype object for this type, creating a new one if needed."
+ if subtype is None and '/' in media:
+ media, subtype = media.split('/', 1)
+ if (media, subtype) not in types:
+ types[(media, subtype)] = MIMEtype(media, subtype)
+ return types[(media, subtype)]
class MIMEtype:
- """Type holding data about a MIME type"""
- def __init__(self, media, subtype):
- "Don't use this constructor directly; use mime.lookup() instead."
- assert media and '/' not in media
- assert subtype and '/' not in subtype
- assert (media, subtype) not in types
-
- self.media = media
- self.subtype = subtype
- self._comment = None
-
- def _load(self):
- "Loads comment for current language. Use get_comment() instead."
- resource = os.path.join('mime', self.media, self.subtype + '.xml')
- for path in xdg.BaseDirectory.load_data_paths(resource):
- doc = minidom.parse(path)
- if doc is None:
- continue
- for comment in doc.documentElement.getElementsByTagNameNS(FREE_NS, 'comment'):
- lang = comment.getAttributeNS(XML_NAMESPACE, 'lang') or 'en'
- goodness = 1 + (lang in xdg.Locale.langs)
- if goodness > self._comment[0]:
- self._comment = (goodness, _get_node_data(comment))
- if goodness == 2: return
-
- # FIXME: add get_icon method
- def get_comment(self):
- """Returns comment for current language, loading it if needed."""
- # Should we ever reload?
- if self._comment is None:
- self._comment = (0, str(self))
- self._load()
- return self._comment[1]
-
- def __str__(self):
- return self.media + '/' + self.subtype
-
- def __repr__(self):
- return '[%s: %s]' % (self, self._comment or '(comment not loaded)')
+ """Type holding data about a MIME type"""
+ def __init__(self, media, subtype):
+ "Don't use this constructor directly; use mime.lookup() instead."
+ assert media and '/' not in media
+ assert subtype and '/' not in subtype
+ assert (media, subtype) not in types
+
+ self.media = media
+ self.subtype = subtype
+ self._comment = None
+
+ def _load(self):
+ "Loads comment for current language. Use get_comment() instead."
+ resource = os.path.join('mime', self.media, self.subtype + '.xml')
+ for path in xdg.BaseDirectory.load_data_paths(resource):
+ doc = minidom.parse(path)
+ if doc is None:
+ continue
+ for comment in doc.documentElement.getElementsByTagNameNS(FREE_NS, 'comment'):
+ lang = comment.getAttributeNS(XML_NAMESPACE, 'lang') or 'en'
+ goodness = 1 + (lang in xdg.Locale.langs)
+ if goodness > self._comment[0]:
+ self._comment = (goodness, _get_node_data(comment))
+ if goodness == 2: return
+
+ # FIXME: add get_icon method
+ def get_comment(self):
+ """Returns comment for current language, loading it if needed."""
+ # Should we ever reload?
+ if self._comment is None:
+ self._comment = (0, str(self))
+ self._load()
+ return self._comment[1]
+
+ def __str__(self):
+ return self.media + '/' + self.subtype
+
+ def __repr__(self):
+ return '[%s: %s]' % (self, self._comment or '(comment not loaded)')
class MagicRule:
- def __init__(self, f):
- self.next=None
- self.prev=None
-
- #print line
- ind=''
- while True:
- c=f.read(1)
- if c=='>':
- break
- ind+=c
- if not ind:
- self.nest=0
- else:
- self.nest=int(ind)
-
- start=''
- while True:
- c=f.read(1)
- if c=='=':
- break
- start+=c
- self.start=int(start)
-
- hb=f.read(1)
- lb=f.read(1)
- self.lenvalue=ord(lb)+(ord(hb)<<8)
-
- self.value=f.read(self.lenvalue)
-
- c=f.read(1)
- if c=='&':
- self.mask=f.read(self.lenvalue)
- c=f.read(1)
- else:
- self.mask=None
-
- if c=='~':
- w=''
- while c!='+' and c!='\n':
- c=f.read(1)
- if c=='+' or c=='\n':
- break
- w+=c
-
- self.word=int(w)
- else:
- self.word=1
-
- if c=='+':
- r=''
- while c!='\n':
- c=f.read(1)
- if c=='\n':
- break
- r+=c
- #print r
- self.range=int(r)
- else:
- self.range=1
-
- if c!='\n':
- raise 'Malformed MIME magic line'
-
- def getLength(self):
- return self.start+self.lenvalue+self.range
-
- def appendRule(self, rule):
- if self.nest<rule.nest:
- self.next=rule
- rule.prev=self
-
- elif self.prev:
- self.prev.appendRule(rule)
-
- def match(self, buffer):
- if self.match0(buffer):
- if self.next:
- return self.next.match(buffer)
- return True
-
- def match0(self, buffer):
- l=len(buffer)
- for o in range(self.range):
- s=self.start+o
- e=s+self.lenvalue
- if l<e:
- return False
- if self.mask:
- test=''
- for i in range(self.lenvalue):
- c=ord(buffer[s+i]) & ord(self.mask[i])
- test+=chr(c)
- else:
- test=buffer[s:e]
-
- if test==self.value:
- return True
-
- def __repr__(self):
- return '<MagicRule %d>%d=[%d]%s&%s~%d+%d>' % (self.nest,
- self.start,
- self.lenvalue,
- `self.value`,
- `self.mask`,
- self.word,
- self.range)
+ def __init__(self, f):
+ self.next=None
+ self.prev=None
+
+ #print line
+ ind=''
+ while True:
+ c=f.read(1)
+ if c=='>':
+ break
+ ind+=c
+ if not ind:
+ self.nest=0
+ else:
+ self.nest=int(ind)
+
+ start=''
+ while True:
+ c=f.read(1)
+ if c=='=':
+ break
+ start+=c
+ self.start=int(start)
+
+ hb=f.read(1)
+ lb=f.read(1)
+ self.lenvalue=ord(lb)+(ord(hb)<<8)
+
+ self.value=f.read(self.lenvalue)
+
+ c=f.read(1)
+ if c=='&':
+ self.mask=f.read(self.lenvalue)
+ c=f.read(1)
+ else:
+ self.mask=None
+
+ if c=='~':
+ w=''
+ while c!='+' and c!='\n':
+ c=f.read(1)
+ if c=='+' or c=='\n':
+ break
+ w+=c
+
+ self.word=int(w)
+ else:
+ self.word=1
+
+ if c=='+':
+ r=''
+ while c!='\n':
+ c=f.read(1)
+ if c=='\n':
+ break
+ r+=c
+ #print r
+ self.range=int(r)
+ else:
+ self.range=1
+
+ if c!='\n':
+ raise 'Malformed MIME magic line'
+
+ def getLength(self):
+ return self.start+self.lenvalue+self.range
+
+ def appendRule(self, rule):
+ if self.nest<rule.nest:
+ self.next=rule
+ rule.prev=self
+
+ elif self.prev:
+ self.prev.appendRule(rule)
+
+ def match(self, buffer):
+ if self.match0(buffer):
+ if self.next:
+ return self.next.match(buffer)
+ return True
+
+ def match0(self, buffer):
+ l=len(buffer)
+ for o in range(self.range):
+ s=self.start+o
+ e=s+self.lenvalue
+ if l<e:
+ return False
+ if self.mask:
+ test=''
+ for i in range(self.lenvalue):
+ c=ord(buffer[s+i]) & ord(self.mask[i])
+ test+=chr(c)
+ else:
+ test=buffer[s:e]
+
+ if test==self.value:
+ return True
+
+ def __repr__(self):
+ return '<MagicRule %d>%d=[%d]%s&%s~%d+%d>' % (self.nest,
+ self.start,
+ self.lenvalue,
+ `self.value`,
+ `self.mask`,
+ self.word,
+ self.range)
class MagicType:
- def __init__(self, mtype):
- self.mtype=mtype
- self.top_rules=[]
- self.last_rule=None
+ def __init__(self, mtype):
+ self.mtype=mtype
+ self.top_rules=[]
+ self.last_rule=None
- def getLine(self, f):
- nrule=MagicRule(f)
+ def getLine(self, f):
+ nrule=MagicRule(f)
- if nrule.nest and self.last_rule:
- self.last_rule.appendRule(nrule)
- else:
- self.top_rules.append(nrule)
+ if nrule.nest and self.last_rule:
+ self.last_rule.appendRule(nrule)
+ else:
+ self.top_rules.append(nrule)
- self.last_rule=nrule
+ self.last_rule=nrule
- return nrule
+ return nrule
- def match(self, buffer):
- for rule in self.top_rules:
- if rule.match(buffer):
- return self.mtype
+ def match(self, buffer):
+ for rule in self.top_rules:
+ if rule.match(buffer):
+ return self.mtype
- def __repr__(self):
- return '<MagicType %s>' % self.mtype
-
+ def __repr__(self):
+ return '<MagicType %s>' % self.mtype
+
class MagicDB:
- def __init__(self):
- self.types={} # Indexed by priority, each entry is a list of type rules
- self.maxlen=0
-
- def mergeFile(self, fname):
- f=file(fname, 'r')
- line=f.readline()
- if line!='MIME-Magic\0\n':
- raise 'Not a MIME magic file'
-
- while True:
- shead=f.readline()
- #print shead
- if not shead:
- break
- if shead[0]!='[' or shead[-2:]!=']\n':
- raise 'Malformed section heading'
- pri, tname=shead[1:-2].split(':')
- #print shead[1:-2]
- pri=int(pri)
- mtype=lookup(tname)
-
- try:
- ents=self.types[pri]
- except:
- ents=[]
- self.types[pri]=ents
-
- magictype=MagicType(mtype)
- #print tname
-
- #rline=f.readline()
- c=f.read(1)
- f.seek(-1, 1)
- while c and c!='[':
- rule=magictype.getLine(f)
- #print rule
- if rule and rule.getLength()>self.maxlen:
- self.maxlen=rule.getLength()
-
- c=f.read(1)
- f.seek(-1, 1)
-
- ents.append(magictype)
- #self.types[pri]=ents
- if not c:
- break
-
- def match(self, path, max_pri=100, min_pri=0):
- try:
- buf=file(path, 'r').read(self.maxlen)
- pris=self.types.keys()
- pris.sort(lambda a, b: -cmp(a, b))
- for pri in pris:
- #print pri, max_pri, min_pri
- if pri>max_pri:
- continue
- if pri<min_pri:
- break
- for type in self.types[pri]:
- m=type.match(buf)
- if m:
- return m
- except:
- pass
-
- return None
-
- def __repr__(self):
- return '<MagicDB %s>' % self.types
-
+ def __init__(self):
+ self.types={} # Indexed by priority, each entry is a list of type rules
+ self.maxlen=0
+
+ def mergeFile(self, fname):
+ f=file(fname, 'r')
+ line=f.readline()
+ if line!='MIME-Magic\0\n':
+ raise 'Not a MIME magic file'
+
+ while True:
+ shead=f.readline()
+ #print shead
+ if not shead:
+ break
+ if shead[0]!='[' or shead[-2:]!=']\n':
+ raise 'Malformed section heading'
+ pri, tname=shead[1:-2].split(':')
+ #print shead[1:-2]
+ pri=int(pri)
+ mtype=lookup(tname)
+
+ try:
+ ents=self.types[pri]
+ except:
+ ents=[]
+ self.types[pri]=ents
+
+ magictype=MagicType(mtype)
+ #print tname
+
+ #rline=f.readline()
+ c=f.read(1)
+ f.seek(-1, 1)
+ while c and c!='[':
+ rule=magictype.getLine(f)
+ #print rule
+ if rule and rule.getLength()>self.maxlen:
+ self.maxlen=rule.getLength()
+
+ c=f.read(1)
+ f.seek(-1, 1)
+
+ ents.append(magictype)
+ #self.types[pri]=ents
+ if not c:
+ break
+
+ def match(self, path, max_pri=100, min_pri=0):
+ try:
+ buf=file(path, 'r').read(self.maxlen)
+ pris=self.types.keys()
+ pris.sort(lambda a, b: -cmp(a, b))
+ for pri in pris:
+ #print pri, max_pri, min_pri
+ if pri>max_pri:
+ continue
+ if pri<min_pri:
+ break
+ for type in self.types[pri]:
+ m=type.match(buf)
+ if m:
+ return m
+ except:
+ pass
+
+ return None
+
+ def __repr__(self):
+ return '<MagicDB %s>' % self.types
+
# Some well-known types
text = lookup('text', 'plain')
@@ -312,152 +312,152 @@ app_exe = lookup('application', 'executable')
_cache_uptodate = False
def _cache_database():
- global exts, globs, literals, magic, _cache_uptodate
-
- _cache_uptodate = True
-
- exts = {} # Maps extensions to types
- globs = [] # List of (glob, type) pairs
- literals = {} # Maps liternal names to types
- magic = MagicDB()
-
- def _import_glob_file(path):
- """Loads name matching information from a MIME directory."""
- for line in file(path):
- if line.startswith('#'): continue
- line = line[:-1]
-
- type_name, pattern = line.split(':', 1)
- mtype = lookup(type_name)
-
- if pattern.startswith('*.'):
- rest = pattern[2:]
- if not ('*' in rest or '[' in rest or '?' in rest):
- exts[rest] = mtype
- continue
- if '*' in pattern or '[' in pattern or '?' in pattern:
- globs.append((pattern, mtype))
- else:
- literals[pattern] = mtype
-
- for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'globs')):
- _import_glob_file(path)
- for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'magic')):
- magic.mergeFile(path)
-
- # Sort globs by length
- globs.sort(lambda a, b: cmp(len(b[0]), len(a[0])))
+ global exts, globs, literals, magic, _cache_uptodate
+
+ _cache_uptodate = True
+
+ exts = {} # Maps extensions to types
+ globs = [] # List of (glob, type) pairs
+ literals = {} # Maps liternal names to types
+ magic = MagicDB()
+
+ def _import_glob_file(path):
+ """Loads name matching information from a MIME directory."""
+ for line in file(path):
+ if line.startswith('#'): continue
+ line = line[:-1]
+
+ type_name, pattern = line.split(':', 1)
+ mtype = lookup(type_name)
+
+ if pattern.startswith('*.'):
+ rest = pattern[2:]
+ if not ('*' in rest or '[' in rest or '?' in rest):
+ exts[rest] = mtype
+ continue
+ if '*' in pattern or '[' in pattern or '?' in pattern:
+ globs.append((pattern, mtype))
+ else:
+ literals[pattern] = mtype
+
+ for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'globs')):
+ _import_glob_file(path)
+ for path in xdg.BaseDirectory.load_data_paths(os.path.join('mime', 'magic')):
+ magic.mergeFile(path)
+
+ # Sort globs by length
+ globs.sort(lambda a, b: cmp(len(b[0]), len(a[0])))
def get_type_by_name(path):
- """Returns type of file by its name, or None if not known"""
- if not _cache_uptodate:
- _cache_database()
-
- leaf = os.path.basename(path)
- if leaf in literals:
- return literals[leaf]
-
- lleaf = leaf.lower()
- if lleaf in literals:
- return literals[lleaf]
-
- ext = leaf
- while 1:
- p = ext.find('.')
- if p < 0: break
- ext = ext[p + 1:]
- if ext in exts:
- return exts[ext]
- ext = lleaf
- while 1:
- p = ext.find('.')
- if p < 0: break
- ext = ext[p+1:]
- if ext in exts:
- return exts[ext]
- for (glob, mime_type) in globs:
- if fnmatch.fnmatch(leaf, glob):
- return mime_type
- if fnmatch.fnmatch(lleaf, glob):
- return mime_type
- return None
+ """Returns type of file by its name, or None if not known"""
+ if not _cache_uptodate:
+ _cache_database()
+
+ leaf = os.path.basename(path)
+ if leaf in literals:
+ return literals[leaf]
+
+ lleaf = leaf.lower()
+ if lleaf in literals:
+ return literals[lleaf]
+
+ ext = leaf
+ while 1:
+ p = ext.find('.')
+ if p < 0: break
+ ext = ext[p + 1:]
+ if ext in exts:
+ return exts[ext]
+ ext = lleaf
+ while 1:
+ p = ext.find('.')
+ if p < 0: break
+ ext = ext[p+1:]
+ if ext in exts:
+ return exts[ext]
+ for (glob, mime_type) in globs:
+ if fnmatch.fnmatch(leaf, glob):
+ return mime_type
+ if fnmatch.fnmatch(lleaf, glob):
+ return mime_type
+ return None
def get_type_by_contents(path, max_pri=100, min_pri=0):
- """Returns type of file by its contents, or None if not known"""
- if not _cache_uptodate:
- _cache_database()
+ """Returns type of file by its contents, or None if not known"""
+ if not _cache_uptodate:
+ _cache_database()
- return magic.match(path, max_pri, min_pri)
+ return magic.match(path, max_pri, min_pri)
def get_type(path, follow=1, name_pri=100):
- """Returns type of file indicated by path.
- path - pathname to check (need not exist)
- follow - when reading file, follow symbolic links
- name_pri - Priority to do name matches. 100=override magic"""
- if not _cache_uptodate:
- _cache_database()
-
- try:
- if follow:
- st = os.stat(path)
- else:
- st = os.lstat(path)
- except:
- t = get_type_by_name(path)
- return t or text
-
- if stat.S_ISREG(st.st_mode):
- t = get_type_by_contents(path, min_pri=name_pri)
- if not t: t = get_type_by_name(path)
- if not t: t = get_type_by_contents(path, max_pri=name_pri)
- if t is None:
- if stat.S_IMODE(st.st_mode) & 0111:
- return app_exe
- else:
- return text
- return t
- elif stat.S_ISDIR(st.st_mode): return inode_dir
- elif stat.S_ISCHR(st.st_mode): return inode_char
- elif stat.S_ISBLK(st.st_mode): return inode_block
- elif stat.S_ISFIFO(st.st_mode): return inode_fifo
- elif stat.S_ISLNK(st.st_mode): return inode_symlink
- elif stat.S_ISSOCK(st.st_mode): return inode_socket
- return inode_door
+ """Returns type of file indicated by path.
+ path - pathname to check (need not exist)
+ follow - when reading file, follow symbolic links
+ name_pri - Priority to do name matches. 100=override magic"""
+ if not _cache_uptodate:
+ _cache_database()
+
+ try:
+ if follow:
+ st = os.stat(path)
+ else:
+ st = os.lstat(path)
+ except:
+ t = get_type_by_name(path)
+ return t or text
+
+ if stat.S_ISREG(st.st_mode):
+ t = get_type_by_contents(path, min_pri=name_pri)
+ if not t: t = get_type_by_name(path)
+ if not t: t = get_type_by_contents(path, max_pri=name_pri)
+ if t is None:
+ if stat.S_IMODE(st.st_mode) & 0111:
+ return app_exe
+ else:
+ return text
+ return t
+ elif stat.S_ISDIR(st.st_mode): return inode_dir
+ elif stat.S_ISCHR(st.st_mode): return inode_char
+ elif stat.S_ISBLK(st.st_mode): return inode_block
+ elif stat.S_ISFIFO(st.st_mode): return inode_fifo
+ elif stat.S_ISLNK(st.st_mode): return inode_symlink
+ elif stat.S_ISSOCK(st.st_mode): return inode_socket
+ return inode_door
def install_mime_info(application, package_file):
- """Copy 'package_file' as ~/.local/share/mime/packages/<application>.xml.
- If package_file is None, install <app_dir>/<application>.xml.
- If already installed, does nothing. May overwrite an existing
- file with the same name (if the contents are different)"""
- application += '.xml'
-
- new_data = file(package_file).read()
-
- # See if the file is already installed
- package_dir = os.path.join('mime', 'packages')
- resource = os.path.join(package_dir, application)
- for x in xdg.BaseDirectory.load_data_paths(resource):
- try:
- old_data = file(x).read()
- except:
- continue
- if old_data == new_data:
- return # Already installed
-
- global _cache_uptodate
- _cache_uptodate = False
-
- # Not already installed; add a new copy
- # Create the directory structure...
- new_file = os.path.join(xdg.BaseDirectory.save_data_path(package_dir), application)
-
- # Write the file...
- file(new_file, 'w').write(new_data)
-
- # Update the database...
- command = 'update-mime-database'
- if os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime')):
- os.unlink(new_file)
- raise Exception("The '%s' command returned an error code!\n" \
- "Make sure you have the freedesktop.org shared MIME package:\n" \
- "http://standards.freedesktop.org/shared-mime-info/") % command
+ """Copy 'package_file' as ~/.local/share/mime/packages/<application>.xml.
+ If package_file is None, install <app_dir>/<application>.xml.
+ If already installed, does nothing. May overwrite an existing
+ file with the same name (if the contents are different)"""
+ application += '.xml'
+
+ new_data = file(package_file).read()
+
+ # See if the file is already installed
+ package_dir = os.path.join('mime', 'packages')
+ resource = os.path.join(package_dir, application)
+ for x in xdg.BaseDirectory.load_data_paths(resource):
+ try:
+ old_data = file(x).read()
+ except:
+ continue
+ if old_data == new_data:
+ return # Already installed
+
+ global _cache_uptodate
+ _cache_uptodate = False
+
+ # Not already installed; add a new copy
+ # Create the directory structure...
+ new_file = os.path.join(xdg.BaseDirectory.save_data_path(package_dir), application)
+
+ # Write the file...
+ file(new_file, 'w').write(new_data)
+
+ # Update the database...
+ command = 'update-mime-database'
+ if os.spawnlp(os.P_WAIT, command, command, xdg.BaseDirectory.save_data_path('mime')):
+ os.unlink(new_file)
+ raise Exception("The '%s' command returned an error code!\n" \
+ "Make sure you have the freedesktop.org shared MIME package:\n" \
+ "http://standards.freedesktop.org/shared-mime-info/") % command
diff --git a/xdg/RecentFiles.py b/xdg/RecentFiles.py
index 32034b9..6c2cd85 100644
--- a/xdg/RecentFiles.py
+++ b/xdg/RecentFiles.py
@@ -8,152 +8,152 @@ import os, time, fcntl
from xdg.Exceptions import *
class RecentFiles:
- def __init__(self):
- self.RecentFiles = []
- self.filename = ""
-
- def parse(self, filename=None):
- if not filename:
- filename = os.path.join(os.getenv("HOME"), ".recently-used")
-
- try:
- doc = xml.dom.minidom.parse(filename)
- except IOError:
- raise ParsingError('File not found', filename)
- except xml.parsers.expat.ExpatError:
- raise ParsingError('Not a valid .menu file', filename)
-
- self.filename = filename
-
- for child in doc.childNodes:
- if child.nodeType == xml.dom.Node.ELEMENT_NODE:
- if child.tagName == "RecentFiles":
- for recent in child.childNodes:
- if recent.nodeType == xml.dom.Node.ELEMENT_NODE:
- if recent.tagName == "RecentItem":
- self.__parseRecentItem(recent)
-
- self.sort()
-
- def __parseRecentItem(self, item):
- recent = RecentFile()
- self.RecentFiles.append(recent)
-
- for attribute in item.childNodes:
- if attribute.nodeType == xml.dom.Node.ELEMENT_NODE:
- if attribute.tagName == "URI":
- recent.URI = attribute.childNodes[0].nodeValue
- elif attribute.tagName == "Mime-Type":
- recent.MimeType = attribute.childNodes[0].nodeValue
- elif attribute.tagName == "Timestamp":
- recent.Timestamp = attribute.childNodes[0].nodeValue
- elif attribute.tagName == "Private":
- recent.Prviate = True
- elif attribute.tagName == "Groups":
-
- for group in attribute.childNodes:
- if group.nodeType == xml.dom.Node.ELEMENT_NODE:
- if group.tagName == "Group":
- recent.Groups.append(group.childNodes[0].nodeValue)
-
- def write(self, filename=None):
- if not filename and not self.filename:
- raise ParsingError('File not found', filename)
- elif not filename:
- filename = self.filename
-
- f = open(filename, "w")
- fcntl.lockf(f, fcntl.LOCK_EX)
- f.write('<?xml version="1.0"?>\n')
- f.write("<RecentFiles>\n")
-
- for r in self.RecentFiles:
- f.write(" <RecentItem>\n")
- f.write(" <URI>%s</URI>\n" % xml.sax.saxutils.escape(r.URI))
- f.write(" <Mime-Type>%s</Mime-Type>\n" % r.MimeType)
- f.write(" <Timestamp>%s</Timestamp>\n" % r.Timestamp)
- if r.Private == True:
- f.write(" <Private/>\n")
- if len(r.Groups) > 0:
- f.write(" <Groups>\n")
- for group in r.Groups:
- f.write(" <Group>%s</Group>\n" % group)
- f.write(" </Groups>\n")
- f.write(" </RecentItem>\n")
-
- f.write("</RecentFiles>\n")
- fcntl.lockf(f, fcntl.LOCK_UN)
- f.close()
-
- def getFiles(self, mimetypes=None, groups=None, limit=0):
- tmp = []
- i = 0
- for item in self.RecentFiles:
- if groups:
- for group in groups:
- if group in item.Groups:
- tmp.append(item)
- i += 1
- elif mimetypes:
- for mimetype in mimetypes:
- if mimetype == item.MimeType:
- tmp.append(item)
- i += 1
- else:
- if item.Private == False:
- tmp.append(item)
- i += 1
- if limit != 0 and i == limit:
- break
-
- return tmp
-
- def addFile(self, item, mimetype, groups=None, private=False):
- # check if entry already there
- if item in self.RecentFiles:
- index = self.RecentFiles.index(item)
- recent = self.RecentFiles[index]
- else:
- # delete if more then 500 files
- if len(self.RecentFiles) == 500:
- self.RecentFiles.pop()
- # add entry
- recent = RecentFile()
- self.RecentFiles.append(recent)
-
- recent.URI = item
- recent.MimeType = mimetype
- recent.Timestamp = int(time.time())
- recent.Private = private
- recent.Groups = groups
-
- self.sort()
-
- def deleteFile(self, item):
- if item in self.RecentFiles:
- self.RecentFiles.remove(item)
-
- def sort(self):
- self.RecentFiles.sort()
- self.RecentFiles.reverse()
+ def __init__(self):
+ self.RecentFiles = []
+ self.filename = ""
+
+ def parse(self, filename=None):
+ if not filename:
+ filename = os.path.join(os.getenv("HOME"), ".recently-used")
+
+ try:
+ doc = xml.dom.minidom.parse(filename)
+ except IOError:
+ raise ParsingError('File not found', filename)
+ except xml.parsers.expat.ExpatError:
+ raise ParsingError('Not a valid .menu file', filename)
+
+ self.filename = filename
+
+ for child in doc.childNodes:
+ if child.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if child.tagName == "RecentFiles":
+ for recent in child.childNodes:
+ if recent.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if recent.tagName == "RecentItem":
+ self.__parseRecentItem(recent)
+
+ self.sort()
+
+ def __parseRecentItem(self, item):
+ recent = RecentFile()
+ self.RecentFiles.append(recent)
+
+ for attribute in item.childNodes:
+ if attribute.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if attribute.tagName == "URI":
+ recent.URI = attribute.childNodes[0].nodeValue
+ elif attribute.tagName == "Mime-Type":
+ recent.MimeType = attribute.childNodes[0].nodeValue
+ elif attribute.tagName == "Timestamp":
+ recent.Timestamp = attribute.childNodes[0].nodeValue
+ elif attribute.tagName == "Private":
+ recent.Prviate = True
+ elif attribute.tagName == "Groups":
+
+ for group in attribute.childNodes:
+ if group.nodeType == xml.dom.Node.ELEMENT_NODE:
+ if group.tagName == "Group":
+ recent.Groups.append(group.childNodes[0].nodeValue)
+
+ def write(self, filename=None):
+ if not filename and not self.filename:
+ raise ParsingError('File not found', filename)
+ elif not filename:
+ filename = self.filename
+
+ f = open(filename, "w")
+ fcntl.lockf(f, fcntl.LOCK_EX)
+ f.write('<?xml version="1.0"?>\n')
+ f.write("<RecentFiles>\n")
+
+ for r in self.RecentFiles:
+ f.write(" <RecentItem>\n")
+ f.write(" <URI>%s</URI>\n" % xml.sax.saxutils.escape(r.URI))
+ f.write(" <Mime-Type>%s</Mime-Type>\n" % r.MimeType)
+ f.write(" <Timestamp>%s</Timestamp>\n" % r.Timestamp)
+ if r.Private == True:
+ f.write(" <Private/>\n")
+ if len(r.Groups) > 0:
+ f.write(" <Groups>\n")
+ for group in r.Groups:
+ f.write(" <Group>%s</Group>\n" % group)
+ f.write(" </Groups>\n")
+ f.write(" </RecentItem>\n")
+
+ f.write("</RecentFiles>\n")
+ fcntl.lockf(f, fcntl.LOCK_UN)
+ f.close()
+
+ def getFiles(self, mimetypes=None, groups=None, limit=0):
+ tmp = []
+ i = 0
+ for item in self.RecentFiles:
+ if groups:
+ for group in groups:
+ if group in item.Groups:
+ tmp.append(item)
+ i += 1
+ elif mimetypes:
+ for mimetype in mimetypes:
+ if mimetype == item.MimeType:
+ tmp.append(item)
+ i += 1
+ else:
+ if item.Private == False:
+ tmp.append(item)
+ i += 1
+ if limit != 0 and i == limit:
+ break
+
+ return tmp
+
+ def addFile(self, item, mimetype, groups=None, private=False):
+ # check if entry already there
+ if item in self.RecentFiles:
+ index = self.RecentFiles.index(item)
+ recent = self.RecentFiles[index]
+ else:
+ # delete if more then 500 files
+ if len(self.RecentFiles) == 500:
+ self.RecentFiles.pop()
+ # add entry
+ recent = RecentFile()
+ self.RecentFiles.append(recent)
+
+ recent.URI = item
+ recent.MimeType = mimetype
+ recent.Timestamp = int(time.time())
+ recent.Private = private
+ recent.Groups = groups
+
+ self.sort()
+
+ def deleteFile(self, item):
+ if item in self.RecentFiles:
+ self.RecentFiles.remove(item)
+
+ def sort(self):
+ self.RecentFiles.sort()
+ self.RecentFiles.reverse()
class RecentFile:
- def __init__(self):
- self.URI = ""
- self.MimeType = ""
- self.Timestamp = ""
- self.Private = False
- self.Groups = []
-
- def __cmp__(self, other):
- return cmp(self.Timestamp, other.Timestamp)
-
- def __eq__(self, other):
- if self.URI == str(other):
- return True
- else:
- return False
-
- def __str__(self):
- return self.URI
+ def __init__(self):
+ self.URI = ""
+ self.MimeType = ""
+ self.Timestamp = ""
+ self.Private = False
+ self.Groups = []
+
+ def __cmp__(self, other):
+ return cmp(self.Timestamp, other.Timestamp)
+
+ def __eq__(self, other):
+ if self.URI == str(other):
+ return True
+ else:
+ return False
+
+ def __str__(self):
+ return self.URI