diff options
author | yetist <[email protected]> | 2018-04-24 09:59:55 +0800 |
---|---|---|
committer | lukefromdc <[email protected]> | 2018-04-25 00:23:22 -0400 |
commit | 80873ff2ec320d0a4feffdec97e86d626e2cf7db (patch) | |
tree | 815c3687123700e980cd9f6c74b180a058c7dfe3 /Mozo/MenuEditor.py | |
parent | defd057d709f723c60a77ed86fb5e51c90784990 (diff) | |
download | mozo-80873ff2ec320d0a4feffdec97e86d626e2cf7db.tar.bz2 mozo-80873ff2ec320d0a4feffdec97e86d626e2cf7db.tar.xz |
just reindent python code
Diffstat (limited to 'Mozo/MenuEditor.py')
-rw-r--r-- | Mozo/MenuEditor.py | 1494 |
1 files changed, 747 insertions, 747 deletions
diff --git a/Mozo/MenuEditor.py b/Mozo/MenuEditor.py index 0bc7cc4..62832e6 100644 --- a/Mozo/MenuEditor.py +++ b/Mozo/MenuEditor.py @@ -27,758 +27,758 @@ from gi.repository import GLib from Mozo import util class Menu: - tree = None - visible_tree = None - path = None - dom = None + tree = None + visible_tree = None + path = None + dom = None class MenuEditor: - def __init__(self): - self.locale = locale.getdefaultlocale()[0] - self.__loadMenus() - self.__undo = [] - self.__redo = [] - - - 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()) - try: - self.applications.dom = xml.dom.minidom.parse(self.applications.path) - except (IOError, xml.parsers.expat.ExpatError): - self.applications.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.applications.tree)) - util.removeWhitespaceNodes(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()) - try: - self.settings.dom = xml.dom.minidom.parse(self.settings.path) - except (IOError, xml.parsers.expat.ExpatError): - self.settings.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.settings.tree)) - util.removeWhitespaceNodes(self.settings.dom) - - self.save(True) - - def save(self, from_loading=False): - for menu in ('applications', 'settings'): - with codecs.open(getattr(self, menu).path, 'w', 'utf-8') as f: - f.write(getattr(self, menu).dom.toprettyxml()) - 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 - try: - menu.dom = xml.dom.minidom.parse(menu.path) - except (IOError, xml.parsers.expat.ExpatError): - menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) - util.removeWhitespaceNodes(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 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() - - 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) - - f_file_path = codecs.open(new_path, 'r', 'utf-8') - f_new_path = codecs.open(new_path, 'rw', 'utf-8') - f_redo_path = codecs.open(redo_path, 'rw', 'utf-8') - - data = f_new_path.read() - f_redo_path.write(data) - data = f_file_path.read() - f_new_path.write(data) - - f_file_path.close() - f_new_path.close() - f_redo_path.close() - - os.unlink(file_path) - redo.append(redo_path) - # reload DOM to make changes stick - for name in ('applications', 'settings'): - menu = getattr(self, name) - try: - menu.dom = xml.dom.minidom.parse(menu.path) - except (IOError, xml.parsers.expat.ExpatError): - menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) - util.removeWhitespaceNodes(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) - f_file_path = codecs.open(new_path, 'r', 'utf-8') - f_new_path = codecs.open(new_path, 'rw', 'utf-8') - f_undo_path = codecs.open(undo_path, 'rw', 'utf-8') - data = f_new_path.read() - f_undo_path.write(data) - data = f_file_path.read() - f_new_path.write(data) - os.unlink(file_path) - undo.append(undo_path) - f_file_path.close() - f_new_path.close() - f_undo_path.close() - #reload DOM to make changes stick - for name in ('applications', 'settings'): - menu = getattr(self, name) - try: - menu.dom = xml.dom.minidom.parse(menu.path) - except (IOError, xml.parsers.expat.ExpatError): - menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) - util.removeWhitespaceNodes(menu.dom) - self.__undo.append(undo) - - def getMenus(self, parent=None): - if parent is 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()) is not None: - 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) is not None: - 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.documentElement, dom) - if visible: - self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Include') - self.__writeItem(item, NoDisplay=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.documentElement, dom) - for node in self.__getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): - node.parentNode.removeChild(node) - self.__writeMenu(item, NoDisplay=not visible) - self.__addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) - self.save() - - def createItem(self, parent, before, after, **kwargs): - file_id = self.__writeItem(None, **kwargs) - 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 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.documentElement, dom) , dom) - menu_xml = self.__getXmlMenu(self.__getPath(parent) + [menu_id], dom.documentElement, 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=icon, Name=name, Comment=comment, Exec=command, Terminal=use_term) - if final: - dom = self.__getMenu(parent).dom - menu_xml = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, 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.documentElement, dom) - self.__writeMenu(menu, Icon=icon, Name=name, Comment=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 = GLib.KeyFile() - keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) - - util.fillKeyFile(keyfile, dict(Categories=[], Hidden=False)) - - file_id = util.getUniqueFileId(item.get_name().replace(os.sep, '-'), '.desktop') - out_path = os.path.join(util.getUserItemPath(), file_id) - - contents, length = keyfile.to_data() - - with open(out_path, 'w') as f: - f.write(contents) - - 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 - path = self.__getPath(menu) - root_path = path[0] - xml_root = self.__getXmlMenu(root_path, dom.documentElement, dom) - old_path = path[1:] - new_path = self.__getPath(new_parent)[1:] + [menu.get_menu_id()] - self.__addXmlMove(xml_root, '/'.join(old_path), '/'.join(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): - undo = [] - # remove the original separator if its parent is not the new destination - if separator.get_parent() != new_parent: - self.deleteSeparator(separator) - undo.append(separator) - # this adds the new separator to the specified position - self.__positionItem(new_parent, separator, before, after) - undo.append(self.__getMenu(new_parent)) - self.__addUndo(undo) - self.save() - - def deleteItem(self, item): - self.__addUndo([item,]) - self.__writeItem(item, Hidden=True) - self.save() - - def deleteMenu(self, menu): - dom = self.__getMenu(menu).dom - menu_xml = self.__getXmlMenu(self.__getPath(menu), dom.documentElement, 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.documentElement, dom) - self.__addXmlLayout(menu_xml, layout, dom) - self.__addUndo([self.__getMenu(item.get_parent()),]) - 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() is 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 - with codecs.open(file_path, 'r', 'utf-8') as f: - data = f.read() - undo_path = util.getUniqueUndoFile(file_path) - with codecs.open(undo_path, 'w', 'utf-8') as f: - f.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 is None: - menu = self.__findMenu(menu_id, self.applications.tree.root) - if menu is not 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 is not 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) is None: - return False - return True - - def __getPath(self, menu): - names = [] - current = menu - while current is not None: - names.append(current.get_menu_id()) - current = current.get_parent() - - # XXX - don't append root menu name, mozo doesn't - # expect it. look into this more. - names.pop(-1) - return names[::-1] - - def __getXmlMenuPart(self, element, name): - for node in self.__getXmlNodesByName('Menu', element): - for child in self.__getXmlNodesByName('Name', node): - if child.childNodes[0].nodeValue == name: - return node - return None - - def __getXmlMenu(self, path, element, dom): - for name in path: - found = self.__getXmlMenuPart(element, name) - if found is not None: - element = found - else: - element = self.__addXmlMenuElement(element, name, dom) - return element - - def __addXmlMenuElement(self, element, name, dom): - if isinstance(name, bytes): - name = name.decode('utf-8') - - node = dom.createElement('Menu') - self.__addXmlTextElement(node, 'Name', name, dom) - return element.appendChild(node) - - def __addXmlTextElement(self, element, name, text, dom): - if isinstance(name, bytes): - name = name.decode('utf-8') - if isinstance(text, bytes): - text = text.decode('utf-8') - - 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'): - if isinstance(filename, bytes): - filename = filename.decode('utf-8') - - # 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 __makeKeyFile(self, file_path, kwargs): - if 'KeyFile' in kwargs: - return kwargs['KeyFile'] - - keyfile = GLib.KeyFile() - - if file_path is not None: - keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) - - util.fillKeyFile(keyfile, kwargs) - return keyfile - - def __writeItem(self, item, **kwargs): - if item is not None: - file_path = item.get_desktop_file_path() - else: - file_path = None - - keyfile = self.__makeKeyFile(file_path, kwargs) - - if item is not None: - file_id = item.get_desktop_file_id() - else: - file_id = util.getUniqueFileId(keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, 'Name'), '.desktop') - - contents, length = keyfile.to_data() - - with open(os.path.join(util.getUserItemPath(), file_id), 'w') as f: - f.write(contents) - return file_id - - def __writeMenu(self, menu, **kwargs): - if menu is not None: - file_id = os.path.split(menu.get_desktop_file_path())[1] - file_path = menu.get_desktop_file_path() - keyfile = GLib.KeyFile() - keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) - elif menu is None and 'Name' not in kwargs: - raise Exception('New menus need a name') - else: - file_id = util.getUniqueFileId(kwargs['Name'], '.directory') - keyfile = GLib.KeyFile() - - util.fillKeyFile(keyfile, kwargs) - - contents, length = keyfile.to_data() - - with open(os.path.join(util.getUserDirectoryPath(), file_id), 'w') as f: - f.write(contents) - 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 __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.documentElement, dom) - self.__addXmlFilename(xml_parent, dom, file_id, 'Include') - - def __positionItem(self, parent, item, before=None, after=None): - if after: - index = parent.contents.index(after) + 1 - elif before: - index = parent.contents.index(before) - else: - # append the item to the list - index = len(parent.contents) - contents = parent.contents - #if this is a move to a new parent you can't remove the item - if item in contents: - # decrease the destination index, if we shorten the list - if (before and (contents.index(item) < index)) \ - or (after and (contents.index(item) < index - 1)): - index -= 1 - contents.remove(item) - contents.insert(index, item) - layout = self.__createLayout(contents) - dom = self.__getMenu(parent).dom - menu_xml = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, 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.documentElement, 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) + def __init__(self): + self.locale = locale.getdefaultlocale()[0] + self.__loadMenus() + self.__undo = [] + self.__redo = [] + + + 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()) + try: + self.applications.dom = xml.dom.minidom.parse(self.applications.path) + except (IOError, xml.parsers.expat.ExpatError): + self.applications.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.applications.tree)) + util.removeWhitespaceNodes(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()) + try: + self.settings.dom = xml.dom.minidom.parse(self.settings.path) + except (IOError, xml.parsers.expat.ExpatError): + self.settings.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.settings.tree)) + util.removeWhitespaceNodes(self.settings.dom) + + self.save(True) + + def save(self, from_loading=False): + for menu in ('applications', 'settings'): + with codecs.open(getattr(self, menu).path, 'w', 'utf-8') as f: + f.write(getattr(self, menu).dom.toprettyxml()) + 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 + try: + menu.dom = xml.dom.minidom.parse(menu.path) + except (IOError, xml.parsers.expat.ExpatError): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + util.removeWhitespaceNodes(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 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() + + 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) + + f_file_path = codecs.open(new_path, 'r', 'utf-8') + f_new_path = codecs.open(new_path, 'rw', 'utf-8') + f_redo_path = codecs.open(redo_path, 'rw', 'utf-8') + + data = f_new_path.read() + f_redo_path.write(data) + data = f_file_path.read() + f_new_path.write(data) + + f_file_path.close() + f_new_path.close() + f_redo_path.close() + + os.unlink(file_path) + redo.append(redo_path) + # reload DOM to make changes stick + for name in ('applications', 'settings'): + menu = getattr(self, name) + try: + menu.dom = xml.dom.minidom.parse(menu.path) + except (IOError, xml.parsers.expat.ExpatError): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + util.removeWhitespaceNodes(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) + f_file_path = codecs.open(new_path, 'r', 'utf-8') + f_new_path = codecs.open(new_path, 'rw', 'utf-8') + f_undo_path = codecs.open(undo_path, 'rw', 'utf-8') + data = f_new_path.read() + f_undo_path.write(data) + data = f_file_path.read() + f_new_path.write(data) + os.unlink(file_path) + undo.append(undo_path) + f_file_path.close() + f_new_path.close() + f_undo_path.close() + #reload DOM to make changes stick + for name in ('applications', 'settings'): + menu = getattr(self, name) + try: + menu.dom = xml.dom.minidom.parse(menu.path) + except (IOError, xml.parsers.expat.ExpatError): + menu.dom = xml.dom.minidom.parseString(util.getUserMenuXml(menu.tree)) + util.removeWhitespaceNodes(menu.dom) + self.__undo.append(undo) + + def getMenus(self, parent=None): + if parent is 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()) is not None: + 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) is not None: + 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.documentElement, dom) + if visible: + self.__addXmlFilename(menu_xml, dom, item.get_desktop_file_id(), 'Include') + self.__writeItem(item, NoDisplay=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.documentElement, dom) + for node in self.__getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): + node.parentNode.removeChild(node) + self.__writeMenu(item, NoDisplay=not visible) + self.__addXmlTextElement(menu_xml, 'DirectoryDir', util.getUserDirectoryPath(), dom) + self.save() + + def createItem(self, parent, before, after, **kwargs): + file_id = self.__writeItem(None, **kwargs) + 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 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.documentElement, dom) , dom) + menu_xml = self.__getXmlMenu(self.__getPath(parent) + [menu_id], dom.documentElement, 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=icon, Name=name, Comment=comment, Exec=command, Terminal=use_term) + if final: + dom = self.__getMenu(parent).dom + menu_xml = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, 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.documentElement, dom) + self.__writeMenu(menu, Icon=icon, Name=name, Comment=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 = GLib.KeyFile() + keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) + + util.fillKeyFile(keyfile, dict(Categories=[], Hidden=False)) + + file_id = util.getUniqueFileId(item.get_name().replace(os.sep, '-'), '.desktop') + out_path = os.path.join(util.getUserItemPath(), file_id) + + contents, length = keyfile.to_data() + + with open(out_path, 'w') as f: + f.write(contents) + + 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 + path = self.__getPath(menu) + root_path = path[0] + xml_root = self.__getXmlMenu(root_path, dom.documentElement, dom) + old_path = path[1:] + new_path = self.__getPath(new_parent)[1:] + [menu.get_menu_id()] + self.__addXmlMove(xml_root, '/'.join(old_path), '/'.join(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): + undo = [] + # remove the original separator if its parent is not the new destination + if separator.get_parent() != new_parent: + self.deleteSeparator(separator) + undo.append(separator) + # this adds the new separator to the specified position + self.__positionItem(new_parent, separator, before, after) + undo.append(self.__getMenu(new_parent)) + self.__addUndo(undo) + self.save() + + def deleteItem(self, item): + self.__addUndo([item,]) + self.__writeItem(item, Hidden=True) + self.save() + + def deleteMenu(self, menu): + dom = self.__getMenu(menu).dom + menu_xml = self.__getXmlMenu(self.__getPath(menu), dom.documentElement, 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.documentElement, dom) + self.__addXmlLayout(menu_xml, layout, dom) + self.__addUndo([self.__getMenu(item.get_parent()),]) + 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() is 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 + with codecs.open(file_path, 'r', 'utf-8') as f: + data = f.read() + undo_path = util.getUniqueUndoFile(file_path) + with codecs.open(undo_path, 'w', 'utf-8') as f: + f.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 is None: + menu = self.__findMenu(menu_id, self.applications.tree.root) + if menu is not 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 is not 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) is None: + return False + return True + + def __getPath(self, menu): + names = [] + current = menu + while current is not None: + names.append(current.get_menu_id()) + current = current.get_parent() + + # XXX - don't append root menu name, mozo doesn't + # expect it. look into this more. + names.pop(-1) + return names[::-1] + + def __getXmlMenuPart(self, element, name): + for node in self.__getXmlNodesByName('Menu', element): + for child in self.__getXmlNodesByName('Name', node): + if child.childNodes[0].nodeValue == name: + return node + return None + + def __getXmlMenu(self, path, element, dom): + for name in path: + found = self.__getXmlMenuPart(element, name) + if found is not None: + element = found + else: + element = self.__addXmlMenuElement(element, name, dom) + return element + + def __addXmlMenuElement(self, element, name, dom): + if isinstance(name, bytes): + name = name.decode('utf-8') + + node = dom.createElement('Menu') + self.__addXmlTextElement(node, 'Name', name, dom) + return element.appendChild(node) + + def __addXmlTextElement(self, element, name, text, dom): + if isinstance(name, bytes): + name = name.decode('utf-8') + if isinstance(text, bytes): + text = text.decode('utf-8') + + 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'): + if isinstance(filename, bytes): + filename = filename.decode('utf-8') + + # 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 __makeKeyFile(self, file_path, kwargs): + if 'KeyFile' in kwargs: + return kwargs['KeyFile'] + + keyfile = GLib.KeyFile() + + if file_path is not None: + keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) + + util.fillKeyFile(keyfile, kwargs) + return keyfile + + def __writeItem(self, item, **kwargs): + if item is not None: + file_path = item.get_desktop_file_path() + else: + file_path = None + + keyfile = self.__makeKeyFile(file_path, kwargs) + + if item is not None: + file_id = item.get_desktop_file_id() + else: + file_id = util.getUniqueFileId(keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, 'Name'), '.desktop') + + contents, length = keyfile.to_data() + + with open(os.path.join(util.getUserItemPath(), file_id), 'w') as f: + f.write(contents) + return file_id + + def __writeMenu(self, menu, **kwargs): + if menu is not None: + file_id = os.path.split(menu.get_desktop_file_path())[1] + file_path = menu.get_desktop_file_path() + keyfile = GLib.KeyFile() + keyfile.load_from_file(file_path, util.KEY_FILE_FLAGS) + elif menu is None and 'Name' not in kwargs: + raise Exception('New menus need a name') + else: + file_id = util.getUniqueFileId(kwargs['Name'], '.directory') + keyfile = GLib.KeyFile() + + util.fillKeyFile(keyfile, kwargs) + + contents, length = keyfile.to_data() + + with open(os.path.join(util.getUserDirectoryPath(), file_id), 'w') as f: + f.write(contents) + 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 __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.documentElement, dom) + self.__addXmlFilename(xml_parent, dom, file_id, 'Include') + + def __positionItem(self, parent, item, before=None, after=None): + if after: + index = parent.contents.index(after) + 1 + elif before: + index = parent.contents.index(before) + else: + # append the item to the list + index = len(parent.contents) + contents = parent.contents + #if this is a move to a new parent you can't remove the item + if item in contents: + # decrease the destination index, if we shorten the list + if (before and (contents.index(item) < index)) \ + or (after and (contents.index(item) < index - 1)): + index -= 1 + contents.remove(item) + contents.insert(index, item) + layout = self.__createLayout(contents) + dom = self.__getMenu(parent).dom + menu_xml = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, 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.documentElement, 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 __init__(self, node=None): + self.order = [] - def parseMenuname(self, value): - self.order.append(['Menuname', value]) + def parseMenuname(self, value): + self.order.append(['Menuname', value]) - def parseSeparator(self): - self.order.append(['Separator']) + def parseSeparator(self): + self.order.append(['Separator']) - def parseFilename(self, value): - self.order.append(['Filename', value]) + def parseFilename(self, value): + self.order.append(['Filename', value]) - def parseMerge(self, merge_type='all'): - self.order.append(['Merge', merge_type]) + def parseMerge(self, merge_type='all'): + self.order.append(['Merge', merge_type]) |