From af199835310b06f403ea130543d6c988ea955f29 Mon Sep 17 00:00:00 2001 From: yetist Date: Wed, 25 Apr 2018 13:38:33 +0800 Subject: Support new mate-menus api - Requires mate-menus 1.21.0 - Use Gobject-introspection - Migrate to python3 --- Mozo/MainWindow.py | 130 +++++++++++++++++----------------- Mozo/MenuEditor.py | 199 ++++++++++++++++++++++++++++++++++------------------- Mozo/util.py | 78 +++++++++++---------- acinclude.m4 | 8 +-- configure.ac | 4 +- mozo.in | 2 +- 6 files changed, 247 insertions(+), 174 deletions(-) diff --git a/Mozo/MainWindow.py b/Mozo/MainWindow.py index 75533fa..849de09 100644 --- a/Mozo/MainWindow.py +++ b/Mozo/MainWindow.py @@ -16,12 +16,13 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import matemenu import gi gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') +gi.require_version('MateMenu', '2.0') from gi.repository import GLib, Gio from gi.repository import Gtk, Gdk, GdkPixbuf +from gi.repository import MateMenu import cgi import os import gettext @@ -40,7 +41,6 @@ from Mozo.MenuEditor import MenuEditor from Mozo import util class MainWindow: - timer = None #hack to make editing menu properties work allow_update = True #drag-and-drop stuff @@ -77,40 +77,38 @@ class MainWindow: keyval, modifier = Gtk.accelerator_parse('F1') accelgroup.connect(keyval, modifier, Gtk.AccelFlags.VISIBLE, self.on_help_button_clicked) self.tree.get_object('mainwindow').add_accel_group(accelgroup) + self.main_window = self.tree.get_object('mainwindow') def run(self): self.loadMenus() - self.editor.applications.tree.add_monitor(self.menuChanged, None) - self.editor.settings.tree.add_monitor(self.menuChanged, None) + self.editor.applications.tree.connect("changed", self.menuChanged) + self.editor.settings.tree.connect("changed", self.menuChanged) self.tree.get_object('mainwindow').show_all() Gtk.main() def menuChanged(self, *a): - if self.timer: - GLib.Source.remove(self.timer) - self.timer = None - self.timer = GLib.timeout_add(3, self.loadUpdates) + self.loadUpdates() def loadUpdates(self): if not self.allow_update: - self.timer = None return False menu_tree = self.tree.get_object('menu_tree') item_tree = self.tree.get_object('item_tree') items, iter = item_tree.get_selection().get_selected() update_items = False - item_id, separator_path = None, None + update_type = None + item_id = None if iter: update_items = True - if items[iter][3].get_type() == matemenu.TYPE_DIRECTORY: + if isinstance(items[iter][3], MateMenu.TreeDirectory): item_id = os.path.split(items[iter][3].get_desktop_file_path())[1] - update_items = True - elif items[iter][3].get_type() == matemenu.TYPE_ENTRY: + update_type = MateMenu.TreeItemType.DIRECTORY + elif isinstance(items[iter][3], MateMenu.TreeEntry): item_id = items[iter][3].get_desktop_file_id() - update_items = True - elif items[iter][3].get_type() == matemenu.TYPE_SEPARATOR: - item_id = items.get_path(iter).to_string() - update_items = True + update_type = MateMenu.TreeItemType.ENTRY + elif isinstance(items[iter][3], MateMenu.TreeSeparator): + item_id = items.get_path(iter) + update_type = MateMenu.TreeItemType.SEPARATOR menus, iter = menu_tree.get_selection().get_selected() update_menus = False menu_id = None @@ -132,12 +130,13 @@ class MainWindow: i = 0 for item in item_tree.get_model(): found = False - if item[3].get_type() == matemenu.TYPE_ENTRY and item[3].get_desktop_file_id() == item_id: - found = True - if item[3].get_type() == matemenu.TYPE_DIRECTORY and item[3].get_desktop_file_path(): - if os.path.split(item[3].get_desktop_file_path())[1] == item_id: + if update_type != MateMenu.TreeItemType.SEPARATOR: + if isinstance (item[3], MateMenu.TreeEntry) and item[3].get_desktop_file_id() == item_id: found = True - if item[3].get_type() == matemenu.TYPE_SEPARATOR: + if isinstance (item[3], MateMenu.TreeDirectory) and item[3].get_desktop_file_path() and update_type == MateMenu.TreeItemType.DIRECTORY: + if os.path.split(item[3].get_desktop_file_path())[1] == item_id: + found = True + if isinstance(item[3], MateMenu.TreeSeparator): if not isinstance(item_id, tuple): #we may not skip the increment via "continue" i += 1 @@ -155,7 +154,6 @@ class MainWindow: self.on_item_tree_cursor_changed(item_tree) break i += 1 - self.timer = None return False def findMenu(self, menus, path, iter, menu_id): @@ -179,15 +177,16 @@ class MainWindow: column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) - column.set_attributes(cell, pixbuf=0) + column.add_attribute(cell, 'pixbuf', 0) cell = Gtk.CellRendererText() cell.set_fixed_size(-1, 25) column.pack_start(cell, True) - column.set_attributes(cell, markup=1) + column.add_attribute(cell, 'markup', 1) column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) menus.append_column(column) menus.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, self.dnd_menus, Gdk.DragAction.COPY) menus.enable_model_drag_dest(self.dnd_both, Gdk.DragAction.PRIVATE) + menus.get_selection().set_mode(Gtk.SelectionMode.BROWSE) def setupItemTree(self): items = self.tree.get_object('item_tree') @@ -195,7 +194,7 @@ class MainWindow: cell = Gtk.CellRendererToggle() cell.connect('toggled', self.on_item_tree_show_toggled) column.pack_start(cell, True) - column.set_attributes(cell, active=0) + column.add_attribute(cell, 'active', 0) #hide toggle for separators column.set_cell_data_func(cell, self._cell_data_toggle_func) items.append_column(column) @@ -203,11 +202,11 @@ class MainWindow: column.set_spacing(4) cell = Gtk.CellRendererPixbuf() column.pack_start(cell, False) - column.set_attributes(cell, pixbuf=1) + column.add_attribute(cell, 'pixbuf', 1) cell = Gtk.CellRendererText() cell.set_fixed_size(-1, 25) column.pack_start(cell, True) - column.set_attributes(cell, markup=2) + column.add_attribute(cell, 'markup', 2) items.append_column(column) self.item_store = Gtk.ListStore(bool, GdkPixbuf.Pixbuf, str, object) items.set_model(self.item_store) @@ -215,7 +214,7 @@ class MainWindow: items.enable_model_drag_dest(self.dnd_items, Gdk.DragAction.PRIVATE) def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter, data=None): - if model[treeiter][3].get_type() == matemenu.TYPE_SEPARATOR: + if isinstance(model[treeiter][3], MateMenu.TreeSeparator): renderer.set_property('visible', False) else: renderer.set_property('visible', True) @@ -254,22 +253,19 @@ class MainWindow: def loadItems(self, menu, menu_path): self.item_store.clear() for item, show in self.editor.getItems(menu): - menu_icon = None - if item.get_type() == matemenu.TYPE_SEPARATOR: + icon = util.getIcon(item) + if isinstance(item, MateMenu.TreeSeparator): name = '---' - icon = None - elif item.get_type() == matemenu.TYPE_ENTRY: + elif isinstance(item, MateMenu.TreeEntry): if show: - name = cgi.escape(item.get_display_name()) + name = cgi.escape(item.get_app_info().get_display_name()) else: - name = '' + cgi.escape(item.get_display_name()) + '' - icon = util.getIcon(item) + name = '' + cgi.escape(item.get_app_info().get_display_name()) + '' else: if show: name = cgi.escape(item.get_name()) else: name = '' + cgi.escape(item.get_name()) + '' - icon = util.getIcon(item) self.item_store.append((show, icon, name, item)) #this is a little timeout callback to insert new items after @@ -346,11 +342,11 @@ class MainWindow: if not iter: return item = items[iter][3] - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.editor.deleteItem(item) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): self.editor.deleteMenu(item) - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): self.editor.deleteSeparator(item) def on_edit_revert_to_original_activate(self, menu): @@ -359,9 +355,9 @@ class MainWindow: if not iter: return item = items[iter][3] - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.editor.revertItem(item) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): self.editor.revertMenu(item) def on_edit_properties_activate(self, menu): @@ -370,13 +366,13 @@ class MainWindow: if not iter: return item = items[iter][3] - if item.get_type() not in (matemenu.TYPE_ENTRY, matemenu.TYPE_DIRECTORY): + if not isinstance(item, MateMenu.TreeEntry) and not isinstance(item, MateMenu.TreeDirectory): return - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id()) file_type = 'Item' - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1]) file_type = 'Menu' @@ -391,7 +387,10 @@ class MainWindow: GLib.timeout_add(100, self.waitForEditProcess, process, file_path) def on_menu_tree_cursor_changed(self, treeview): - menus, iter = treeview.get_selection().get_selected() + selection = treeview.get_selection() + if selection is None: + return + menus, iter = selection.get_selected() if iter is None: return menu_path = menus.get_path(iter) @@ -404,6 +403,8 @@ class MainWindow: self.tree.get_object('move_up_button').set_sensitive(False) self.tree.get_object('move_down_button').set_sensitive(False) self.tree.get_object('new_separator_button').set_sensitive(False) + self.tree.get_object('properties_button').set_sensitive(False) + self.tree.get_object('delete_button').set_sensitive(False) def on_menu_tree_drag_data_get(self, treeview, context, selection, target_id, etime): menus, iter = treeview.get_selection().get_selected() @@ -423,12 +424,12 @@ class MainWindow: return False item = self.drag_data new_parent = menus[path][2] - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.editor.copyItem(item, new_parent) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): if not self.editor.moveMenu(item, new_parent): self.loadUpdates() - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): self.editor.moveSeparator(item, new_parent) else: context.finish(False, False, etime) @@ -437,7 +438,7 @@ class MainWindow: def on_item_tree_show_toggled(self, cell, path): item = self.item_store[path][3] - if item.get_type() == matemenu.TYPE_SEPARATOR: + if isinstance(item, MateMenu.TreeSeparator): return if self.item_store[path][0]: self.editor.setVisible(item, False) @@ -446,7 +447,10 @@ class MainWindow: self.item_store[path][0] = not self.item_store[path][0] def on_item_tree_cursor_changed(self, treeview): - items, iter = treeview.get_selection().get_selected() + selection = treeview.get_selection() + if selection is None: + return + items, iter = selection.get_selected() if iter is None: return @@ -458,7 +462,7 @@ class MainWindow: can_revert = self.editor.canRevert(item) self.tree.get_object('edit_revert_to_original').set_sensitive(can_revert) - can_edit = not item.get_type() == matemenu.TYPE_SEPARATOR + can_edit = not isinstance(item, MateMenu.TreeSeparator) self.tree.get_object('edit_properties').set_sensitive(can_edit) self.tree.get_object('properties_button').set_sensitive(can_edit) @@ -517,7 +521,7 @@ class MainWindow: path, position = drop_info target = items[path][3] # move the item to the directory, if the item was dropped into it - if (target.get_type() == matemenu.TYPE_DIRECTORY) and (position in types_into): + if isinstance(target, MateMenu.TreeDirectory) and (position in types_into): # append the selected item to the choosen menu destination = target elif position in types_before: @@ -530,12 +534,12 @@ class MainWindow: else: path = (len(items) - 1,) after = items[path][3] - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.editor.moveItem(item, destination, before, after) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): if not self.editor.moveMenu(item, destination, before, after): self.loadUpdates() - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): self.editor.moveSeparator(item, destination, before, after) context.finish(True, True, etime) elif str(selection.get_target()) == 'text/plain': @@ -587,12 +591,12 @@ class MainWindow: if path.get_indices()[0] == 0: return item = items[path][3] - before = items[(path[0] - 1,)][3] - if item.get_type() == matemenu.TYPE_ENTRY: + before = items[(path.get_indices()[0] - 1,)][3] + if isinstance(item, MateMenu.TreeEntry): self.editor.moveItem(item, item.get_parent(), before=before) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): self.editor.moveMenu(item, item.get_parent(), before=before) - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): self.editor.moveSeparator(item, item.get_parent(), before=before) def on_move_down_button_clicked(self, button): @@ -606,11 +610,11 @@ class MainWindow: return item = items[path][3] after = items[path][3] - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.editor.moveItem(item, item.get_parent(), after=after) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): self.editor.moveMenu(item, item.get_parent(), after=after) - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): self.editor.moveSeparator(item, item.get_parent(), after=after) def on_mainwindow_undo(self, accelgroup, window, keyval, modifier): diff --git a/Mozo/MenuEditor.py b/Mozo/MenuEditor.py index 62832e6..17315da 100644 --- a/Mozo/MenuEditor.py +++ b/Mozo/MenuEditor.py @@ -22,8 +22,9 @@ import re import xml.dom.minidom import xml.parsers.expat import locale -import matemenu -from gi.repository import GLib +import gi +gi.require_version('MateMenu', '2.0') +from gi.repository import MateMenu, GLib from Mozo import util class Menu: @@ -32,41 +33,56 @@ class Menu: path = None dom = None -class MenuEditor: +class MenuEditor(object): def __init__(self): self.locale = locale.getdefaultlocale()[0] - self.__loadMenus() self.__undo = [] self.__redo = [] + self.applications = Menu() + self.applications.tree = MateMenu.Tree.new('mate-applications.menu', MateMenu.TreeFlags.SHOW_EMPTY|MateMenu.TreeFlags.INCLUDE_EXCLUDED|MateMenu.TreeFlags.INCLUDE_NODISPLAY|MateMenu.TreeFlags.SHOW_ALL_SEPARATORS|MateMenu.TreeFlags.SORT_DISPLAY_NAME) + self.applications.visible_tree = MateMenu.Tree.new('mate-applications.menu', MateMenu.TreeFlags.SORT_DISPLAY_NAME) + self.applications.tree.sort_key = MateMenu.TreeFlags.SORT_DISPLAY_NAME + self.applications.visible_tree.sort_key = MateMenu.TreeFlags.SORT_DISPLAY_NAME + self.applications.tree.connect('changed', self.menuChanged) + self.settings = Menu() + self.settings.tree = MateMenu.Tree.new('mate-settings.menu', MateMenu.TreeFlags.SHOW_EMPTY|MateMenu.TreeFlags.INCLUDE_EXCLUDED|MateMenu.TreeFlags.INCLUDE_NODISPLAY|MateMenu.TreeFlags.SHOW_ALL_SEPARATORS|MateMenu.TreeFlags.SORT_DISPLAY_NAME) + self.settings.visible_tree = MateMenu.Tree.new('mate-settings.menu', MateMenu.TreeFlags.SORT_DISPLAY_NAME) + self.settings.tree.sort_key = MateMenu.TreeFlags.SORT_DISPLAY_NAME + self.settings.visible_tree.sort_key = MateMenu.TreeFlags.SORT_DISPLAY_NAME + self.settings.tree.connect('changed', self.menuChanged) + self.load() + 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()) + self.applications.path = os.path.join(util.getUserMenuPath(), self.applications.tree.props.menu_basename) 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()) + self.settings.path = os.path.join(util.getUserMenuPath(), self.settings.tree.props.menu_basename) 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 load(self): + if not self.applications.tree.load_sync(): + raise ValueError("can not load menu tree %r" % (self.applications.tree.props.menu_basename,)) + if not self.settings.tree.load_sync(): + raise ValueError("can not load menu tree %r" % (self.settings.tree.props.menu_basename,)) + if not self.applications.visible_tree.load_sync(): + raise ValueError("can not load menu tree %r" % (self.applications.visible_tree.props.menu_basename,)) + if not self.settings.visible_tree.load_sync(): + raise ValueError("can not load menu tree %r" % (self.settings.visible_tree.props.menu_basename,)) + + def menuChanged(self, *a): + self.load() + def save(self, from_loading=False): for menu in ('applications', 'settings'): with codecs.open(getattr(self, menu).path, 'w', 'utf-8') as f: @@ -91,8 +107,8 @@ class MenuEditor: 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()) + self.revertTree(menu.tree.get_root_directory()) + path = os.path.join(util.getUserMenuPath(), menu.tree.props.menu_basename) try: os.unlink(path) except OSError: @@ -108,11 +124,16 @@ class MenuEditor: 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) + item_iter = menu.iter() + item_type = item_iter.next() + while item_type != MateMenu.TreeItemType.INVALID: + if item_type == MateMenu.TreeItemType.DIRECTORY: + item = item_iter.get_directory() + self.revertTree(item) + elif item_type == MateMenu.TreeItemType.ENTRY: + item = item_iter.get_entry() + self.revertItem(item) + item_type = item_iter.next() self.revertMenu(menu) def revertItem(self, item): @@ -206,29 +227,55 @@ class MenuEditor: def getMenus(self, parent=None): if parent is None: - yield self.applications.tree.root - yield self.settings.tree.root + yield self.applications.tree.get_root_directory() + yield self.settings.tree.get_root_directory() else: - for menu in parent.get_contents(): - if menu.get_type() == matemenu.TYPE_DIRECTORY: - yield (menu, self.__isVisible(menu)) + item_iter = parent.iter() + item_type = item_iter.next() + while item_type != MateMenu.TreeItemType.INVALID: + if item_type == MateMenu.TreeItemType.DIRECTORY: + item = item_iter.get_directory() + yield (item, self.__isVisible(item)) + item_type = item_iter.next() + + def getContents(self, item): + contents = [] + item_iter = item.iter() + item_type = item_iter.next() + + while item_type != MateMenu.TreeItemType.INVALID: + item = None + if item_type == MateMenu.TreeItemType.DIRECTORY: + item = item_iter.get_directory() + elif item_type == MateMenu.TreeItemType.ENTRY: + item = item_iter.get_entry() + elif item_type == MateMenu.TreeItemType.HEADER: + item = item_iter.get_header() + elif item_type == MateMenu.TreeItemType.ALIAS: + item = item_iter.get_alias() + elif item_type == MateMenu.TreeItemType.SEPARATOR: + item = item_iter.get_separator() + if item: + contents.append(item) + item_type = item_iter.next() + return contents def getItems(self, menu): - for item in menu.get_contents(): - if item.get_type() == matemenu.TYPE_SEPARATOR: - yield (item, True) + for item in self.getContents(menu): + if isinstance(item, MateMenu.TreeSeparator): + yield(item, True) else: - if item.get_type() == matemenu.TYPE_ENTRY and item.get_desktop_file_id()[-19:] == '-usercustom.desktop': - continue + if isinstance(item, MateMenu.TreeEntry) 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 isinstance(item, MateMenu.TreeEntry): 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: + elif isinstance(item, MateMenu.TreeDirectory): if item.get_desktop_file_path(): file_id = os.path.split(item.get_desktop_file_path())[1] else: @@ -241,7 +288,7 @@ class MenuEditor: def setVisible(self, item, visible): dom = self.__getMenu(item).dom - if item.get_type() == matemenu.TYPE_ENTRY: + if isinstance(item, MateMenu.TreeEntry): self.__addUndo([self.__getMenu(item), item]) menu_xml = self.__getXmlMenu(self.__getPath(item.get_parent()), dom.documentElement, dom) if visible: @@ -250,10 +297,12 @@ class MenuEditor: 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: + elif isinstance(item, MateMenu.TreeDirectory): self.__addUndo([self.__getMenu(item), item]) + item_iter = item.iter() + first_child_type = item_iter.next() #don't mess with it if it's empty - if len(item.get_contents()) == 0: + if first_child_type == MateMenu.TreeItemType.INVALID: return menu_xml = self.__getXmlMenu(self.__getPath(item), dom.documentElement, dom) for node in self.__getXmlNodesByName(['Deleted', 'NotDeleted'], menu_xml): @@ -264,7 +313,7 @@ class MenuEditor: def createItem(self, parent, before, after, **kwargs): file_id = self.__writeItem(None, **kwargs) - self.insertExternalItem(file_id, parent.menu_id, before, after) + self.insertExternalItem(file_id, parent.get_menu_id(), before, after) def insertExternalItem(self, file_id, parent_id, before=None, after=None): parent = self.__findMenu(parent_id) @@ -292,7 +341,8 @@ class MenuEditor: 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(): + app_info = item.get_app_info() + if icon == app_info.get_icon() and name == app_info.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: @@ -328,7 +378,8 @@ class MenuEditor: util.fillKeyFile(keyfile, dict(Categories=[], Hidden=False)) - file_id = util.getUniqueFileId(item.get_name().replace(os.sep, '-'), '.desktop') + app_info = item.get_app_info() + file_id = util.getUniqueFileId(app_info.get_name().replace(os.sep, '-'), '.desktop') out_path = os.path.join(util.getUserItemPath(), file_id) contents, length = keyfile.to_data() @@ -409,7 +460,7 @@ class MenuEditor: def deleteSeparator(self, item): parent = item.get_parent() - contents = parent.get_contents() + contents = self.getContents(parent) contents.remove(item) layout = self.__createLayout(contents) dom = self.__getMenu(parent).dom @@ -435,13 +486,13 @@ class MenuEditor: file_path = util.getDirectoryPath(item[1]) else: continue - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): 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: + elif isinstance(item, MateMenu.TreeEntry): 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() @@ -465,39 +516,45 @@ class MenuEditor: root = root.get_parent() else: break - if root.menu_id == self.applications.tree.root.menu_id: + if root.get_menu_id() == self.applications.tree.get_root_directory().get_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) + menu = self.__findMenu(menu_id, self.applications.tree.get_root_directory()) 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 self.__findMenu(menu_id, self.settings.tree.get_root_directory()) + if menu_id == self.applications.tree.get_root_directory().get_menu_id(): + return self.applications.tree.get_root_directory() + if menu_id == self.settings.tree.get_root_directory().get_menu_id(): + return self.settings.tree.get_root_directory() + + item_iter = parent.iter() + item_type = item_iter.next() + while item_type != MateMenu.TreeItemType.INVALID: + if item_type == MateMenu.TreeItemType.DIRECTORY: + item = item_iter.get_directory() + if item.get_menu_id() == menu_id: return item menu = self.__findMenu(menu_id, item) if menu is not None: return menu + item_type = item_iter.next() def __isVisible(self, item): - if item.get_type() == matemenu.TYPE_ENTRY: - return not (item.get_is_excluded() or item.get_is_nodisplay()) + if isinstance(item, MateMenu.TreeEntry): + app_info = item.get_app_info() + return not (item.get_is_excluded() or app_info.get_nodisplay()) menu = self.__getMenu(item) if menu == self.applications: - root = self.applications.visible_tree.root + root = self.applications.visible_tree.get_root_directory() 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: + root = self.settings.visible_tree.get_root_directory() + if isinstance(item, MateMenu.TreeDirectory): + if self.__findMenu(item.get_menu_id(), root) is None: return False return True @@ -682,11 +739,11 @@ class MenuEditor: layout.parseMenuname(item[1]) elif item[0] == 'Item': layout.parseFilename(item[1]) - elif item.get_type() == matemenu.TYPE_DIRECTORY: + elif isinstance(item, MateMenu.TreeDirectory): layout.parseMenuname(item.get_menu_id()) - elif item.get_type() == matemenu.TYPE_ENTRY: + elif isinstance(item, MateMenu.TreeEntry): layout.parseFilename(item.get_desktop_file_id()) - elif item.get_type() == matemenu.TYPE_SEPARATOR: + elif isinstance(item, MateMenu.TreeSeparator): layout.parseSeparator() layout.order.append(['Merge', 'files']) return layout @@ -695,20 +752,24 @@ class MenuEditor: xml_parent = self.__getXmlMenu(self.__getPath(parent), dom.documentElement, dom) self.__addXmlFilename(xml_parent, dom, file_id, 'Include') + def moveItem(self, parent, item, before=None, after=None): + self.__positionItem(parent, item, before=before, after=after) + self.save() + def __positionItem(self, parent, item, before=None, after=None): + contents = self.getContents(parent) if after: - index = parent.contents.index(after) + 1 + index = contents.index(after) + 1 elif before: - index = parent.contents.index(before) + index = contents.index(before) else: # append the item to the list - index = len(parent.contents) - contents = parent.contents + index = len(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)): + or (after and (contents.index(item) < index - 1)): index -= 1 contents.remove(item) contents.insert(index, item) diff --git a/Mozo/util.py b/Mozo/util.py index bb9e1c6..992731d 100644 --- a/Mozo/util.py +++ b/Mozo/util.py @@ -18,24 +18,26 @@ import os import xml.dom.minidom -import matemenu import gi +gi.require_version('Gtk', '3.0') +gi.require_version('MateMenu', '2.0') from collections import Sequence from gi.repository import GLib, Gtk, Gdk, GdkPixbuf +from gi.repository import MateMenu DESKTOP_GROUP = GLib.KEY_FILE_DESKTOP_GROUP KEY_FILE_FLAGS = GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS def fillKeyFile(keyfile, items): - for key, item in items.iteritems(): + for key, item in items.items(): if item is None: continue if isinstance(item, bool): keyfile.set_boolean(DESKTOP_GROUP, key, item) - elif isinstance(item, Sequence): + elif isinstance(item, Sequence) and not isinstance(item, (str, bytes, bytearray)): keyfile.set_string_list(DESKTOP_GROUP, key, item) - elif isinstance(item, basestring): + elif isinstance(item, str): keyfile.set_string(DESKTOP_GROUP, key, item) def getUniqueFileId(name, extension): @@ -123,49 +125,55 @@ def getSystemMenuPath(file_id): return None def getUserMenuXml(tree): - system_file = getSystemMenuPath(tree.get_menu_file()) - name = tree.root.get_menu_id() + system_file = getSystemMenuPath(os.path.basename(tree.get_canonical_menu_path())) + name = tree.get_root_directory().get_menu_id() menu_xml = "\n" menu_xml += "\n " + name + "\n " menu_xml += "" + system_file + "\n\n" return menu_xml def getIcon(item): - pixbuf, path = None, None - if item is None: - return None - if isinstance(item, str): + iconName = None + gicon = None + if isinstance(item, MateMenu.TreeDirectory): + gicon = item.get_icon() + elif isinstance(item, MateMenu.TreeEntry): + app_info = item.get_app_info() + gicon = app_info.get_icon() + elif isinstance(item, str): iconName = item - else: - iconName = item.get_icon() if iconName and not '/' in iconName and iconName[-3:] in ('png', 'svg', 'xpm'): iconName = iconName[:-4] + + pixbuf = None icon_theme = Gtk.IconTheme.get_default() - try: - pixbuf = icon_theme.load_icon(iconName, 24, 0) - path = icon_theme.lookup_icon(iconName, 24, 0).get_filename() - except: - if iconName and '/' in iconName: - try: - pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(iconName, 24, 24) - path = iconName - except: - pass - if pixbuf is None: - if item.get_type() == matemenu.TYPE_DIRECTORY: - iconName = 'mate-fs-directory' - elif item.get_type() == matemenu.TYPE_ENTRY: - iconName = 'application-default-icon' - try: - pixbuf = icon_theme.load_icon(iconName, 24, 0) - path = icon_theme.lookup_icon(iconName, 24, 0).get_filename() - except: - return None + if gicon: + info = icon_theme.lookup_by_gicon(gicon, 24, 0) + try: + pixbuf = info.load_icon() + except: + pass + elif iconName is not None: + try: + pixbuf = icon_theme.load_icon(iconName, 24, 0) + except: + if iconName and '/' in iconName: + try: + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(iconName, 24, 24) + except: + pass + # fallback to use image-missing icon + if pixbuf is None: + try: + pixbuf = icon_theme.load_icon('image-missing', 24, 0) + except: + pass if pixbuf is None: return None - if pixbuf.get_width() != 24 or pixbuf.get_height() != 24: - pixbuf = pixbuf.scale_simple(24, 24, GdkPixbuf.InterpType.HYPER) - return pixbuf + else: + if pixbuf.get_width() != 24 or pixbuf.get_height() != 24: + pixbuf = pixbuf.scale_simple(24, 24, GdkPixbuf.InterpType.HYPER) + return pixbuf def removeWhitespaceNodes(node): remove_list = [] diff --git a/acinclude.m4 b/acinclude.m4 index 1227123..8babc67 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -71,7 +71,7 @@ AC_DEFUN([AM_PATH_PYTHON_VERSION], dnl library. AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], - [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) + [am_cv_python_version=`$PYTHON -c "import sys; print(sys.version[[:3]])"`]) AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) dnl Use the values of $prefix and $exec_prefix for the corresponding @@ -86,7 +86,7 @@ AC_DEFUN([AM_PATH_PYTHON_VERSION], dnl to know which OS platform Python thinks this is. AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], - [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) + [am_cv_python_platform=`$PYTHON -c "import sys; print(sys.platform)"`]) AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) @@ -101,7 +101,7 @@ AC_DEFUN([AM_PATH_PYTHON_VERSION], dnl doesn't work. AC_CACHE_CHECK([for $am_display_PYTHON script directory], [am_cv_python_pythondir], - [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null || + [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX'))" 2>/dev/null || echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) AC_SUBST([pythondir], [$am_cv_python_pythondir]) @@ -118,7 +118,7 @@ AC_DEFUN([AM_PATH_PYTHON_VERSION], dnl doesn't work. AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], [am_cv_python_pyexecdir], - [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null || + [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX'))" 2>/dev/null || echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) diff --git a/configure.ac b/configure.ac index 8348533..c35cf4c 100644 --- a/configure.ac +++ b/configure.ac @@ -18,9 +18,9 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext package]) AM_GLIB_GNU_GETTEXT IT_PROG_INTLTOOL([0.40.0]) -AM_PATH_PYTHON_VERSION(2.7, 2.7.0) +AM_PATH_PYTHON(3.5) -PKG_CHECK_MODULES(MOZO, libmate-menu >= 1.1.0 pygobject-3.0) +PKG_CHECK_MODULES(MOZO, libmate-menu >= 1.21.0 pygobject-3.0) AC_ARG_ENABLE(icon-update, AC_HELP_STRING([--disable-icon-update], [Disable icon cache update])) diff --git a/mozo.in b/mozo.in index 52b4157..7a42108 100644 --- a/mozo.in +++ b/mozo.in @@ -28,7 +28,7 @@ def main(): version = config.VERSION except: datadir = './data/' - version = '1.7.0' + version = '1.21.0' app = MainWindow(datadir, version, sys.argv) app.run() -- cgit v1.2.1