+++ /dev/null
-"""
-Implementation of the XDG Menu Specification Version 1.0.draft-1
-http://standards.freedesktop.org/menu-spec/
-"""
-
-from __future__ import generators
-import locale, os, xml.dom.minidom
-
-from xdg.BaseDirectory import *
-from xdg.DesktopEntry import *
-from xdg.Exceptions import *
-
-import xdg.Locale
-import xdg.Config
-
-ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE
-
-# for python <= 2.3
-try:
- reversed = reversed
-except NameError:
- def reversed(x):
- return x[::-1]
-
-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
-
-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
-
-
-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])
-
-
-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("""
-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
- 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 += ")"
-
-
-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
-
-
-class Separator:
- "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
-
- def __str__(self):
- return self.Name
-
-
-tmp = {}
-
-def __getFileName(filename):
- 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
-
-def parse(filename=None):
- # conver to absolute path
- if filename and not os.path.isabs(filename):
- filename = __getFileName(filename)
-
- # use default if no filename given
- if not filename:
- candidate = os.environ.get('XDG_MENU_PREFIX', '') + "applications.menu"
- filename = __getFileName(candidate)
-
- if not filename:
- raise ParsingError('File not found', "/etc/xdg/menus/%s" % candidate)
-
- # 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)
-
- # parse menufile
- tmp["Root"] = ""
- tmp["mergeFiles"] = []
- tmp["DirectoryDirs"] = []
- tmp["cache"] = MenuEntryCache()
-
- __parse(doc, filename, tmp["Root"])
- __parsemove(tmp["Root"])
- __postparse(tmp["Root"])
-
- tmp["Root"].Doc = doc
- tmp["Root"].Filename = filename
-
- # generate the menu
- __genmenuNotOnlyAllocated(tmp["Root"])
- __genmenuOnlyAllocated(tmp["Root"])
-
- # and finally sort
- sort(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)
-
-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)
-
-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
-
-
-# Menu parsing stuff
-def __parseMenu(child, filename, parent):
- 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)
-
- if not os.path.isabs(value):
- value = os.path.join(path, 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
-
-# App/Directory Dir Stuff
-def __parseAppDir(value, filename, parent):
- 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)
-
-def __parseDirectoryDir(value, filename, parent):
- 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)
-
-# 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)
-
-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
-
-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)
-
-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
-
-# Legacy Dir Stuff
-def __parseLegacyDir(dir, prefix, filename, parent):
- 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
-
-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
-
-# 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
-
-# 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)
-
-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)
-
-# 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)
-
-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
-
-# 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)
-
-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