diff options
author | Stefano Karapetsas <[email protected]> | 2012-01-12 18:43:33 +0100 |
---|---|---|
committer | Stefano Karapetsas <[email protected]> | 2012-01-12 18:43:33 +0100 |
commit | b3e60df70d30451437b045b1d4c0d456da80d844 (patch) | |
tree | 6de108529b874d43545b7b1160f009802c3ce298 /Mozo/MenuEditor.py | |
parent | e87fbefcec0c44579495b1bcb22ac59814029566 (diff) | |
download | mozo-b3e60df70d30451437b045b1d4c0d456da80d844.tar.bz2 mozo-b3e60df70d30451437b045b1d4c0d456da80d844.tar.xz |
Alacarte => Mozo
Diffstat (limited to 'Mozo/MenuEditor.py')
-rw-r--r-- | Mozo/MenuEditor.py | 775 |
1 files changed, 775 insertions, 0 deletions
diff --git a/Mozo/MenuEditor.py b/Mozo/MenuEditor.py new file mode 100644 index 0000000..d74cb2b --- /dev/null +++ b/Mozo/MenuEditor.py @@ -0,0 +1,775 @@ +# -*- coding: utf-8 -*- +# Mozo Menu Editor - Simple fd.o Compliant Menu Editor +# Copyright (C) 2006 Travis Watkins, Heinrich Wendel +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os, re, xml.dom.minidom, locale +import matemenu +from Mozo import util + +class Menu: + tree = None + visible_tree = None + path = None + dom = None + +class MenuEditor: + #lists for undo/redo functionality + __undo = [] + __redo = [] + + def __init__(self): + self.locale = locale.getdefaultlocale()[0] + self.__loadMenus() + + def __loadMenus(self): + self.applications = Menu() + self.applications.tree = matemenu.lookup_tree('mate-applications.menu', matemenu.FLAGS_SHOW_EMPTY|matemenu.FLAGS_INCLUDE_EXCLUDED|matemenu.FLAGS_INCLUDE_NODISPLAY|matemenu.FLAGS_SHOW_ALL_SEPARATORS) + self.applications.visible_tree = matemenu.lookup_tree('mate-applications.menu') + self.applications.tree.sort_key = matemenu.SORT_DISPLAY_NAME + self.applications.visible_tree.sort_key = matemenu.SORT_DISPLAY_NAME + self.applications.path = os.path.join(util.getUserMenuPath(), self.applications.tree.get_menu_file()) + if not os.path.isfile(self.applications.path): + self.applications.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.applications.tree)) + else: + self.applications.dom = xml.dom.minidom.parse(self.applications.path) + self.__remove_whilespace_nodes(self.applications.dom) + + self.settings = Menu() + self.settings.tree = matemenu.lookup_tree('mate-settings.menu', matemenu.FLAGS_SHOW_EMPTY|matemenu.FLAGS_INCLUDE_EXCLUDED|matemenu.FLAGS_INCLUDE_NODISPLAY|matemenu.FLAGS_SHOW_ALL_SEPARATORS) + self.settings.visible_tree = matemenu.lookup_tree('mate-settings.menu') + self.settings.tree.sort_key = matemenu.SORT_DISPLAY_NAME + self.settings.visible_tree.sort_key = matemenu.SORT_DISPLAY_NAME + self.settings.path = os.path.join(util.getUserMenuPath(), self.settings.tree.get_menu_file()) + if not os.path.isfile(self.settings.path): + self.settings.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.settings.tree)) + else: + self.settings.dom = xml.dom.minidom.parse(self.settings.path) + self.__remove_whilespace_nodes(self.settings.dom) + + self.save(True) + + def save(self, from_loading=False): + for menu in ('applications', 'settings'): + fd = open(getattr(self, menu).path, 'w') + fd.write(re.sub("\n[\s]*([^\n<]*)\n[\s]*</", "\\1</", getattr(self, menu).dom.toprettyxml().replace('<?xml version="1.0" ?>\n', ''))) + fd.close() + if not from_loading: + self.__loadMenus() + + def quit(self): + for file_name in os.listdir(util.getUserItemPath()): + if file_name[-6:-2] in ('redo', 'undo'): + file_path = os.path.join(util.getUserItemPath(), file_name) + os.unlink(file_path) + for file_name in os.listdir(util.getUserDirectoryPath()): + if file_name[-6:-2] in ('redo', 'undo'): + file_path = os.path.join(util.getUserDirectoryPath(), file_name) + os.unlink(file_path) + for file_name in os.listdir(util.getUserMenuPath()): + if file_name[-6:-2] in ('redo', 'undo'): + file_path = os.path.join(util.getUserMenuPath(), file_name) + os.unlink(file_path) + + def revert(self): + for name in ('applications', 'settings'): + menu = getattr(self, name) + self.revertTree(menu.tree.root) + path = os.path.join(util.getUserMenuPath(), menu.tree.get_menu_file()) + try: + os.unlink(path) + except OSError: + pass + #reload DOM for each menu + if not os.path.isfile(menu.path): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + else: + menu.dom = xml.dom.minidom.parse(menu.path) + self.__remove_whilespace_nodes(menu.dom) + #reset undo/redo, no way to recover from this + self.__undo, self.__redo = [], [] + self.save() + + def revertTree(self, menu): + for child in menu.get_contents(): + if child.get_type() == matemenu.TYPE_DIRECTORY: + self.revertTree(child) + elif child.get_type() == matemenu.TYPE_ENTRY: + self.revertItem(child) + self.revertMenu(menu) + + def undo(self): + if len(self.__undo) == 0: + return + files = self.__undo.pop() + redo = [] + for file_path in files: + new_path = file_path.rsplit('.', 1)[0] + redo_path = util.getUniqueRedoFile(new_path) + data = open(new_path).read() + open(redo_path, 'w').write(data) + data = open(file_path).read() + open(new_path, 'w').write(data) + os.unlink(file_path) + redo.append(redo_path) + #reload DOM to make changes stick + for name in ('applications', 'settings'): + menu = getattr(self, name) + if not os.path.isfile(menu.path): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + else: + menu.dom = xml.dom.minidom.parse(menu.path) + self.__remove_whilespace_nodes(menu.dom) + self.__redo.append(redo) + + def redo(self): + if len(self.__redo) == 0: + return + files = self.__redo.pop() + undo = [] + for file_path in files: + new_path = file_path.rsplit('.', 1)[0] + undo_path = util.getUniqueUndoFile(new_path) + data = open(new_path).read() + open(undo_path, 'w').write(data) + data = open(file_path).read() + open(new_path, 'w').write(data) + os.unlink(file_path) + undo.append(undo_path) + #reload DOM to make changes stick + for name in ('applications', 'settings'): + menu = getattr(self, name) + if not os.path.isfile(menu.path): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + else: + menu.dom = xml.dom.minidom.parse(menu.path) + self.__remove_whilespace_nodes(menu.dom) + self.__undo.append(undo) + + def getMenus(self, parent=None): + if parent == None: + yield self.applications.tree.root + yield self.settings.tree.root + else: + for menu in parent.get_contents(): + if menu.get_type() == matemenu.TYPE_DIRECTORY: + yield (menu, self.__isVisible(menu)) + + def getItems(self, menu): + for item in menu.get_contents(): + if item.get_type() == matemenu.TYPE_SEPARATOR: + yield (item, True) + else: + if item.get_type() == matemenu.TYPE_ENTRY and item.get_desktop_file_id()[-19:] == '-usercustom.desktop': + continue + yield (item, self.__isVisible(item)) + + def canRevert(self, item): + if item.get_type() == matemenu.TYPE_ENTRY: + if util.getItemPath(item.get_desktop_file_id()): + path = util.getUserItemPath() + if os.path.isfile(os.path.join(path, item.get_desktop_file_id())): + return True + elif item.get_type() == matemenu.TYPE_DIRECTORY: + if item.get_desktop_file_path(): + file_id = os.path.split(item.get_desktop_file_path())[1] + else: + file_id = item.get_menu_id() + '.directory' + if util.getDirectoryPath(file_id): + path = util.getUserDirectoryPath() + if os.path.isfile(os.path.join(path, file_id)): + return True + return False + + def setVisible(self, item, visible): + dom = self.__getMenu(item).dom + if item.get_type() == matemenu.TYPE_ENTRY: + self.__addUndo([self.__getMenu(item), item]) + menu_xml = self.__getXmlMenu(self.__getPath(item.get_parent()), dom, dom) + if visible: + self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Include') + self.__writeItem(item, no_display=False) + else: + self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Exclude') + self.__addXmlTextElement(menu_xml, 'AppDir', util.getUserItemPath(), dom) + elif item.get_type() == matemenu.TYPE_DIRECTORY: + self.__addUndo([self.__getMenu(item), item]) + #don't mess with it if it's empty + if len(item.get_contents()) == 0: + return + menu_xml = self.__getXmlMenu(self.__getPath(item), dom, dom) + for node in self.__getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): + node.parentNode.removeChild(node) + if visible: + self.__writeMenu(item, no_display=False) + else: + self.__writeMenu(item, no_display=True) + self.__addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) + self.save() + + def createItem(self, parent, icon, name, comment, command, use_term, before=None, after=None): + file_id = self.__writeItem(None, icon, name, comment, command, use_term) + self.insertExternalItem(file_id, parent.menu_id, before, after) + + def insertExternalItem(self, file_id, parent_id, before=None, after=None): + parent = self.__findMenu(parent_id) + dom = self.__getMenu(parent).dom + self.__addItem(parent, file_id, dom) + self.__positionItem(parent, ('Item', file_id), before, after) + self.__addUndo([self.__getMenu(parent), ('Item', file_id)]) + self.save() + + def createMenu(self, parent, icon, name, comment, before=None, after=None): + file_id = self.__writeMenu(None, icon, name, comment) + self.insertExternalMenu(file_id, parent.menu_id, before, after) + + def insertExternalMenu(self, file_id, parent_id, before=None, after=None): + menu_id = file_id.rsplit('.', 1)[0] + parent = self.__findMenu(parent_id) + dom = self.__getMenu(parent).dom + self.__addXmlDefaultLayout(self.__getXmlMenu(self.__getPath(parent), dom, dom) , dom) + menu_xml = self.__getXmlMenu(self.__getPath(parent) + '/' + menu_id, dom, dom) + self.__addXmlTextElement(menu_xml, 'Directory', file_id, dom) + self.__positionItem(parent, ('Menu', menu_id), before, after) + self.__addUndo([self.__getMenu(parent), ('Menu', file_id)]) + self.save() + + def createSeparator(self, parent, before=None, after=None): + self.__positionItem(parent, ('Separator',), before, after) + self.__addUndo([self.__getMenu(parent), ('Separator',)]) + self.save() + + def editItem(self, item, icon, name, comment, command, use_term, parent=None, final=True): + #if nothing changed don't make a user copy + if icon == item.get_icon() and name == item.get_display_name() and comment == item.get_comment() and command == item.get_exec() and use_term == item.get_launch_in_terminal(): + return + #hack, item.get_parent() seems to fail a lot + if not parent: + parent = item.get_parent() + if final: + self.__addUndo([self.__getMenu(parent), item]) + self.__writeItem(item, icon, name, comment, command, use_term) + if final: + dom = self.__getMenu(parent).dom + menu_xml = self.__getXmlMenu(self.__getPath(parent), dom, dom) + self.__addXmlTextElement(menu_xml, 'AppDir', util.getUserItemPath(), dom) + self.save() + + def editMenu(self, menu, icon, name, comment, final=True): + #if nothing changed don't make a user copy + if icon == menu.get_icon() and name == menu.get_name() and comment == menu.get_comment(): + return + #we don't use this, we just need to make sure the <Menu> exists + #otherwise changes won't show up + dom = self.__getMenu(menu).dom + menu_xml = self.__getXmlMenu(self.__getPath(menu), dom, dom) + file_id = self.__writeMenu(menu, icon, name, comment) + if final: + self.__addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) + self.__addUndo([self.__getMenu(menu), menu]) + self.save() + + def copyItem(self, item, new_parent, before=None, after=None): + dom = self.__getMenu(new_parent).dom + file_path = item.get_desktop_file_path() + keyfile = util.DesktopParser(file_path) + #erase Categories in new file + keyfile.set('Categories', ('',)) + keyfile.set('Hidden', False) + file_id = util.getUniqueFileId(item.get_name(), '.desktop') + out_path = os.path.join(util.getUserItemPath(), file_id) + keyfile.write(open(out_path, 'w')) + self.__addItem(new_parent, file_id, dom) + self.__positionItem(new_parent, ('Item', file_id), before, after) + self.__addUndo([self.__getMenu(new_parent), ('Item', file_id)]) + self.save() + return file_id + + def moveItem(self, item, new_parent, before=None, after=None): + undo = [] + if item.get_parent() != new_parent: + #hide old item + self.deleteItem(item) + undo.append(item) + file_id = self.copyItem(item, new_parent) + item = ('Item', file_id) + undo.append(item) + self.__positionItem(new_parent, item, before, after) + undo.append(self.__getMenu(new_parent)) + self.__addUndo(undo) + self.save() + + def moveMenu(self, menu, new_parent, before=None, after=None): + parent = new_parent + #don't move a menu into it's child + while parent.get_parent(): + parent = parent.get_parent() + if parent == menu: + return False + + #don't move a menu into itself + if new_parent == menu: + return False + + #can't move between top-level menus + if self.__getMenu(menu) != self.__getMenu(new_parent): + return False + if menu.get_parent() != new_parent: + dom = self.__getMenu(menu).dom + root_path = self.__getPath(menu).split('/', 1)[0] + xml_root = self.__getXmlMenu(root_path, dom, dom) + old_path = self.__getPath(menu).split('/', 1)[1] + #root menu's path has no / + if '/' in self.__getPath(new_parent): + new_path = self.__getPath(new_parent).split('/', 1)[1] + '/' + menu.get_menu_id() + else: + new_path = menu.get_menu_id() + self.__addXmlMove(xml_root, old_path, new_path, dom) + self.__positionItem(new_parent, menu, before, after) + self.__addUndo([self.__getMenu(new_parent),]) + self.save() + + def moveSeparator(self, separator, new_parent, before=None, after=None): + self.__positionItem(new_parent, separator, before, after) + self.__addUndo([self.__getMenu(new_parent),]) + self.save() + + def deleteItem(self, item): + self.__writeItem(item, hidden=True) + self.__addUndo([item,]) + self.save() + + def deleteMenu(self, menu): + dom = self.__getMenu(menu).dom + menu_xml = self.__getXmlMenu(self.__getPath(menu), dom, dom) + self.__addDeleted(menu_xml, dom) + self.__addUndo([self.__getMenu(menu),]) + self.save() + + def deleteSeparator(self, item): + parent = item.get_parent() + contents = parent.get_contents() + contents.remove(item) + layout = self.__createLayout(contents) + dom = self.__getMenu(parent).dom + menu_xml = self.__getXmlMenu(self.__getPath(parent), dom, dom) + self.__addXmlLayout(menu_xml, layout, dom) + self.__addUndo([self.__getMenu(item.get_parent()),]) + self.save() + + def revertItem(self, item): + if not self.canRevert(item): + return + self.__addUndo([item,]) + try: + os.remove(item.get_desktop_file_path()) + except OSError: + pass + self.save() + + def revertMenu(self, menu): + if not self.canRevert(menu): + return + #wtf happened here? oh well, just bail + if not menu.get_desktop_file_path(): + return + self.__addUndo([menu,]) + file_id = os.path.split(menu.get_desktop_file_path())[1] + path = os.path.join(util.getUserDirectoryPath(), file_id) + try: + os.remove(path) + except OSError: + pass + self.save() + + #private stuff + def __addUndo(self, items): + self.__undo.append([]) + for item in items: + if isinstance(item, Menu): + file_path = item.path + elif isinstance(item, tuple): + if item[0] == 'Item': + file_path = os.path.join(util.getUserItemPath(), item[1]) + if not os.path.isfile(file_path): + file_path = util.getItemPath(item[1]) + elif item[0] == 'Menu': + file_path = os.path.join(util.getUserDirectoryPath(), item[1]) + if not os.path.isfile(file_path): + file_path = util.getDirectoryPath(item[1]) + else: + continue + elif item.get_type() == matemenu.TYPE_DIRECTORY: + if item.get_desktop_file_path() == None: + continue + file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1]) + if not os.path.isfile(file_path): + file_path = item.get_desktop_file_path() + elif item.get_type() == matemenu.TYPE_ENTRY: + file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id()) + if not os.path.isfile(file_path): + file_path = item.get_desktop_file_path() + else: + continue + data = open(file_path).read() + undo_path = util.getUniqueUndoFile(file_path) + open(undo_path, 'w').write(data) + self.__undo[-1].append(undo_path) + + def __getMenu(self, item): + root = item.get_parent() + if not root: + #already at the top + root = item + else: + while True: + if root.get_parent(): + root = root.get_parent() + else: + break + if root.menu_id == self.applications.tree.root.menu_id: + return self.applications + return self.settings + + def __findMenu(self, menu_id, parent=None): + if parent == None: + menu = self.__findMenu(menu_id, self.applications.tree.root) + if menu != None: + return menu + else: + return self.__findMenu(menu_id, self.settings.tree.root) + if menu_id == self.applications.tree.root.menu_id: + return self.applications.tree.root + if menu_id == self.settings.tree.root.menu_id: + return self.settings.tree.root + for item in parent.get_contents(): + if item.get_type() == matemenu.TYPE_DIRECTORY: + if item.menu_id == menu_id: + return item + menu = self.__findMenu(menu_id, item) + if menu != None: + return menu + + def __isVisible(self, item): + if item.get_type() == matemenu.TYPE_ENTRY: + return not (item.get_is_excluded() or item.get_is_nodisplay()) + menu = self.__getMenu(item) + if menu == self.applications: + root = self.applications.visible_tree.root + elif menu == self.settings: + root = self.settings.visible_tree.root + if item.get_type() == matemenu.TYPE_DIRECTORY: + if self.__findMenu(item.menu_id, root) == None: + return False + return True + + def __getPath(self, menu, path=None): + if not path: + path = menu.tree.root.get_menu_id() + if menu.get_parent(): + path = self.__getPath(menu.get_parent(), path) + path += '/' + path += menu.menu_id + return path + + def __getXmlMenu(self, path, element, dom): + if '/' in path: + (name, path) = path.split('/', 1) + else: + name = path + path = '' + + found = None + for node in self.__getXmlNodesByName('Menu', element): + for child in self.__getXmlNodesByName('Name', node): + if child.childNodes[0].nodeValue == name: + if path: + found = self.__getXmlMenu(path, node, dom) + else: + found = node + break + if found: + break + if not found: + node = self.__addXmlMenuElement(element, name, dom) + if path: + found = self.__getXmlMenu(path, node, dom) + else: + found = node + + return found + + def __addXmlMenuElement(self, element, name, dom): + node = dom.createElement('Menu') + self.__addXmlTextElement(node, 'Name', name, dom) + return element.appendChild(node) + + def __addXmlTextElement(self, element, name, text, dom): + for temp in element.childNodes: + if temp.nodeName == name: + if temp.childNodes[0].nodeValue == text: + return + node = dom.createElement(name) + text = dom.createTextNode(text) + node.appendChild(text) + return element.appendChild(node) + + def __addXmlFilename(self, element, dom, filename, type = 'Include'): + # remove old filenames + for node in self.__getXmlNodesByName(['Include', 'Exclude'], element): + if node.childNodes[0].nodeName == 'Filename' and node.childNodes[0].childNodes[0].nodeValue == filename: + element.removeChild(node) + + # add new filename + node = dom.createElement(type) + node.appendChild(self.__addXmlTextElement(node, 'Filename', filename, dom)) + return element.appendChild(node) + + def __addDeleted(self, element, dom): + node = dom.createElement('Deleted') + return element.appendChild(node) + + def __writeItem(self, item=None, icon=None, name=None, comment=None, command=None, use_term=None, no_display=None, startup_notify=None, hidden=None): + if item: + file_path = item.get_desktop_file_path() + file_id = item.get_desktop_file_id() + keyfile = util.DesktopParser(file_path) + elif item == None and name == None: + raise Exception('New menu items need a name') + else: + file_id = util.getUniqueFileId(name, '.desktop') + keyfile = util.DesktopParser() + if icon: + keyfile.set('Icon', icon) + keyfile.set('Icon', icon, self.locale) + if name: + keyfile.set('Name', name) + keyfile.set('Name', name, self.locale) + if comment: + keyfile.set('Comment', comment) + keyfile.set('Comment', comment, self.locale) + if command: + keyfile.set('Exec', command) + if use_term != None: + keyfile.set('Terminal', use_term) + if no_display != None: + keyfile.set('NoDisplay', no_display) + if startup_notify != None: + keyfile.set('StartupNotify', startup_notify) + if hidden != None: + keyfile.set('Hidden', hidden) + out_path = os.path.join(util.getUserItemPath(), file_id) + keyfile.write(open(out_path, 'w')) + return file_id + + def __writeMenu(self, menu=None, icon=None, name=None, comment=None, no_display=None): + if menu: + file_id = os.path.split(menu.get_desktop_file_path())[1] + file_path = menu.get_desktop_file_path() + keyfile = util.DesktopParser(file_path) + elif menu == None and name == None: + raise Exception('New menus need a name') + else: + file_id = util.getUniqueFileId(name, '.directory') + keyfile = util.DesktopParser(file_type='Directory') + if icon: + keyfile.set('Icon', icon) + if name: + keyfile.set('Name', name) + keyfile.set('Name', name, self.locale) + if comment: + keyfile.set('Comment', comment) + keyfile.set('Comment', comment, self.locale) + if no_display != None: + keyfile.set('NoDisplay', no_display) + out_path = os.path.join(util.getUserDirectoryPath(), file_id) + keyfile.write(open(out_path, 'w')) + return file_id + + def __getXmlNodesByName(self, name, element): + for child in element.childNodes: + if child.nodeType == xml.dom.Node.ELEMENT_NODE: + if isinstance(name, str) and child.nodeName == name: + yield child + elif isinstance(name, list) or isinstance(name, tuple): + if child.nodeName in name: + yield child + + def __remove_whilespace_nodes(self, node): + remove_list = [] + for child in node.childNodes: + if child.nodeType == xml.dom.minidom.Node.TEXT_NODE: + child.data = child.data.strip() + if not child.data.strip(): + remove_list.append(child) + elif child.hasChildNodes(): + self.__remove_whilespace_nodes(child) + for node in remove_list: + node.parentNode.removeChild(node) + + def __addXmlMove(self, element, old, new, dom): + if not self.__undoMoves(element, old, new, dom): + node = dom.createElement('Move') + node.appendChild(self.__addXmlTextElement(node, 'Old', old, dom)) + node.appendChild(self.__addXmlTextElement(node, 'New', new, dom)) + #are parsed in reverse order, need to put at the beginning + return element.insertBefore(node, element.firstChild) + + def __addXmlLayout(self, element, layout, dom): + # remove old layout + for node in self.__getXmlNodesByName('Layout', element): + element.removeChild(node) + + # add new layout + node = dom.createElement('Layout') + for order in layout.order: + if order[0] == 'Separator': + child = dom.createElement('Separator') + node.appendChild(child) + elif order[0] == 'Filename': + child = self.__addXmlTextElement(node, 'Filename', order[1], dom) + elif order[0] == 'Menuname': + child = self.__addXmlTextElement(node, 'Menuname', order[1], dom) + elif order[0] == 'Merge': + child = dom.createElement('Merge') + child.setAttribute('type', order[1]) + node.appendChild(child) + return element.appendChild(node) + + def __addXmlDefaultLayout(self, element, dom): + # remove old default layout + for node in self.__getXmlNodesByName('DefaultLayout', element): + element.removeChild(node) + + # add new layout + node = dom.createElement('DefaultLayout') + node.setAttribute('inline', 'false') + return element.appendChild(node) + + def __createLayout(self, items): + layout = Layout() + layout.order = [] + + layout.order.append(['Merge', 'menus']) + for item in items: + if isinstance(item, tuple): + if item[0] == 'Separator': + layout.parseSeparator() + elif item[0] == 'Menu': + layout.parseMenuname(item[1]) + elif item[0] == 'Item': + layout.parseFilename(item[1]) + elif item.get_type() == matemenu.TYPE_DIRECTORY: + layout.parseMenuname(item.get_menu_id()) + elif item.get_type() == matemenu.TYPE_ENTRY: + layout.parseFilename(item.get_desktop_file_id()) + elif item.get_type() == matemenu.TYPE_SEPARATOR: + layout.parseSeparator() + layout.order.append(['Merge', 'files']) + return layout + + def __addItem(self, parent, file_id, dom): + xml_parent = self.__getXmlMenu(self.__getPath(parent), dom, dom) + self.__addXmlFilename(xml_parent, dom, file_id, 'Include') + + def __deleteItem(self, parent, file_id, dom, before=None, after=None): + xml_parent = self.__getXmlMenu(self.__getPath(parent), dom, dom) + self.__addXmlFilename(xml_parent, dom, file_id, 'Exclude') + + def __positionItem(self, parent, item, before=None, after=None): + if not before and not after: + return + if after: + index = parent.contents.index(after) + 1 + elif before: + index = parent.contents.index(before) + contents = parent.contents + #if this is a move to a new parent you can't remove the item + try: + contents.remove(item) + except: + pass + contents.insert(index, item) + layout = self.__createLayout(contents) + dom = self.__getMenu(parent).dom + menu_xml = self.__getXmlMenu(self.__getPath(parent), dom, dom) + self.__addXmlLayout(menu_xml, layout, dom) + + def __undoMoves(self, element, old, new, dom): + nodes = [] + matches = [] + original_old = old + final_old = old + #get all <Move> elements + for node in self.__getXmlNodesByName(['Move'], element): + nodes.insert(0, node) + #if the <New> matches our old parent we've found a stage to undo + for node in nodes: + xml_old = node.getElementsByTagName('Old')[0] + xml_new = node.getElementsByTagName('New')[0] + if xml_new.childNodes[0].nodeValue == old: + matches.append(node) + #we should end up with this path when completed + final_old = xml_old.childNodes[0].nodeValue + #undoing <Move>s + for node in matches: + element.removeChild(node) + if len(matches) > 0: + for node in nodes: + xml_old = node.getElementsByTagName('Old')[0] + xml_new = node.getElementsByTagName('New')[0] + path = os.path.split(xml_new.childNodes[0].nodeValue) + if path[0] == original_old: + element.removeChild(node) + for node in dom.getElementsByTagName('Menu'): + name_node = node.getElementsByTagName('Name')[0] + name = name_node.childNodes[0].nodeValue + if name == os.path.split(new)[1]: + #copy app and dir directory info from old <Menu> + root_path = dom.getElementsByTagName('Menu')[0].getElementsByTagName('Name')[0].childNodes[0].nodeValue + xml_menu = self.__getXmlMenu(root_path + '/' + new, dom, dom) + for app_dir in node.getElementsByTagName('AppDir'): + xml_menu.appendChild(app_dir) + for dir_dir in node.getElementsByTagName('DirectoryDir'): + xml_menu.appendChild(dir_dir) + parent = node.parentNode + parent.removeChild(node) + node = dom.createElement('Move') + node.appendChild(self.__addXmlTextElement(node, 'Old', xml_old.childNodes[0].nodeValue, dom)) + node.appendChild(self.__addXmlTextElement(node, 'New', os.path.join(new, path[1]), dom)) + element.appendChild(node) + if final_old == new: + return True + node = dom.createElement('Move') + node.appendChild(self.__addXmlTextElement(node, 'Old', final_old, dom)) + node.appendChild(self.__addXmlTextElement(node, 'New', new, dom)) + return element.appendChild(node) + +class Layout: + def __init__(self, node=None): + self.order = [] + + def parseMenuname(self, value): + self.order.append(['Menuname', value]) + + def parseSeparator(self): + self.order.append(['Separator']) + + def parseFilename(self, value): + self.order.append(['Filename', value]) + + def parseMerge(self, merge_type='all'): + self.order.append(['Merge', merge_type]) |