--- a/files/usr/bin/cinnamon-menu-editor
+++ b/files/usr/bin/cinnamon-menu-editor
@@ -12,7 +12,7 @@ def main():
except:
datadir = '.'
version = '0.9'
- app = MainWindow.MainWindow(datadir, version, sys.argv)
+ app = MainWindow.MainWindow(datadir, version)
app.run()
if __name__ == '__main__':
--- a/files/usr/lib/cinnamon-menu-editor/Alacarte/config.py
+++ b/files/usr/lib/cinnamon-menu-editor/Alacarte/config.py
@@ -5,5 +5,5 @@ pkgdatadir="/usr/share/alacarte"
libdir="/usr/lib"
libexecdir="/usr/lib/alacarte"
PACKAGE="alacarte"
-VERSION="0.13.2"
+VERSION="3.5.4"
GETTEXT_PACKAGE="alacarte"
--- a/files/usr/lib/cinnamon-menu-editor/Alacarte/MainWindow.py
+++ b/files/usr/lib/cinnamon-menu-editor/Alacarte/MainWindow.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+# vim: set noexpandtab:
# Alacarte Menu Editor - Simple fd.o Compliant Menu Editor
# Copyright (C) 2006 Travis Watkins
#
@@ -16,614 +17,437 @@
# 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 gtk, gmenu, gobject, gio
-import cgi, os
-import gettext, locale
+from gi.repository import Gtk, GObject, Gio, GdkPixbuf, Gdk, GMenu, GLib
+import cgi
+import os
+import gettext
import subprocess
-import urllib
-try:
- from Alacarte import config
- gettext.bindtextdomain(config.GETTEXT_PACKAGE,config.localedir)
- gettext.textdomain(config.GETTEXT_PACKAGE)
- locale.bind_textdomain_codeset(config.GETTEXT_PACKAGE,'UTF-8')
-except:
- pass
+
+from Alacarte import config
+gettext.bindtextdomain(config.GETTEXT_PACKAGE, config.localedir)
+gettext.textdomain(config.GETTEXT_PACKAGE)
+
_ = gettext.gettext
from Alacarte.MenuEditor import MenuEditor
from Alacarte import util
-class MainWindow:
- timer = None
- #hack to make editing menu properties work
- allow_update = True
- #drag-and-drop stuff
- dnd_items = [('ALACARTE_ITEM_ROW', gtk.TARGET_SAME_APP, 0), ('text/plain', 0, 1)]
- dnd_menus = [('ALACARTE_MENU_ROW', gtk.TARGET_SAME_APP, 0)]
- dnd_both = [dnd_items[0],] + dnd_menus
- drag_data = None
- edit_pool = []
-
- def __init__(self, datadir, version, argv):
- self.file_path = datadir
- self.version = version
- self.editor = MenuEditor()
- gtk.window_set_default_icon_name('alacarte')
- self.tree = gtk.Builder()
- self.tree.set_translation_domain(config.GETTEXT_PACKAGE)
- self.tree.add_from_file('/usr/lib/cinnamon-menu-editor/cinnamon-menu-editor.ui')
- self.tree.connect_signals(self)
- self.setupMenuTree()
- self.setupItemTree()
- self.tree.get_object('edit_delete').set_sensitive(False)
- self.tree.get_object('edit_revert_to_original').set_sensitive(False)
- self.tree.get_object('edit_properties').set_sensitive(False)
- 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)
- accelgroup = gtk.AccelGroup()
- keyval, modifier = gtk.accelerator_parse('<Ctrl>Z')
- accelgroup.connect_group(keyval, modifier, gtk.ACCEL_VISIBLE, self.on_mainwindow_undo)
- keyval, modifier = gtk.accelerator_parse('<Ctrl><Shift>Z')
- accelgroup.connect_group(keyval, modifier, gtk.ACCEL_VISIBLE, self.on_mainwindow_redo)
- self.tree.get_object('mainwindow').add_accel_group(accelgroup)
-
- def run(self):
- self.loadMenus()
- self.editor.applications.tree.add_monitor(self.menuChanged, None)
- self.tree.get_object('mainwindow').show_all()
- gtk.main()
-
- def menuChanged(self, *a):
- if self.timer:
- gobject.source_remove(self.timer)
- self.timer = None
- self.timer = gobject.timeout_add(3, self.loadUpdates)
-
- def loadUpdates(self):
- if not self.allow_update:
- 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
- if iter:
- update_items = True
- if items[iter][3].get_type() == gmenu.TYPE_DIRECTORY:
- item_id = os.path.split(items[iter][3].get_desktop_file_path())[1]
- update_items = True
- elif items[iter][3].get_type() == gmenu.TYPE_ENTRY:
- item_id = items[iter][3].get_desktop_file_id()
- update_items = True
- elif items[iter][3].get_type() == gmenu.TYPE_SEPARATOR:
- item_id = items.get_path(iter)
- update_items = True
- menus, iter = menu_tree.get_selection().get_selected()
- update_menus = False
- menu_id = None
- if iter:
- if menus[iter][2].get_desktop_file_path():
- menu_id = os.path.split(menus[iter][2].get_desktop_file_path())[1]
- else:
- menu_id = menus[iter][2].get_menu_id()
- update_menus = True
- self.loadMenus()
- #find current menu in new tree
- if update_menus:
- menu_tree.get_model().foreach(self.findMenu, menu_id)
- menus, iter = menu_tree.get_selection().get_selected()
- if iter:
- self.on_menu_tree_cursor_changed(menu_tree)
- #find current item in new list
- if update_items:
- i = 0
- for item in item_tree.get_model():
- found = False
- if item[3].get_type() == gmenu.TYPE_ENTRY and item[3].get_desktop_file_id() == item_id:
- found = True
- if item[3].get_type() == gmenu.TYPE_DIRECTORY and item[3].get_desktop_file_path():
- if os.path.split(item[3].get_desktop_file_path())[1] == item_id:
- found = True
- if item[3].get_type() == gmenu.TYPE_SEPARATOR:
- if not isinstance(item_id, tuple):
- continue
- #separators have no id, have to find them manually
- #probably won't work with two separators together
- if (item_id[0] - 1,) == (i,):
- found = True
- elif (item_id[0] + 1,) == (i,):
- found = True
- elif (item_id[0],) == (i,):
- found = True
- if found:
- item_tree.get_selection().select_path((i,))
- self.on_item_tree_cursor_changed(item_tree)
- break
- i += 1
- return False
-
- def findMenu(self, menus, path, iter, menu_id):
- if not menus[path][2].get_desktop_file_path():
- if menu_id == menus[path][2].get_menu_id():
- menu_tree = self.tree.get_object('menu_tree')
- menu_tree.expand_to_path(path)
- menu_tree.get_selection().select_path(path)
- return True
- return False
- if os.path.split(menus[path][2].get_desktop_file_path())[1] == menu_id:
- menu_tree = self.tree.get_object('menu_tree')
- menu_tree.expand_to_path(path)
- menu_tree.get_selection().select_path(path)
- return True
-
- def setupMenuTree(self):
- self.menu_store = gtk.TreeStore(gtk.gdk.Pixbuf, str, object)
- menus = self.tree.get_object('menu_tree')
- column = gtk.TreeViewColumn(_("Name"))
- column.set_spacing(4)
- cell = gtk.CellRendererPixbuf()
- column.pack_start(cell, False)
- column.set_attributes(cell, pixbuf=0)
- cell = gtk.CellRendererText()
- cell.set_fixed_size(-1, 25)
- column.pack_start(cell, True)
- column.set_attributes(cell, markup=1)
- column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- menus.append_column(column)
- menus.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, self.dnd_menus, gtk.gdk.ACTION_COPY)
- menus.enable_model_drag_dest(self.dnd_both, gtk.gdk.ACTION_PRIVATE)
-
- def setupItemTree(self):
- items = self.tree.get_object('item_tree')
- column = gtk.TreeViewColumn(_("Show"))
- cell = gtk.CellRendererToggle()
- cell.connect('toggled', self.on_item_tree_show_toggled)
- column.pack_start(cell, True)
- column.set_attributes(cell, active=0)
- #hide toggle for separators
- column.set_cell_data_func(cell, self._cell_data_toggle_func)
- items.append_column(column)
- column = gtk.TreeViewColumn(_("Item"))
- column.set_spacing(4)
- cell = gtk.CellRendererPixbuf()
- column.pack_start(cell, False)
- column.set_attributes(cell, pixbuf=1)
- cell = gtk.CellRendererText()
- cell.set_fixed_size(-1, 25)
- column.pack_start(cell, True)
- column.set_attributes(cell, markup=2)
- items.append_column(column)
- self.item_store = gtk.ListStore(bool, gtk.gdk.Pixbuf, str, object)
- items.set_model(self.item_store)
- items.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, self.dnd_items, gtk.gdk.ACTION_COPY)
- items.enable_model_drag_dest(self.dnd_items, gtk.gdk.ACTION_PRIVATE)
-
- def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter):
- if model[treeiter][3].get_type() == gmenu.TYPE_SEPARATOR:
- renderer.set_property('visible', False)
- else:
- renderer.set_property('visible', True)
-
- def loadMenus(self):
- self.menu_store.clear()
- for menu in self.editor.getMenus():
- iters = [None]*20
- self.loadMenu(iters, menu)
- menu_tree = self.tree.get_object('menu_tree')
- menu_tree.set_model(self.menu_store)
- for menu in self.menu_store:
- #this might not work for some reason
- try:
- menu_tree.expand_to_path(menu.path)
- except:
- pass
- menu_tree.get_selection().select_path((0,))
- self.on_menu_tree_cursor_changed(menu_tree)
-
- def loadMenu(self, iters, parent, depth=0):
- if depth == 0:
- icon = util.getIcon(parent)
- iters[depth] = self.menu_store.append(None, (icon, cgi.escape(parent.get_name()), parent))
- depth += 1
- for menu, show in self.editor.getMenus(parent):
- if show:
- name = cgi.escape(menu.get_name())
- else:
- name = '<small><i>' + cgi.escape(menu.get_name()) + '</i></small>'
- icon = util.getIcon(menu)
- iters[depth] = self.menu_store.append(iters[depth-1], (icon, name, menu))
- self.loadMenu(iters, menu, depth)
- depth -= 1
-
- 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() == gmenu.TYPE_SEPARATOR:
- name = '---'
- icon = None
- elif item.get_type() == gmenu.TYPE_ENTRY:
- if show:
- name = cgi.escape(item.get_display_name())
- else:
- name = '<small><i>' + cgi.escape(item.get_display_name()) + '</i></small>'
- icon = util.getIcon(item)
- else:
- if show:
- name = cgi.escape(item.get_name())
- else:
- name = '<small><i>' + cgi.escape(item.get_name()) + '</i></small>'
- icon = util.getIcon(item)
- self.item_store.append((show, icon, name, item))
-
- #this is a little timeout callback to insert new items after
- #gnome-desktop-item-edit has finished running
- def waitForNewItemProcess(self, process, parent, file_path):
- if process.poll() != None:
- if os.path.isfile(file_path):
- self.editor.insertExternalItem(os.path.split(file_path)[1], parent)
- return False
- return True
-
- def waitForNewMenuProcess(self, process, parent_id, file_path):
- if process.poll() != None:
- #hack for broken gnome-desktop-item-edit
- broken_path = os.path.join(os.path.split(file_path)[0], '.directory')
- if os.path.isfile(broken_path):
- os.rename(broken_path, file_path)
- if os.path.isfile(file_path):
- self.editor.insertExternalMenu(os.path.split(file_path)[1], parent_id)
- return False
- return True
-
- #this callback keeps you from editing the same item twice
- def waitForEditProcess(self, process, file_path):
- if process.poll() != None:
- self.edit_pool.remove(file_path)
- return False
- return True
-
- def on_new_menu_button_clicked(self, button):
- menu_tree = self.tree.get_object('menu_tree')
- menus, iter = menu_tree.get_selection().get_selected()
- if not iter:
- parent = menus[(0,)][2]
- menu_tree.expand_to_path((0,))
- menu_tree.get_selection().select_path((0,))
- else:
- parent = menus[iter][2]
- file_path = os.path.join(util.getUserDirectoryPath(), util.getUniqueFileId('alacarte-made', '.directory'))
- process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
- gobject.timeout_add(100, self.waitForNewMenuProcess, process, parent.menu_id, file_path)
-
- def on_new_item_button_clicked(self, button):
- menu_tree = self.tree.get_object('menu_tree')
- menus, iter = menu_tree.get_selection().get_selected()
- if not iter:
- parent = menus[(0,)][2]
- menu_tree.expand_to_path((0,))
- menu_tree.get_selection().select_path((0,))
- else:
- parent = menus[iter][2]
- file_path = os.path.join(util.getUserItemPath(), util.getUniqueFileId('alacarte-made', '.desktop'))
- process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
- gobject.timeout_add(100, self.waitForNewItemProcess, process, parent, file_path)
-
- def on_new_separator_button_clicked(self, button):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- else:
- after = items[iter][3]
- menu_tree = self.tree.get_object('menu_tree')
- menus, iter = menu_tree.get_selection().get_selected()
- parent = menus[iter][2]
- self.editor.createSeparator(parent, after=after)
-
- def on_edit_delete_activate(self, menu):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- item = items[iter][3]
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.deleteItem(item)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- self.editor.deleteMenu(item)
- elif item.get_type() == gmenu.TYPE_SEPARATOR:
- self.editor.deleteSeparator(item)
-
- def on_edit_revert_to_original_activate(self, menu):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- item = items[iter][3]
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.revertItem(item)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- self.editor.revertMenu(item)
-
- def on_edit_properties_activate(self, menu):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- item = items[iter][3]
- if item.get_type() not in (gmenu.TYPE_ENTRY, gmenu.TYPE_DIRECTORY):
- return
-
- if item.get_type() == gmenu.TYPE_ENTRY:
- file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id())
- file_type = 'Item'
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- if item.get_desktop_file_path() == None:
- file_path = util.getUniqueFileId('alacarte-made', '.directory')
- parser = util.DesktopParser(file_path, 'Directory')
- parser.set('Name', item.get_name())
- parser.set('Comment', item.get_comment())
- parser.set('Icon', item.get_icon())
- parser.write(open(file_path))
- else:
- file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1])
- file_type = 'Menu'
-
- if not os.path.isfile(file_path):
- data = open(item.get_desktop_file_path()).read()
- open(file_path, 'w').write(data)
- self.editor._MenuEditor__addUndo([(file_type, os.path.split(file_path)[1]),])
- else:
- self.editor._MenuEditor__addUndo([item,])
- if file_path not in self.edit_pool:
- self.edit_pool.append(file_path)
- process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
- gobject.timeout_add(100, self.waitForEditProcess, process, file_path)
-
- def on_menu_tree_cursor_changed(self, treeview):
- menus, iter = treeview.get_selection().get_selected()
- menu_path = menus.get_path(iter)
- item_tree = self.tree.get_object('item_tree')
- item_tree.get_selection().unselect_all()
- self.loadItems(self.menu_store[menu_path][2], menu_path)
- self.tree.get_object('edit_delete').set_sensitive(False)
- self.tree.get_object('edit_revert_to_original').set_sensitive(False)
- self.tree.get_object('edit_properties').set_sensitive(False)
- 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()
- self.drag_data = menus[iter][2]
-
- def on_menu_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime):
- menus = treeview.get_model()
- drop_info = treeview.get_dest_row_at_pos(x, y)
- if drop_info:
- path, position = drop_info
- types = (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, gtk.TREE_VIEW_DROP_INTO_OR_AFTER)
- if position not in types:
- context.finish(False, False, etime)
- return False
- if selection.target in ('ALACARTE_ITEM_ROW', 'ALACARTE_MENU_ROW'):
- if self.drag_data == None:
- return False
- item = self.drag_data
- new_parent = menus[path][2]
- treeview.get_selection().select_path(path)
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.copyItem(item, new_parent)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- if self.editor.moveMenu(item, new_parent) == False:
- self.loadUpdates()
- else:
- context.finish(False, False, etime)
- context.finish(True, True, etime)
- self.drag_data = None
-
- def on_item_tree_show_toggled(self, cell, path):
- item = self.item_store[path][3]
- if item.get_type() == gmenu.TYPE_SEPARATOR:
- return
- if self.item_store[path][0]:
- self.editor.setVisible(item, False)
- else:
- self.editor.setVisible(item, True)
- 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()
- if iter is None:
- return
- item = items[iter][3]
- self.tree.get_object('edit_delete').set_sensitive(True)
- self.tree.get_object('new_separator_button').set_sensitive(True)
- self.tree.get_object('delete_button').set_sensitive(True)
- if self.editor.canRevert(item):
- self.tree.get_object('edit_revert_to_original').set_sensitive(True)
- else:
- self.tree.get_object('edit_revert_to_original').set_sensitive(False)
- if not item.get_type() == gmenu.TYPE_SEPARATOR:
- self.tree.get_object('edit_properties').set_sensitive(True)
- self.tree.get_object('properties_button').set_sensitive(True)
- else:
- self.tree.get_object('edit_properties').set_sensitive(False)
- self.tree.get_object('properties_button').set_sensitive(False)
-
- # If first item...
- if items.get_path(iter)[0] == 0:
- self.tree.get_object('move_up_button').set_sensitive(False)
- else:
- self.tree.get_object('move_up_button').set_sensitive(True)
-
- # If last item...
- if items.get_path(iter)[0] == (len(items)-1):
- self.tree.get_object('move_down_button').set_sensitive(False)
- else:
- self.tree.get_object('move_down_button').set_sensitive(True)
-
- def on_item_tree_row_activated(self, treeview, path, column):
- self.on_edit_properties_activate(None)
-
- def on_item_tree_popup_menu(self, item_tree, event=None):
- model, iter = item_tree.get_selection().get_selected()
- if event:
- #don't show if it's not the right mouse button
- if event.button != 3:
- return
- button = event.button
- event_time = event.time
- info = item_tree.get_path_at_pos(int(event.x), int(event.y))
- if info != None:
- path, col, cellx, celly = info
- item_tree.grab_focus()
- item_tree.set_cursor(path, col, 0)
- else:
- path = model.get_path(iter)
- button = 0
- event_time = 0
- item_tree.grab_focus()
- item_tree.set_cursor(path, item_tree.get_columns()[0], 0)
- popup = self.tree.get_object('edit_menu')
- popup.popup(None, None, None, button, event_time)
- #without this shift-f10 won't work
- return True
-
- def on_item_tree_drag_data_get(self, treeview, context, selection, target_id, etime):
- items, iter = treeview.get_selection().get_selected()
- self.drag_data = items[iter][3]
-
- def on_item_tree_drag_data_received(self, treeview, context, x, y, selection, info, etime):
- items = treeview.get_model()
- types = (gtk.TREE_VIEW_DROP_BEFORE, gtk.TREE_VIEW_DROP_INTO_OR_BEFORE)
- if selection.target == 'ALACARTE_ITEM_ROW':
- drop_info = treeview.get_dest_row_at_pos(x, y)
- before = None
- after = None
- if self.drag_data == None:
- return False
- item = self.drag_data
- if drop_info:
- path, position = drop_info
- if position in types:
- before = items[path][3]
- else:
- after = items[path][3]
- else:
- path = (len(items) - 1,)
- after = items[path][3]
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.moveItem(item, item.get_parent(), before, after)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- if self.editor.moveMenu(item, item.get_parent(), before, after) == False:
- self.loadUpdates()
- elif item.get_type() == gmenu.TYPE_SEPARATOR:
- self.editor.moveSeparator(item, item.get_parent(), before, after)
- context.finish(True, True, etime)
- elif selection.target == 'text/plain':
- if selection.data == None:
- return False
- menus, iter = self.tree.get_object('menu_tree').get_selection().get_selected()
- parent = menus[iter][2]
- drop_info = treeview.get_dest_row_at_pos(x, y)
- before = None
- after = None
- if drop_info:
- path, position = drop_info
- if position in types:
- before = items[path][3]
- else:
- after = items[path][3]
- else:
- path = (len(items) - 1,)
- after = items[path][3]
- file_path = urllib.unquote(selection.data).strip()
- if not file_path.startswith('file:'):
- return
- myfile = gio.File(uri=file_path)
- file_info = myfile.query_info(gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
- content_type = file_info.get_content_type()
- if content_type == 'application/x-desktop':
- input_stream = myfile.read()
- open('/tmp/alacarte-dnd.desktop', 'w').write(input_stream.read())
- parser = util.DesktopParser('/tmp/alacarte-dnd.desktop')
- self.editor.createItem(parent, parser.get('Icon'), parser.get('Name', self.editor.locale), parser.get('Comment', self.editor.locale), parser.get('Exec'), parser.get('Terminal'), before, after)
- elif content_type in ('application/x-shellscript', 'application/x-executable'):
- self.editor.createItem(parent, None, os.path.split(file_path)[1].strip(), None, file_path.replace('file://', '').strip(), False, before, after)
- self.drag_data = None
-
- def on_item_tree_key_press_event(self, item_tree, event):
- if event.keyval == gtk.keysyms.Delete:
- self.on_edit_delete_activate(item_tree)
-
- def on_move_up_button_clicked(self, button):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- path = items.get_path(iter)
- #at top, can't move up
- if path[0] == 0:
- return
- item = items[path][3]
- before = items[(path[0] - 1,)][3]
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.moveItem(item, item.get_parent(), before=before)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- self.editor.moveMenu(item, item.get_parent(), before=before)
- elif item.get_type() == gmenu.TYPE_SEPARATOR:
- self.editor.moveSeparator(item, item.get_parent(), before=before)
-
- def on_move_down_button_clicked(self, button):
- item_tree = self.tree.get_object('item_tree')
- items, iter = item_tree.get_selection().get_selected()
- if not iter:
- return
- path = items.get_path(iter)
- #at bottom, can't move down
- if path[0] == (len(items) - 1):
- return
- item = items[path][3]
- after = items[path][3]
- if item.get_type() == gmenu.TYPE_ENTRY:
- self.editor.moveItem(item, item.get_parent(), after=after)
- elif item.get_type() == gmenu.TYPE_DIRECTORY:
- self.editor.moveMenu(item, item.get_parent(), after=after)
- elif item.get_type() == gmenu.TYPE_SEPARATOR:
- self.editor.moveSeparator(item, item.get_parent(), after=after)
-
- def on_mainwindow_undo(self, accelgroup, window, keyval, modifier):
- self.editor.undo()
-
- def on_mainwindow_redo(self, accelgroup, window, keyval, modifier):
- self.editor.redo()
-
- def on_revert_button_clicked(self, button):
- dialog = self.tree.get_object('revertdialog')
- dialog.set_transient_for(self.tree.get_object('mainwindow'))
- dialog.show_all()
- if dialog.run() == gtk.RESPONSE_YES:
- self.editor.revert()
- dialog.hide()
-
- def on_close_button_clicked(self, button):
- try:
- self.tree.get_object('mainwindow').hide()
- except:
- pass
- gobject.timeout_add(10, self.quit)
-
- def on_properties_button_clicked(self, button):
- self.on_edit_properties_activate(None)
- def on_delete_button_clicked(self, button):
- self.on_edit_delete_activate(None)
-
- def on_style_set(self, *args):
- self.loadUpdates()
-
- def quit(self):
- self.editor.quit()
- gtk.main_quit()
+class MainWindow(object):
+ timer = None
+ #hack to make editing menu properties work
+ edit_pool = []
+
+ def __init__(self, datadir, version):
+ self.file_path = datadir
+ self.version = version
+ self.editor = MenuEditor()
+ self.editor.tree.connect("changed", self.menuChanged)
+ Gtk.Window.set_default_icon_name('alacarte')
+ self.tree = Gtk.Builder()
+ self.tree.set_translation_domain(config.GETTEXT_PACKAGE)
+ self.tree.add_from_file('/usr/lib/cinnamon-menu-editor/cinnamon-menu-editor.ui')
+ self.tree.connect_signals(self)
+ self.setupMenuTree()
+ self.setupItemTree()
+ self.tree.get_object('edit_delete').set_sensitive(False)
+ self.tree.get_object('edit_properties').set_sensitive(False)
+ 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)
+ accelgroup = Gtk.AccelGroup()
+ 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)
+
+ def run(self):
+ self.loadMenus()
+ self.tree.get_object('mainwindow').show_all()
+ Gtk.main()
+
+ def menuChanged(self, *a):
+ self.loadUpdates()
+
+ def loadUpdates(self):
+ 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
+ update_type = None
+ item_id = None
+ if iter:
+ update_items = True
+ if isinstance(items[iter][3], GMenu.TreeEntry):
+ item_id = items[iter][3].get_desktop_file_id()
+ update_type = GMenu.TreeItemType.ENTRY
+ elif isinstance(items[iter][3], GMenu.TreeDirectory):
+ item_id = os.path.split(items[iter][3].get_desktop_file_path())[1]
+ update_type = GMenu.TreeItemType.DIRECTORY
+ elif isinstance(items[iter][3], GMenu.TreeSeparator):
+ item_id = items.get_path(iter)
+ update_type = GMenu.TreeItemType.SEPARATOR
+ menus, iter = menu_tree.get_selection().get_selected()
+ update_menus = False
+ menu_id = None
+ if iter:
+ if menus[iter][2].get_desktop_file_path():
+ menu_id = os.path.split(menus[iter][2].get_desktop_file_path())[1]
+ else:
+ menu_id = menus[iter][2].get_menu_id()
+ update_menus = True
+ self.loadMenus()
+ #find current menu in new tree
+ if update_menus:
+ menu_tree.get_model().foreach(self.findMenu, menu_id)
+ menus, iter = menu_tree.get_selection().get_selected()
+ if iter:
+ self.on_menu_tree_cursor_changed(menu_tree)
+ #find current item in new list
+ if update_items:
+ i = 0
+ for item in item_tree.get_model():
+ found = False
+ if update_type != GMenu.TreeItemType.SEPARATOR:
+ if isinstance (item[3], GMenu.TreeEntry) and item[3].get_desktop_file_id() == item_id:
+ found = True
+ if isinstance (item[3], GMenu.TreeDirectory) and item[3].get_desktop_file_path() and update_type == GMenu.TreeItemType.DIRECTORY:
+ if os.path.split(item[3].get_desktop_file_path())[1] == item_id:
+ found = True
+ if isinstance(item[3], GMenu.TreeSeparator):
+ if not isinstance(item_id, tuple):
+ #we may not skip the increment via "continue"
+ i += 1
+ continue
+ #separators have no id, have to find them manually
+ #probably won't work with two separators together
+ if (item_id[0] - 1,) == (i,):
+ found = True
+ elif (item_id[0] + 1,) == (i,):
+ found = True
+ elif (item_id[0],) == (i,):
+ found = True
+ if found:
+ item_tree.get_selection().select_path((i,))
+ self.on_item_tree_cursor_changed(item_tree)
+ break
+ i += 1
+ return False
+
+ def findMenu(self, menus, path, iter, menu_id):
+ if not menus[path][2].get_desktop_file_path():
+ if menu_id == menus[path][2].get_menu_id():
+ menu_tree = self.tree.get_object('menu_tree')
+ menu_tree.expand_to_path(path)
+ menu_tree.get_selection().select_path(path)
+ return True
+ return False
+ if os.path.split(menus[path][2].get_desktop_file_path())[1] == menu_id:
+ menu_tree = self.tree.get_object('menu_tree')
+ menu_tree.expand_to_path(path)
+ menu_tree.get_selection().select_path(path)
+ return True
+
+ def setupMenuTree(self):
+ self.menu_store = Gtk.TreeStore(GdkPixbuf.Pixbuf, str, object)
+ menus = self.tree.get_object('menu_tree')
+ column = Gtk.TreeViewColumn(_('Name'))
+ column.set_spacing(4)
+ cell = Gtk.CellRendererPixbuf()
+ column.pack_start(cell, False)
+ column.add_attribute(cell, 'pixbuf', 0)
+ cell = Gtk.CellRendererText()
+ column.pack_start(cell, True)
+ column.add_attribute(cell, 'markup', 1)
+ menus.append_column(column)
+ menus.get_selection().set_mode(Gtk.SelectionMode.BROWSE)
+
+ def setupItemTree(self):
+ items = self.tree.get_object('item_tree')
+ column = Gtk.TreeViewColumn(_('Show'))
+ cell = Gtk.CellRendererToggle()
+ cell.connect('toggled', self.on_item_tree_show_toggled)
+ column.pack_start(cell, True)
+ 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)
+ column = Gtk.TreeViewColumn(_('Item'))
+ column.set_spacing(4)
+ cell = Gtk.CellRendererPixbuf()
+ column.pack_start(cell, False)
+ column.add_attribute(cell, 'pixbuf', 1)
+ cell = Gtk.CellRendererText()
+ column.pack_start(cell, True)
+ 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)
+
+ def _cell_data_toggle_func(self, tree_column, renderer, model, treeiter, data=None):
+ if isinstance(model[treeiter][3], GMenu.TreeSeparator):
+ renderer.set_property('visible', False)
+ else:
+ renderer.set_property('visible', True)
+
+ def loadMenus(self):
+ self.menu_store.clear()
+ self.loadMenu({ None: None })
+
+ menu_tree = self.tree.get_object('menu_tree')
+ menu_tree.set_model(self.menu_store)
+ for menu in self.menu_store:
+ menu_tree.expand_to_path(menu.path)
+ menu_tree.get_selection().select_path((0,))
+ self.on_menu_tree_cursor_changed(menu_tree)
+
+ def loadMenu(self, iters, parent=None):
+ for menu, show in self.editor.getMenus(parent):
+ name = cgi.escape(menu.get_name())
+ if not show:
+ name = "<small><i>%s</i></small>" % (name,)
+
+ icon = util.getIcon(menu)
+ iters[menu] = self.menu_store.append(iters[parent], (icon, name, menu))
+ self.loadMenu(iters, menu)
+
+ def loadItems(self, menu):
+ self.item_store.clear()
+ for item, show in self.editor.getItems(menu):
+ icon = util.getIcon(item)
+ if isinstance(item, GMenu.TreeDirectory):
+ name = item.get_name()
+ elif isinstance(item, GMenu.TreeEntry):
+ name = item.get_app_info().get_display_name()
+ elif isinstance(item, GMenu.TreeSeparator):
+ name = '---'
+ else:
+ assert False, 'should not be reached'
+
+ name = cgi.escape(name)
+ if not show:
+ name = "<small><i>%s</i></small>" % (name,)
+
+ self.item_store.append((show, icon, name, item))
+
+ #this is a little timeout callback to insert new items after
+ #gnome-desktop-item-edit has finished running
+ def waitForNewItemProcess(self, process, parent_id, file_path):
+ if process.poll() is not None:
+ if os.path.isfile(file_path):
+ self.editor.insertExternalItem(os.path.split(file_path)[1], parent_id)
+ return False
+ return True
+
+ def waitForNewMenuProcess(self, process, parent_id, file_path):
+ if process.poll() is not None:
+ if os.path.isfile(file_path):
+ self.editor.insertExternalMenu(os.path.split(file_path)[1], parent_id)
+ return False
+ return True
+
+ #this callback keeps you from editing the same item twice
+ def waitForEditProcess(self, process, file_path):
+ if process.poll() is not None:
+ self.edit_pool.remove(file_path)
+ return False
+ return True
+
+ def on_new_menu_button_clicked(self, button):
+ menu_tree = self.tree.get_object('menu_tree')
+ menus, iter = menu_tree.get_selection().get_selected()
+ if not iter:
+ parent = menus[(0,)][2]
+ menu_tree.expand_to_path((0,))
+ menu_tree.get_selection().select_path((0,))
+ else:
+ parent = menus[iter][2]
+ file_path = os.path.join(util.getUserDirectoryPath(), util.getUniqueFileId('alacarte-made', '.directory'))
+ process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
+ GObject.timeout_add(100, self.waitForNewMenuProcess, process, parent.get_menu_id(), file_path)
+
+ def on_new_item_button_clicked(self, button):
+ menu_tree = self.tree.get_object('menu_tree')
+ menus, iter = menu_tree.get_selection().get_selected()
+ if not iter:
+ parent = menus[(0,)][2]
+ menu_tree.expand_to_path((0,))
+ menu_tree.get_selection().select_path((0,))
+ else:
+ parent = menus[iter][2]
+ file_path = os.path.join(util.getUserItemPath(), util.getUniqueFileId('alacarte-made', '.desktop'))
+ process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
+ GObject.timeout_add(100, self.waitForNewItemProcess, process, parent.get_menu_id(), file_path)
+
+ def on_new_separator_button_clicked(self, button):
+ item_tree = self.tree.get_object('item_tree')
+ items, iter = item_tree.get_selection().get_selected()
+ if not iter:
+ return
+ else:
+ after = items[iter][3]
+ menu_tree = self.tree.get_object('menu_tree')
+ menus, iter = menu_tree.get_selection().get_selected()
+ parent = menus[iter][2]
+ self.editor.createSeparator(parent, after=after)
+
+ def on_edit_delete_activate(self, menu):
+ item_tree = self.tree.get_object('item_tree')
+ items, iter = item_tree.get_selection().get_selected()
+ if not iter:
+ return
+ item = items[iter][3]
+ if isinstance(item, GMenu.TreeEntry):
+ self.editor.deleteItem(item)
+ elif isinstance(item, GMenu.TreeDirectory):
+ self.editor.deleteMenu(item)
+ elif isinstance(item, GMenu.TreeSeparator):
+ self.editor.deleteSeparator(item)
+
+ def on_edit_properties_activate(self, menu):
+ item_tree = self.tree.get_object('item_tree')
+ items, iter = item_tree.get_selection().get_selected()
+ if not iter:
+ return
+ item = items[iter][3]
+ if not isinstance(item, GMenu.TreeEntry) and not isinstance(item, GMenu.TreeDirectory):
+ return
+
+ if isinstance(item, GMenu.TreeEntry):
+ file_path = os.path.join(util.getUserItemPath(), item.get_desktop_file_id())
+ file_type = 'Item'
+ elif isinstance(item, GMenu.TreeDirectory):
+ file_path = os.path.join(util.getUserDirectoryPath(), os.path.split(item.get_desktop_file_path())[1])
+ file_type = 'Menu'
+
+ if not os.path.isfile(file_path):
+ data = open(item.get_desktop_file_path()).read()
+ open(file_path, 'w').write(data)
+
+ if file_path not in self.edit_pool:
+ self.edit_pool.append(file_path)
+ process = subprocess.Popen(['gnome-desktop-item-edit', file_path], env=os.environ)
+ GObject.timeout_add(100, self.waitForEditProcess, process, file_path)
+
+ def on_menu_tree_cursor_changed(self, treeview):
+ 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)
+ item_tree = self.tree.get_object('item_tree')
+ item_tree.get_selection().unselect_all()
+ self.loadItems(self.menu_store[menu_path][2])
+ self.tree.get_object('edit_delete').set_sensitive(False)
+ self.tree.get_object('edit_properties').set_sensitive(False)
+ 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_item_tree_show_toggled(self, cell, path):
+ item = self.item_store[path][3]
+ if isinstance(item, GMenu.TreeSeparator):
+ return
+ if self.item_store[path][0]:
+ self.editor.setVisible(item, False)
+ else:
+ self.editor.setVisible(item, True)
+ self.item_store[path][0] = not self.item_store[path][0]
+
+ def on_item_tree_cursor_changed(self, treeview):
+ selection = treeview.get_selection()
+ if selection is None:
+ return
+ items, iter = selection.get_selected()
+ if iter is None:
+ return
+
+ item = items[iter][3]
+ self.tree.get_object('edit_delete').set_sensitive(True)
+ self.tree.get_object('new_separator_button').set_sensitive(True)
+ self.tree.get_object('delete_button').set_sensitive(True)
+
+ can_edit = not isinstance(item, GMenu.TreeSeparator)
+ self.tree.get_object('edit_properties').set_sensitive(can_edit)
+ self.tree.get_object('properties_button').set_sensitive(can_edit)
+
+ index = items.get_path(iter).get_indices()[0]
+ can_go_up = index > 0
+ can_go_down = index < len(items) - 1
+ self.tree.get_object('move_up_button').set_sensitive(can_go_up)
+ self.tree.get_object('move_down_button').set_sensitive(can_go_down)
+
+ def on_item_tree_row_activated(self, treeview, path, column):
+ self.on_edit_properties_activate(None)
+
+ def on_item_tree_popup_menu(self, item_tree, event=None):
+ model, iter = item_tree.get_selection().get_selected()
+ if event:
+ #don't show if it's not the right mouse button
+ if event.button != 3:
+ return
+ button = event.button
+ event_time = event.time
+ info = item_tree.get_path_at_pos(int(event.x), int(event.y))
+ if info is not None:
+ path, col, cellx, celly = info
+ item_tree.grab_focus()
+ item_tree.set_cursor(path, col, 0)
+ else:
+ path = model.get_path(iter)
+ button = 0
+ event_time = 0
+ item_tree.grab_focus()
+ item_tree.set_cursor(path, item_tree.get_columns()[0], 0)
+ popup = self.tree.get_object('edit_menu')
+ popup.popup(None, None, None, None, button, event_time)
+ #without this shift-f10 won't work
+ return True
+
+ def on_item_tree_key_press_event(self, item_tree, event):
+ if event.keyval == Gdk.KEY_Delete:
+ self.on_edit_delete_activate(item_tree)
+
+ def on_move_up_button_clicked(self, button):
+ item_tree = self.tree.get_object('item_tree')
+ items, iter = item_tree.get_selection().get_selected()
+ if not iter:
+ return
+ path = items.get_path(iter)
+ #at top, can't move up
+ if path.get_indices()[0] == 0:
+ return
+ item = items[path][3]
+ before = items[(path.get_indices()[0] - 1,)][3]
+ self.editor.moveItem(item.get_parent(), item, before=before)
+
+ def on_move_down_button_clicked(self, button):
+ item_tree = self.tree.get_object('item_tree')
+ items, iter = item_tree.get_selection().get_selected()
+ if not iter:
+ return
+ path = items.get_path(iter)
+ #at bottom, can't move down
+ if path.get_indices()[0] == (len(items) - 1):
+ return
+ item = items[path][3]
+ after = items[path][3]
+ self.editor.moveItem(item.get_parent(), item, after=after)
+
+ def on_help_button_clicked(self, *args):
+ Gtk.show_uri(Gdk.Screen.get_default(), "ghelp:user-guide#menu-editor", Gtk.get_current_event_time())
+
+ def on_restore_button_clicked(self, button):
+ self.editor.restoreToSystem()
+
+ def on_close_button_clicked(self, button):
+ self.quit()
+
+ def on_properties_button_clicked(self, button):
+ self.on_edit_properties_activate(None)
+ def on_delete_button_clicked(self, button):
+ self.on_edit_delete_activate(None)
+
+ def quit(self):
+ Gtk.main_quit()
--- a/files/usr/lib/cinnamon-menu-editor/Alacarte/MenuEditor.py
+++ b/files/usr/lib/cinnamon-menu-editor/Alacarte/MenuEditor.py
@@ -16,731 +16,555 @@
# 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 gmenu
+import os
+import xml.dom.minidom
+import xml.parsers.expat
+from gi.repository import GMenu, GLib
from Alacarte 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 = gmenu.lookup_tree('cinnamon-applications.menu', gmenu.FLAGS_SHOW_EMPTY|gmenu.FLAGS_INCLUDE_EXCLUDED|gmenu.FLAGS_INCLUDE_NODISPLAY|gmenu.FLAGS_SHOW_ALL_SEPARATORS)
- self.applications.visible_tree = gmenu.lookup_tree('cinnamon-applications.menu')
- self.applications.tree.sort_key = gmenu.SORT_DISPLAY_NAME
- self.applications.visible_tree.sort_key = gmenu.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.save(True)
-
- def save(self, from_loading=False):
- for menu in ('applications',):
- 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',):
- 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() == gmenu.TYPE_DIRECTORY:
- self.revertTree(child)
- elif child.get_type() == gmenu.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',):
- 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',):
- 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
- else:
- for menu in parent.get_contents():
- if menu.get_type() == gmenu.TYPE_DIRECTORY:
- yield (menu, self.__isVisible(menu))
-
- def getItems(self, menu):
- for item in menu.get_contents():
- if item.get_type() == gmenu.TYPE_SEPARATOR:
- yield (item, True)
- else:
- if item.get_type() == gmenu.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() == gmenu.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() == gmenu.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() == gmenu.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() == gmenu.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, before, after)
-
- def insertExternalItem(self, file_id, parent, before=None, after=None):
- 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.__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, 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() == gmenu.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() == gmenu.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):
- return self.applications
-
- def __findMenu(self, menu_id, parent=None):
- if parent == None:
- return self.__findMenu(menu_id, self.applications.tree.root)
- if menu_id == self.applications.tree.root.menu_id:
- return self.applications.tree.root
- for item in parent.get_contents():
- if item.get_type() == gmenu.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() == gmenu.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
- if item.get_type() == gmenu.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() == gmenu.TYPE_DIRECTORY:
- layout.parseMenuname(item.get_menu_id())
- elif item.get_type() == gmenu.TYPE_ENTRY:
- layout.parseFilename(item.get_desktop_file_id())
- elif item.get_type() == gmenu.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
- current = parent.contents.index(item)
- if after:
- index = parent.contents.index(after)
- if current > index:
- index += 1
- elif before:
- index = parent.contents.index(before)
- if current < index:
- index -= 1
- 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])
+class MenuEditor(object):
+ def __init__(self, name='cinnamon-applications.menu'):
+ self.name = name
+
+ self.tree = GMenu.Tree.new(name, GMenu.TreeFlags.SHOW_EMPTY|GMenu.TreeFlags.INCLUDE_EXCLUDED|GMenu.TreeFlags.INCLUDE_NODISPLAY|GMenu.TreeFlags.SHOW_ALL_SEPARATORS|GMenu.TreeFlags.SORT_DISPLAY_NAME)
+ self.tree.connect('changed', self.menuChanged)
+ self.load()
+
+ self.path = os.path.join(util.getUserMenuPath(), self.tree.props.menu_basename)
+ self.loadDOM()
+
+ def loadDOM(self):
+ try:
+ self.dom = xml.dom.minidom.parse(self.path)
+ except (IOError, xml.parsers.expat.ExpatError), e:
+ self.dom = xml.dom.minidom.parseString(util.getUserMenuXml(self.tree))
+ util.removeWhitespaceNodes(self.dom)
+
+ def load(self):
+ if not self.tree.load_sync():
+ raise ValueError("can not load menu tree %r" % (self.name,))
+
+ def menuChanged(self, *a):
+ self.load()
+
+ def save(self):
+ fd = open(self.path, 'w')
+ fd.write(self.dom.toprettyxml())
+ fd.close()
+
+ def restoreToSystem(self):
+ self.restoreTree(self.tree.get_root_directory())
+ path = os.path.join(util.getUserMenuPath(), os.path.basename(self.tree.get_canonical_menu_path()))
+ try:
+ os.unlink(path)
+ except OSError:
+ pass
+
+ self.loadDOM()
+ self.save()
+
+ def restoreTree(self, menu):
+ item_iter = menu.iter()
+ item_type = item_iter.next()
+ while item_type != GMenu.TreeItemType.INVALID:
+ if item_type == GMenu.TreeItemType.DIRECTORY:
+ item = item_iter.get_directory()
+ self.restoreTree(item)
+ elif item_type == GMenu.TreeItemType.ENTRY:
+ item = item_iter.get_entry()
+ self.restoreItem(item)
+ item_type = item_iter.next()
+ self.restoreMenu(menu)
+
+ def restoreItem(self, item):
+ if not self.canRevert(item):
+ return
+ try:
+ os.remove(item.get_desktop_file_path())
+ except OSError:
+ pass
+ self.save()
+
+ def restoreMenu(self, menu):
+ if not self.canRevert(menu):
+ return
+ #wtf happened here? oh well, just bail
+ if not menu.get_desktop_file_path():
+ return
+ 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 getMenus(self, parent):
+ if parent is None:
+ yield (self.tree.get_root_directory(), True)
+ return
+
+ item_iter = parent.iter()
+ item_type = item_iter.next()
+ while item_type != GMenu.TreeItemType.INVALID:
+ if item_type == GMenu.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 != GMenu.TreeItemType.INVALID:
+ item = None
+ if item_type == GMenu.TreeItemType.DIRECTORY:
+ item = item_iter.get_directory()
+ elif item_type == GMenu.TreeItemType.ENTRY:
+ item = item_iter.get_entry()
+ elif item_type == GMenu.TreeItemType.HEADER:
+ item = item_iter.get_header()
+ elif item_type == GMenu.TreeItemType.ALIAS:
+ item = item_iter.get_alias()
+ elif item_type == GMenu.TreeItemType.SEPARATOR:
+ item = item_iter.get_separator()
+ if item:
+ contents.append(item)
+ item_type = item_iter.next()
+ return contents
+
+ def getItems(self, menu):
+ item_iter = menu.iter()
+ item_type = item_iter.next()
+ while item_type != GMenu.TreeItemType.INVALID:
+ item = None
+ if item_type == GMenu.TreeItemType.ENTRY:
+ item = item_iter.get_entry()
+ elif item_type == GMenu.TreeItemType.DIRECTORY:
+ item = item_iter.get_directory()
+ elif item_type == GMenu.TreeItemType.HEADER:
+ item = item_iter.get_header()
+ elif item_type == GMenu.TreeItemType.ALIAS:
+ item = item_iter.get_alias()
+ elif item_type == GMenu.TreeItemType.SEPARATOR:
+ item = item_iter.get_separator()
+ yield (item, self.isVisible(item))
+ item_type = item_iter.next()
+
+ def canRevert(self, item):
+ if isinstance(item, GMenu.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 isinstance(item, GMenu.TreeDirectory):
+ 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.dom
+ if isinstance(item, GMenu.TreeEntry):
+ 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 isinstance(item, GMenu.TreeDirectory):
+ item_iter = item.iter()
+ first_child_type = item_iter.next()
+ #don't mess with it if it's empty
+ if first_child_type == GMenu.TreeItemType.INVALID:
+ 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.get_menu_id(), before, after)
+
+ def insertExternalItem(self, file_id, parent_id, before=None, after=None):
+ parent = self.findMenu(parent_id)
+ dom = self.dom
+ self.addItem(parent, file_id, dom)
+ self.positionItem(parent, ('Item', file_id), before, after)
+ 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.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.save()
+
+ def createSeparator(self, parent, before=None, after=None):
+ self.positionItem(parent, ('Separator',), before, after)
+ 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
+ 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:
+ parent = item.get_parent()
+ self.writeItem(item, Icon=icon, Name=name, Comment=comment, Exec=command, Terminal=use_term)
+ if final:
+ dom = self.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.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.save()
+
+ def copyItem(self, item, new_parent, before=None, after=None):
+ dom = self.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))
+
+ 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()
+
+ f = open(out_path, 'w')
+ f.write(contents)
+ f.close()
+
+ self.addItem(new_parent, file_id, dom)
+ self.positionItem(new_parent, ('Item', file_id), before, after)
+ self.save()
+ return file_id
+
+ def deleteItem(self, item):
+ self.writeItem(item, Hidden=True)
+ self.save()
+
+ def deleteMenu(self, menu):
+ dom = self.dom
+ menu_xml = self.getXmlMenu(self.getPath(menu), dom.documentElement, dom)
+ self.addDeleted(menu_xml, dom)
+ self.save()
+
+ def deleteSeparator(self, item):
+ parent = item.get_parent()
+ contents = self.getContents(parent)
+ contents.remove(item)
+ layout = self.createLayout(contents)
+ dom = self.dom
+ menu_xml = self.getXmlMenu(self.getPath(parent), dom.documentElement, dom)
+ self.addXmlLayout(menu_xml, layout, dom)
+ self.save()
+
+ def findMenu(self, menu_id, parent=None):
+ if parent is None:
+ parent = self.tree.get_root_directory()
+
+ if menu_id == parent.get_menu_id():
+ return parent
+
+ item_iter = parent.iter()
+ item_type = item_iter.next()
+ while item_type != GMenu.TreeItemType.INVALID:
+ if item_type == GMenu.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 isinstance(item, GMenu.TreeEntry):
+ app_info = item.get_app_info()
+ return not (item.get_is_excluded() or app_info.get_nodisplay())
+ elif isinstance(item, GMenu.TreeDirectory):
+ return not item.get_is_nodisplay()
+ 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, alacarte 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):
+ 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 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()
+
+ f = open(os.path.join(util.getUserItemPath(), file_id), 'w')
+ f.write(contents)
+ f.close()
+ 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()
+
+ f = open(os.path.join(util.getUserDirectoryPath(), file_id), 'w')
+ f.write(contents)
+ f.close()
+ 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:
+ 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.append(('Merge', 'menus'))
+ for item in items:
+ if isinstance(item, GMenu.TreeDirectory):
+ layout.append(('Menuname', item.get_menu_id()))
+ elif isinstance(item, GMenu.TreeEntry):
+ layout.append(('Filename', item.get_desktop_file_id()))
+ elif isinstance(item, GMenu.TreeSeparator):
+ layout.append(('Separator',))
+ else:
+ layout.append(item)
+ layout.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 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 = contents.index(after) + 1
+ elif before:
+ index = contents.index(before)
+ else:
+ # append the item to the list
+ 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)):
+ index -= 1
+ contents.remove(item)
+ contents.insert(index, item)
+ layout = self.createLayout(contents)
+ dom = self.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)
--- a/files/usr/lib/cinnamon-menu-editor/Alacarte/util.py
+++ b/files/usr/lib/cinnamon-menu-editor/Alacarte/util.py
@@ -17,228 +17,158 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
-import gtk, gmenu
-from ConfigParser import ConfigParser
-
-class DesktopParser(ConfigParser):
- def __init__(self, filename=None, file_type='Application'):
- ConfigParser.__init__(self)
- self.filename = filename
- self.file_type = file_type
- if filename:
- if len(self.read(filename)) == 0:
- #file doesn't exist
- self.add_section('Desktop Entry')
- else:
- self.add_section('Desktop Entry')
- self._list_separator = ';'
-
- def optionxform(self, option):
- #makes keys not be lowercase
- return option
-
- def get(self, option, locale=None):
- locale_option = option + '[%s]' % locale
- try:
- value = ConfigParser.get(self, 'Desktop Entry', locale_option)
- except:
- try:
- value = ConfigParser.get(self, 'Desktop Entry', option)
- except:
- return None
- if self._list_separator in value:
- value = value.split(self._list_separator)
- if value == 'true':
- value = True
- if value == 'false':
- value = False
- return value
-
- def set(self, option, value, locale=None):
- if locale:
- option = option + '[%s]' % locale
- if value == True:
- value = 'true'
- if value == False:
- value = 'false'
- if isinstance(value, tuple) or isinstance(value, list):
- value = self._list_separator.join(value) + ';'
- ConfigParser.set(self, 'Desktop Entry', option, value)
-
- def write(self, file_object):
- file_object.write('[Desktop Entry]\n')
- items = []
- if not self.filename:
- file_object.write('Encoding=UTF-8\n')
- file_object.write('Type=' + str(self.file_type) + '\n')
- for item in self.items('Desktop Entry'):
- items.append(item)
- items.sort()
- for item in items:
- file_object.write(item[0] + '=' + item[1] + '\n')
+import xml.dom.minidom
+from collections import Sequence
+from gi.repository import Gtk, GdkPixbuf, GMenu, GLib
+
+# XXX: look into pygobject error marshalling
+from gi._glib import GError
+
+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():
+ if item is None:
+ continue
+
+ if isinstance(item, bool):
+ keyfile.set_boolean(DESKTOP_GROUP, key, item)
+ elif isinstance(item, Sequence):
+ keyfile.set_string_list(DESKTOP_GROUP, key, item)
+ elif isinstance(item, basestring):
+ keyfile.set_string(DESKTOP_GROUP, key, item)
def getUniqueFileId(name, extension):
- append = 0
- while 1:
- if append == 0:
- filename = name + extension
- else:
- filename = name + '-' + str(append) + extension
- if extension == '.desktop':
- path = getUserItemPath()
- if not os.path.isfile(os.path.join(path, filename)) and not getItemPath(filename):
- break
- elif extension == '.directory':
- path = getUserDirectoryPath()
- if not os.path.isfile(os.path.join(path, filename)) and not getDirectoryPath(filename):
- break
- append += 1
- return filename
+ append = 0
+ while 1:
+ if append == 0:
+ filename = name + extension
+ else:
+ filename = name + '-' + str(append) + extension
+ if extension == '.desktop':
+ path = getUserItemPath()
+ if not os.path.isfile(os.path.join(path, filename)) and not getItemPath(filename):
+ break
+ elif extension == '.directory':
+ path = getUserDirectoryPath()
+ if not os.path.isfile(os.path.join(path, filename)) and not getDirectoryPath(filename):
+ break
+ append += 1
+ return filename
def getUniqueRedoFile(filepath):
- append = 0
- while 1:
- new_filepath = filepath + '.redo-' + str(append)
- if not os.path.isfile(new_filepath):
- break
- else:
- append += 1
- return new_filepath
+ append = 0
+ while 1:
+ new_filepath = filepath + '.redo-' + str(append)
+ if not os.path.isfile(new_filepath):
+ break
+ else:
+ append += 1
+ return new_filepath
def getUniqueUndoFile(filepath):
- filename, extension = os.path.split(filepath)[1].rsplit('.', 1)
- append = 0
- while 1:
- if extension == 'desktop':
- path = getUserItemPath()
- elif extension == 'directory':
- path = getUserDirectoryPath()
- elif extension == 'menu':
- path = getUserMenuPath()
- new_filepath = os.path.join(path, filename + '.' + extension + '.undo-' + str(append))
- if not os.path.isfile(new_filepath):
- break
- else:
- append += 1
- return new_filepath
-
-def getUserMenuPath():
- menu_dir = None
- if os.environ.has_key('XDG_CONFIG_HOME'):
- menu_dir = os.path.join(os.environ['XDG_CONFIG_HOME'], 'menus')
- else:
- menu_dir = os.path.join(os.environ['HOME'], '.config', 'menus')
- #move .config out of the way if it's not a dir, it shouldn't be there
- if os.path.isfile(os.path.split(menu_dir)[0]):
- os.rename(os.path.split(menu_dir)[0], os.path.split(menu_dir)[0] + '.old')
- if not os.path.isdir(menu_dir):
- os.makedirs(menu_dir)
- return menu_dir
+ filename, extension = os.path.split(filepath)[1].rsplit('.', 1)
+ append = 0
+ while 1:
+ if extension == 'desktop':
+ path = getUserItemPath()
+ elif extension == 'directory':
+ path = getUserDirectoryPath()
+ elif extension == 'menu':
+ path = getUserMenuPath()
+ new_filepath = os.path.join(path, filename + '.' + extension + '.undo-' + str(append))
+ if not os.path.isfile(new_filepath):
+ break
+ else:
+ append += 1
+ return new_filepath
def getItemPath(file_id):
- if os.environ.has_key('XDG_DATA_DIRS'):
- for system_path in os.environ['XDG_DATA_DIRS'].split(':'):
- file_path = os.path.join(system_path, 'applications', file_id)
- if os.path.isfile(file_path):
- return file_path
- file_path = os.path.join('/', 'usr', 'share', 'applications', file_id)
- if os.path.isfile(file_path):
- return file_path
- return False
+ for path in GLib.get_system_data_dirs():
+ file_path = os.path.join(path, 'applications', file_id)
+ if os.path.isfile(file_path):
+ return file_path
+ return None
def getUserItemPath():
- item_dir = None
- if os.environ.has_key('XDG_DATA_HOME'):
- item_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'applications')
- else:
- item_dir = os.path.join(os.environ['HOME'], '.local', 'share', 'applications')
- if not os.path.isdir(item_dir):
- os.makedirs(item_dir)
- return item_dir
+ item_dir = os.path.join(GLib.get_user_data_dir(), 'applications')
+ if not os.path.isdir(item_dir):
+ os.makedirs(item_dir)
+ return item_dir
def getDirectoryPath(file_id):
- home = getUserDirectoryPath()
- file_path = os.path.join(home, file_id)
- if os.path.isfile(file_path):
- return file_path
- if os.environ.has_key('XDG_DATA_DIRS'):
- for system_path in os.environ['XDG_DATA_DIRS'].split(':'):
- file_path = os.path.join(system_path, 'desktop-directories', file_id)
- if os.path.isfile(file_path):
- return file_path
- file_path = os.path.join('/', 'usr', 'share', 'desktop-directories', file_id)
- if os.path.isfile(file_path):
- return file_path
- return False
+ for path in GLib.get_system_data_dirs():
+ file_path = os.path.join(path, 'desktop-directories', file_id)
+ if os.path.isfile(file_path):
+ return file_path
+ return None
def getUserDirectoryPath():
- menu_dir = None
- if os.environ.has_key('XDG_DATA_HOME'):
- menu_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'desktop-directories')
- else:
- menu_dir = os.path.join(os.environ['HOME'], '.local', 'share', 'desktop-directories')
- if not os.path.isdir(menu_dir):
- os.makedirs(menu_dir)
- return menu_dir
-
-def getSystemMenuPath(file_name):
- if os.environ.has_key('XDG_CONFIG_DIRS'):
- for system_path in os.environ['XDG_CONFIG_DIRS'].split(':'):
- file_path = os.path.join(system_path, 'menus', file_name)
- if os.path.isfile(file_path):
- return file_path
- file_path = os.path.join('/', 'etc', 'xdg', 'menus', file_name)
- if os.path.isfile(file_path):
- return file_path
- return False
+ menu_dir = os.path.join(GLib.get_user_data_dir(), 'desktop-directories')
+ if not os.path.isdir(menu_dir):
+ os.makedirs(menu_dir)
+ return menu_dir
+
+def getUserMenuPath():
+ menu_dir = os.path.join(GLib.get_user_config_dir(), 'menus')
+ if not os.path.isdir(menu_dir):
+ os.makedirs(menu_dir)
+ return menu_dir
+
+def getSystemMenuPath(file_id):
+ for path in GLib.get_system_config_dirs():
+ file_path = os.path.join(path, 'menus', file_id)
+ if os.path.isfile(file_path):
+ return file_path
+ return None
def getUserMenuXml(tree):
- system_file = getSystemMenuPath(tree.get_menu_file())
- name = tree.root.get_menu_id()
- menu_xml = "<!DOCTYPE Menu PUBLIC '-//freedesktop//DTD Menu 1.0//EN' 'http://standards.freedesktop.org/menu-spec/menu-1.0.dtd'>\n"
- menu_xml += "<Menu>\n <Name>" + name + "</Name>\n "
- menu_xml += "<MergeFile type=\"parent\">" + system_file + "</MergeFile>\n</Menu>\n"
- return menu_xml
-
-def getIcon(item, for_properties=False):
- pixbuf, path = None, None
- if item == None:
- if for_properties:
- return None, None
- return None
- if 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]
- icon_theme = gtk.icon_theme_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 = gtk.gdk.pixbuf_new_from_file_at_size(iconName, 24, 24)
- path = iconName
- except:
- pass
- if pixbuf == None:
- if for_properties:
- return None, None
- if item.get_type() == gmenu.TYPE_DIRECTORY:
- iconName = 'gnome-fs-directory'
- elif item.get_type() == gmenu.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 pixbuf == None:
- return None
- if pixbuf.get_width() != 24 or pixbuf.get_height() != 24:
- pixbuf = pixbuf.scale_simple(24, 24, gtk.gdk.INTERP_HYPER)
- if for_properties:
- return pixbuf, path
- return pixbuf
+ system_file = getSystemMenuPath(os.path.basename(tree.get_canonical_menu_path()))
+ name = tree.get_root_directory().get_menu_id()
+ menu_xml = "<!DOCTYPE Menu PUBLIC '-//freedesktop//DTD Menu 1.0//EN' 'http://standards.freedesktop.org/menu-spec/menu-1.0.dtd'>\n"
+ menu_xml += "<Menu>\n <Name>" + name + "</Name>\n "
+ menu_xml += "<MergeFile type=\"parent\">" + system_file + "</MergeFile>\n</Menu>\n"
+ return menu_xml
+
+def getIcon(item):
+ pixbuf = None
+ if item is None:
+ return None
+
+ if isinstance(item, GMenu.TreeDirectory):
+ gicon = item.get_icon()
+ elif isinstance(item, GMenu.TreeEntry):
+ app_info = item.get_app_info()
+ gicon = app_info.get_icon()
+ else:
+ return None
+
+ if gicon is None:
+ return None
+
+ icon_theme = Gtk.IconTheme.get_default()
+ info = icon_theme.lookup_by_gicon(gicon, 24, 0)
+ if info is None:
+ return None
+ try:
+ pixbuf = info.load_icon()
+ except GError:
+ return None
+ 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
+
+def removeWhitespaceNodes(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():
+ removeWhitespaceNodes(child)
+ for node in remove_list:
+ node.parentNode.removeChild(node)
--- a/files/usr/lib/cinnamon-menu-editor/cinnamon-menu-editor.ui
+++ b/files/usr/lib/cinnamon-menu-editor/cinnamon-menu-editor.ui
@@ -8,22 +8,14 @@
<object class="GtkAction" id="edit_properties">
<property name="stock_id">gtk-properties</property>
<property name="name">edit_properties</property>
- <signal handler="on_edit_properties_activate" last_modification_time="Sun, 23 Apr 2006 02:16:34 GMT" name="activate"/>
- </object>
- </child>
- <child>
- <object class="GtkAction" id="edit_revert_to_original">
- <property name="stock_id">gtk-revert-to-saved</property>
- <property name="name">edit_revert_to_original</property>
- <property name="label" translatable="yes">_Revert to Original</property>
- <signal handler="on_edit_revert_to_original_activate" last_modification_time="Sun, 23 Apr 2006 02:16:34 GMT" name="activate"/>
+ <signal handler="on_edit_properties_activate" name="activate"/>
</object>
</child>
<child>
<object class="GtkAction" id="edit_delete">
<property name="stock_id">gtk-delete</property>
<property name="name">edit_delete</property>
- <signal handler="on_edit_delete_activate" last_modification_time="Sun, 23 Apr 2006 02:16:34 GMT" name="activate"/>
+ <signal handler="on_edit_delete_activate" name="activate"/>
</object>
</child>
</object>
@@ -31,8 +23,6 @@
<ui>
<popup name="edit_menu">
<menuitem action="edit_properties"/>
- <menuitem action="edit_revert_to_original"/>
- <separator/>
<menuitem action="edit_delete"/>
</popup>
</ui>
@@ -49,8 +39,8 @@
</object>
<object class="GtkDialog" id="mainwindow">
<property name="border_width">5</property>
- <property name="width_request">675</property>
- <property name="height_request">530</property>
+ <property name="default_width">675</property>
+ <property name="default_height">530</property>
<property name="visible">True</property>
<property name="title" translatable="yes">Main Menu</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
@@ -65,10 +55,8 @@
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="urgency_hint">False</property>
- <property name="has_separator">False</property>
- <signal handler="on_close_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:46:45 GMT" name="close"/>
- <signal handler="on_close_button_clicked" last_modification_time="Fri, 28 Apr 2006 10:49:37 GMT" name="destroy"/>
- <signal handler="on_style_set" name="style-set"/>
+ <signal handler="on_close_button_clicked" name="close"/>
+ <signal handler="on_close_button_clicked" name="destroy"/>
<accelerator key="Escape" modifiers="0" signal="close"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox5">
@@ -78,18 +66,30 @@
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <signal handler="on_help_button_clicked" name="clicked"/>
+ </object>
+ </child>
<child>
- <object class="GtkButton" id="revert_button">
+ <object class="GtkButton" id="restore_button">
<property name="visible">True</property>
<property name="tooltip-text" translatable="yes">Restore the default menu layout</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
- <property name="label">gtk-revert-to-saved</property>
+ <property name="label" translatable="yes">Restore System Configuration</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_revert_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:38:17 GMT" name="clicked"/>
+ <signal handler="on_restore_button_clicked" name="clicked"/>
</object>
</child>
<child>
@@ -102,7 +102,7 @@
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_close_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:38:03 GMT" name="clicked"/>
+ <signal handler="on_close_button_clicked" name="clicked"/>
</object>
</child>
</object>
@@ -140,31 +140,6 @@
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkLabel" id="label20">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_Menus:</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="mnemonic_widget">menu_tree</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">True</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
<object class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -185,8 +160,6 @@
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
<signal handler="on_menu_tree_cursor_changed" name="cursor-changed"/>
- <signal handler="on_menu_tree_drag_data_received" last_modification_time="Tue, 18 Apr 2006 01:13:34 GMT" name="drag_data_received"/>
- <signal handler="on_menu_tree_drag_data_get" last_modification_time="Tue, 18 Apr 2006 23:58:24 GMT" name="drag_data_get"/>
</object>
</child>
</object>
@@ -208,31 +181,6 @@
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkLabel" id="label21">
- <property name="visible">True</property>
- <property name="label" translatable="yes">It_ems:</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="mnemonic_widget">item_tree</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">True</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
<object class="GtkHBox" id="hbox16">
<property name="visible">True</property>
<property name="homogeneous">False</property>
@@ -259,11 +207,9 @@
<signal handler="on_item_tree_row_activated" name="row-activated"/>
<signal handler="on_item_tree_popup_menu" name="popup-menu"/>
<signal handler="on_item_tree_cursor_changed" name="cursor-changed"/>
- <signal handler="on_item_tree_popup_menu" last_modification_time="Thu, 06 Apr 2006 01:25:48 GMT" name="button_press_event"/>
- <signal handler="on_item_tree_drag_data_get" last_modification_time="Tue, 18 Apr 2006 01:13:21 GMT" name="drag_data_get"/>
- <signal handler="on_item_tree_cursor_changed" last_modification_time="Tue, 18 Apr 2006 15:32:26 GMT" name="cursor_changed"/>
- <signal handler="on_item_tree_drag_data_received" last_modification_time="Tue, 18 Apr 2006 23:58:15 GMT" name="drag_data_received"/>
- <signal handler="on_item_tree_key_press_event" last_modification_time="Sun, 23 Apr 2006 02:21:53 GMT" name="key_press_event"/>
+ <signal handler="on_item_tree_popup_menu" name="button_press_event"/>
+ <signal handler="on_item_tree_cursor_changed" name="cursor_changed"/>
+ <signal handler="on_item_tree_key_press_event" name="key_press_event"/>
</object>
</child>
</object>
@@ -285,142 +231,28 @@
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="new_menu_button">
+ <property name="label" translatable="yes">_New Menu</property>
+ <property name="use_underline">True</property>
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_new_menu_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:04:38 GMT" name="clicked"/>
- <child>
- <object class="GtkAlignment" id="alignment7">
- <property name="visible">True</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xscale">0</property>
- <property name="yscale">0</property>
- <property name="top_padding">0</property>
- <property name="bottom_padding">0</property>
- <property name="left_padding">0</property>
- <property name="right_padding">0</property>
- <child>
- <object class="GtkHBox" id="hbox14">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkImage" id="image21">
- <property name="visible">True</property>
- <property name="stock">gtk-new</property>
- <property name="icon_size">4</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label18">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_New Menu</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <property name="image">new_menu_image</property>
+ <signal handler="on_new_menu_button_clicked" name="clicked"/>
</object>
</child>
<child>
<object class="GtkButton" id="new_item_button">
+ <property name="label" translatable="yes">Ne_w Item</property>
+ <property name="use_underline">True</property>
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_new_item_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:04:43 GMT" name="clicked"/>
- <child>
- <object class="GtkAlignment" id="alignment8">
- <property name="visible">True</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xscale">0</property>
- <property name="yscale">0</property>
- <property name="top_padding">0</property>
- <property name="bottom_padding">0</property>
- <property name="left_padding">0</property>
- <property name="right_padding">0</property>
- <child>
- <object class="GtkHBox" id="hbox15">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkImage" id="image22">
- <property name="visible">True</property>
- <property name="stock">gtk-add</property>
- <property name="icon_size">4</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label19">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ne_w Item</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <property name="image">new_item_image</property>
+ <signal handler="on_new_item_button_clicked" name="clicked"/>
</object>
</child>
<child>
@@ -432,7 +264,7 @@
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_new_separator_button_clicked" last_modification_time="Wed, 26 Apr 2006 18:04:48 GMT" name="clicked"/>
+ <signal handler="on_new_separator_button_clicked" name="clicked"/>
</object>
</child>
</object>
@@ -460,174 +292,58 @@
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="move_up_button">
+ <property name="label" translatable="yes">Move Up</property>
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_move_up_button_clicked" last_modification_time="Wed, 26 Apr 2006 22:09:11 GMT" name="clicked"/>
- <child>
- <object class="GtkAlignment" id="alignment10">
- <property name="visible">True</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xscale">0</property>
- <property name="yscale">0</property>
- <property name="top_padding">0</property>
- <property name="bottom_padding">0</property>
- <property name="left_padding">0</property>
- <property name="right_padding">0</property>
- <child>
- <object class="GtkHBox" id="hbox17">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkImage" id="image23">
- <property name="visible">True</property>
- <property name="stock">gtk-go-up</property>
- <property name="icon_size">4</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label22">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Move Up</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <property name="image">move_up_image</property>
+ <signal handler="on_move_up_button_clicked" name="clicked"/>
</object>
</child>
<child>
<object class="GtkButton" id="move_down_button">
+ <property name="label" translatable="yes">Move Down</property>
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
- <signal handler="on_move_down_button_clicked" last_modification_time="Wed, 26 Apr 2006 22:09:15 GMT" name="clicked"/>
- <child>
- <object class="GtkAlignment" id="alignment11">
- <property name="visible">True</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xscale">0</property>
- <property name="yscale">0</property>
- <property name="top_padding">0</property>
- <property name="bottom_padding">0</property>
- <property name="left_padding">0</property>
- <property name="right_padding">0</property>
- <child>
- <object class="GtkHBox" id="hbox18">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkImage" id="image24">
- <property name="visible">True</property>
- <property name="stock">gtk-go-down</property>
- <property name="icon_size">4</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label23">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Move Down</property>
- <property name="use_underline">True</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <property name="image">move_down_image</property>
+ <signal handler="on_move_down_button_clicked" name="clicked"/>
</object>
</child>
<child>
- <object class="GtkButton" id="properties_button">
- <property name="label" translatable="no">gtk-properties</property>
+ <object class="GtkButton" id="delete_button">
+ <property name="label" translatable="no">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
- <signal handler="on_properties_button_clicked" name="clicked"/>
+ <signal handler="on_delete_button_clicked" name="clicked"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
- </child>
+ </child>
<child>
- <object class="GtkButton" id="delete_button">
- <property name="label" translatable="no">gtk-delete</property>
+ <object class="GtkButton" id="properties_button">
+ <property name="label" translatable="no">gtk-properties</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
- <signal handler="on_delete_button_clicked" name="clicked"/>
+ <signal handler="on_properties_button_clicked" name="clicked"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
- </child>
+ </child>
</object>
</child>
</object>
@@ -675,124 +391,26 @@
</child>
</object>
</child>
- <action-widgets>
- <action-widget response="0">revert_button</action-widget>
+ <action-widgets>
+ <action-widget response="-11">help_button</action-widget>
+ <action-widget response="0">restore_button</action-widget>
<action-widget response="-7">close_button</action-widget>
</action-widgets>
</object>
- <object class="GtkDialog" id="revertdialog">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Revert Changes?</property>
- <property name="type">GTK_WINDOW_TOPLEVEL</property>
- <property name="window_position">GTK_WIN_POS_NONE</property>
- <property name="modal">False</property>
- <property name="resizable">False</property>
- <property name="destroy_with_parent">False</property>
- <property name="decorated">True</property>
- <property name="skip_taskbar_hint">False</property>
- <property name="skip_pager_hint">False</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
- <property name="focus_on_map">True</property>
- <property name="urgency_hint">False</property>
- <property name="has_separator">False</property>
- <child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox6">
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkHButtonBox" id="dialog-action_area6">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
- <child>
- <object class="GtkButton" id="cancel_revert_button">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="button2">
- <property name="visible">True</property>
- <property name="can_default">True</property>
- <property name="can_focus">True</property>
- <property name="label">gtk-revert-to-saved</property>
- <property name="use_stock">True</property>
- <property name="relief">GTK_RELIEF_NORMAL</property>
- <property name="focus_on_click">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox19">
- <property name="border_width">5</property>
- <property name="visible">True</property>
- <property name="homogeneous">False</property>
- <property name="spacing">8</property>
- <child>
- <object class="GtkImage" id="image25">
- <property name="visible">True</property>
- <property name="icon_size">6</property>
- <property name="icon_name">gtk-dialog-question</property>
- <property name="xalign">0</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">False</property>
- <property name="fill">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label24">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Revert all menus to original settings?</property>
- <property name="use_underline">False</property>
- <property name="use_markup">False</property>
- <property name="justify">GTK_JUSTIFY_LEFT</property>
- <property name="wrap">False</property>
- <property name="selectable">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
- <property name="width_chars">-1</property>
- <property name="single_line_mode">False</property>
- <property name="angle">0</property>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">True</property>
- <property name="fill">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="padding">0</property>
- <property name="expand">True</property>
- <property name="fill">True</property>
- </packing>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">cancel_revert_button</action-widget>
- <action-widget response="-8">button2</action-widget>
- </action-widgets>
+ <object class="GtkImage" id="new_menu_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-new</property>
+ </object>
+ <object class="GtkImage" id="new_item_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-add</property>
+ </object>
+ <object class="GtkImage" id="move_down_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ </object>
+ <object class="GtkImage" id="move_up_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
</object>
</interface>