404

[ Avaa Bypassed ]




Upload:

Command:

botdev@18.188.236.178: ~ $
"""
Implementation of the XDG Menu Specification Version 1.0.draft-1
http://standards.freedesktop.org/menu-spec/

Example code:

from xdg.Menu import parse, Menu, MenuEntry

def print_menu(menu, tab=0):
  for submenu in menu.Entries:
    if isinstance(submenu, Menu):
      print ("\t" * tab) + unicode(submenu)
      print_menu(submenu, tab+1)
    elif isinstance(submenu, MenuEntry):
      print ("\t" * tab) + unicode(submenu.DesktopEntry)

print_menu(parse())
"""

import locale, os, xml.dom.minidom
import subprocess

from xdg.BaseDirectory import xdg_data_dirs, xdg_config_dirs
from xdg.DesktopEntry import DesktopEntry
from xdg.Exceptions import ParsingError, ValidationError, debug
from xdg.util import PY3

import xdg.Locale
import xdg.Config

ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE

def _strxfrm(s):
    """Wrapper around locale.strxfrm that accepts unicode strings on Python 2.
    
    See Python bug #2481.
    """
    if (not PY3) and isinstance(s, unicode):
        s = s.encode('utf-8')
    return locale.strxfrm(s)

class Menu:
    """Menu containing sub menus under menu.Entries

	Contains both Menu and MenuEntry items.
    """
    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 _key(self):
        """Key function for locale-aware sorting."""
        return _strxfrm(self.getName())
    
    def __lt__(self, other):
        try:
            other = other._key()
        except AttributeError:
            pass
        return self._key() < other

    def __eq__(self, other):
        try:
            return self.Name == unicode(other)
        except NameError:  # unicode() becomes str() in Python 3
            return self.Name == str(other)

    """ PUBLIC STUFF """
    def getEntries(self, hidden=False):
        """Interator for a list of Entries visible to the user."""
        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):
        """Searches for a MenuEntry with a given DesktopFileID."""
        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):
        """Searches for a Menu with a given 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):
        """Returns this menu's path in the menu structure."""
        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):
        """Returns the menu's localised name."""
        try:
            return self.Directory.DesktopEntry.getName()
        except AttributeError:
            return self.Name

    def getGenericName(self):
        """Returns the menu's generic name."""
        try:
            return self.Directory.DesktopEntry.getGenericName()
        except AttributeError:
            return ""

    def getComment(self):
        """Returns the menu's comment text."""
        try:
            return self.Directory.DesktopEntry.getComment()
        except AttributeError:
            return ""

    def getIcon(self):
        """Returns the menu's icon, filename or simple name"""
        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)

    def __str__(self):
        return self.Rule
    
    def do(self, menuentries, type, run):
        for menuentry in menuentries:
            if run == 2 and ( menuentry.MatchedInclude == True \
            or menuentry.Allocated == True ):
                continue
            elif eval(self.Rule):
                if type == "Include":
                    menuentry.Add = True
                    menuentry.MatchedInclude = True
                else:
                    menuentry.Add = False
        return menuentries

    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 == %r" % value.strip()

    def parseCategory(self, value):
        self.parseNew()
        self.Rule += "%r 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):
        """Save any changes to the desktop entry."""
        if self.DesktopEntry.tainted == True:
            self.DesktopEntry.write()

    def getDir(self):
        """Return the directory containing the desktop entry file."""
        return self.DesktopEntry.filename.replace(self.Filename, '')

    def getType(self):
        """Return the type of MenuEntry, 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 _key(self):
        """Key function for locale-aware sorting."""
        return _strxfrm(self.DesktopEntry.getName())
    
    def __lt__(self, other):
        try:
            other = other._key()
        except AttributeError:
            pass
        return self._key() < other
            

    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):
    """Load an applications.menu file.
    
    filename : str, optional
      The default is ``$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu``.
    """
    # convert to absolute path
    if filename and not os.path.isabs(filename):
        filename = __getFileName(filename)

    # use default if no filename given
    if not filename: 
        # Upstream's default, we leave it as default in case someone produces
        # their own applications.menu and is depending on it.
        candidate = os.environ.get('XDG_MENU_PREFIX', '') + "applications.menu"
        filename = __getFileName(candidate)
        # Since applications.menu isn't provided in Debian, we'll fallback to
        # debian.menu, typically in /etc/xdg/menus/debian-menu.menu
        # (Closes: #654978)
        if not filename:
            candidate = os.environ.get('XDG_MENU_PREFIX', '') + \
                                                            "debian-menu.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)
                m.Rules.append(r)
            if not dir in parent.AppDirs:
                categories.append("Legacy")
                menuentry.Categories = categories

        return m

def __parseKDELegacyDirs(filename, parent):
    try:
        proc = subprocess.Popen(['kde-config', '--path', 'apps'],
                                stdout=subprocess.PIPE, universal_newlines=True)
        output = proc.communicate()[0].splitlines()
    except OSError:
        # If kde-config doesn't exist, ignore this.
        return
    
    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 dir in self.cacheEntries:
                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

Filemanager

Name Type Size Permission Actions
__pycache__ Folder 0755
BaseDirectory.py File 5.62 KB 0644
Config.py File 728 B 0644
DesktopEntry.py File 16.41 KB 0644
Exceptions.py File 1.49 KB 0644
IconTheme.py File 15.72 KB 0644
IniFile.py File 13.18 KB 0644
Locale.py File 2.11 KB 0644
Menu.py File 38.14 KB 0644
MenuEditor.py File 17.97 KB 0644
Mime.py File 15.64 KB 0644
RecentFiles.py File 5.99 KB 0644
__init__.py File 171 B 0644
util.py File 164 B 0644