Viewing file: Menu.py (35.46 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
""" 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
|