[cinnamon/f16] initial commit
leigh123linux
leigh123linux at fedoraproject.org
Fri Jul 20 17:53:20 UTC 2012
commit f39f899ef97fb022b51f9c8735d6228b5399e71b
Author: leigh123linux <leigh123linux at googlemail.com>
Date: Fri Jul 20 18:53:16 2012 +0100
initial commit
.gitignore | 1 +
cinnamon-1.4.0_bluetooth.patch | 21 +
cinnamon-1.4.0_f16_powerapplet.patch | 49 +
cinnamon-1.4.0_f17_favourite-apps-firefox.patch | 20 +
cinnamon-1.4.0_favourite-apps-firefox.patch | 20 +
cinnamon-1.4.0_menu.patch | 158 ++
cinnamon-1.4.0_settings.patch | 369 +++
cinnamon-1.4.0_windowAttention.patch | 82 +
cinnamon-menu.patch | 3410 +++++++++++++++++++++++
cinnamon.desktop | 10 +
cinnamon.session | 6 +
cinnamon.spec | 310 ++
menu.png | Bin 0 -> 1307 bytes
sources | 1 +
14 files changed, 4457 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e69de29..dee2584 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/cinnamon-1.4.0.UP1.tar.gz
diff --git a/cinnamon-1.4.0_bluetooth.patch b/cinnamon-1.4.0_bluetooth.patch
new file mode 100644
index 0000000..31c4a1a
--- /dev/null
+++ b/cinnamon-1.4.0_bluetooth.patch
@@ -0,0 +1,21 @@
+--- a/configure.ac 2012-05-13 23:30:06.000000000 +0100
++++ b/configure.ac 2012-05-27 18:45:10.476763796 +0100
+@@ -130,6 +130,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >=
+ [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
+ BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
+ AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
++ AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
+ AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
+ AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
+ AC_SUBST([HAVE_BLUETOOTH],[1])
+--- a/src/Makefile.am 2012-05-13 23:30:06.000000000 +0100
++++ b/src/Makefile.am 2012-05-27 18:44:01.057713012 +0100
+@@ -277,7 +277,7 @@ Cinnamon_0_1_gir_CFLAGS = $(libcinnamon_
+ Cinnamon_0_1_gir_LIBS = libcinnamon.la
+ Cinnamon_0_1_gir_FILES = $(libcinnamon_la_gir_sources)
+ Cinnamon_0_1_gir_SCANNERFLAGS = --include-uninstalled=$(builddir)/St-1.0.gir \
+- --add-include-path=$(MUFFIN_GIR_DIR)
++ --add-include-path=$(MUFFIN_GIR_DIR) -L $(BLUETOOTH_DIR)
+ INTROSPECTION_GIRS += Cinnamon-0.1.gir
+ CLEANFILES += Cinnamon-0.1.gir
+
diff --git a/cinnamon-1.4.0_f16_powerapplet.patch b/cinnamon-1.4.0_f16_powerapplet.patch
new file mode 100644
index 0000000..e726f4f
--- /dev/null
+++ b/cinnamon-1.4.0_f16_powerapplet.patch
@@ -0,0 +1,49 @@
+--- a/files/usr/share/cinnamon/applets/power at cinnamon.org/applet.js 2012-05-13 23:30:06.000000000 +0100
++++ b/files/usr/share/cinnamon/applets/power at cinnamon.org/applet.js 2012-05-28 21:49:03.472325821 +0100
+@@ -40,7 +40,7 @@ const PowerManagerInterface = {
+ { name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susdut)' },
+ ],
+ signals: [
+- { name: 'PropertiesChanged', inSignature: 's,a{sv},a[s]' },
++ { name: 'Changed', inSignature: '' },
+ ],
+ properties: [
+ { name: 'Icon', signature: 's', access: 'read' },
+@@ -48,20 +48,6 @@ const PowerManagerInterface = {
+ };
+ let PowerManagerProxy = DBus.makeProxyClass(PowerManagerInterface);
+
+-const SettingsManagerInterface = {
+- name: 'org.freedesktop.DBus.Properties',
+- methods: [
+- { name: 'Get', inSignature: 's,s', outSignature: 'v' },
+- { name: 'GetAll', inSignature: 's', outSignature: 'a{sv}' },
+- { name: 'Set', inSignature: 's,s,v', outSignature: '' }
+- ],
+- signals: [
+- {name: 'PropertiesChanged', inSignature:'s,a{sv},a[s]', outSignature:''}
+- ]
+-};
+-
+-let SettingsManagerProxy = DBus.makeProxyClass(SettingsManagerInterface);
+-
+ function DeviceItem() {
+ this._init.apply(this, arguments);
+ }
+@@ -136,7 +122,6 @@ MyApplet.prototype = {
+
+ this.set_applet_icon_symbolic_name('battery-missing');
+ this._proxy = new PowerManagerProxy(DBus.session, BUS_NAME, OBJECT_PATH);
+- this._smProxy = new SettingsManagerProxy(DBus.session, BUS_NAME, OBJECT_PATH);
+
+ let icon = this.actor.get_children()[0];
+ this.actor.remove_actor(icon);
+@@ -163,7 +148,7 @@ MyApplet.prototype = {
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
+
+- this._smProxy.connect('PropertiesChanged', Lang.bind(this, this._devicesChanged));
++ this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
+ this._devicesChanged();
+ }
+ catch (e) {
diff --git a/cinnamon-1.4.0_f17_favourite-apps-firefox.patch b/cinnamon-1.4.0_f17_favourite-apps-firefox.patch
new file mode 100644
index 0000000..a5ea847
--- /dev/null
+++ b/cinnamon-1.4.0_f17_favourite-apps-firefox.patch
@@ -0,0 +1,20 @@
+--- cinnamon-1.1.3/data/org.cinnamon.gschema1.xml.in 2012-01-02 12:03:30.000000000 +0000
++++ cinnamon-1.1.3/data/org.cinnamon.gschema.xml.in 2012-01-02 16:41:39.242419968 +0000
+@@ -231,7 +231,7 @@
+ </key>
+
+ <key name="panel-launchers" type="as">
+- <default>[ 'firefox.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']</default>
++ <default>[ 'firefox.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']</default>
+ <_summary>Desktop files of the applications to put in the panel launchers applet</_summary>
+ <_description>
+ Cinnamon allows to show applications launchers on the panel.
+@@ -251,7 +251,7 @@
+ </key>
+
+ <key name="favorite-apps" type="as">
+- <default>[ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nautilus.desktop' ]</default>
++ <default>[ 'cinnamon-settings.desktop', 'firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
+ <_summary>List of desktop file IDs for favorite applications</_summary>
+ <_description>
+ The applications corresponding to these identifiers
diff --git a/cinnamon-1.4.0_favourite-apps-firefox.patch b/cinnamon-1.4.0_favourite-apps-firefox.patch
new file mode 100644
index 0000000..60174ed
--- /dev/null
+++ b/cinnamon-1.4.0_favourite-apps-firefox.patch
@@ -0,0 +1,20 @@
+--- cinnamon-1.1.3/data/org.cinnamon.gschema1.xml.in 2012-01-02 12:03:30.000000000 +0000
++++ cinnamon-1.1.3/data/org.cinnamon.gschema.xml.in 2012-01-02 16:41:39.242419968 +0000
+@@ -231,7 +231,7 @@
+ </key>
+
+ <key name="panel-launchers" type="as">
+- <default>[ 'firefox.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']</default>
++ <default>[ 'mozilla-firefox.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']</default>
+ <_summary>Desktop files of the applications to put in the panel launchers applet</_summary>
+ <_description>
+ Cinnamon allows to show applications launchers on the panel.
+@@ -251,7 +251,7 @@
+ </key>
+
+ <key name="favorite-apps" type="as">
+- <default>[ 'firefox.desktop', 'mintInstall.desktop', 'cinnamon-settings.desktop', 'xchat.desktop', 'gnome-terminal.desktop', 'nautilus.desktop' ]</default>
++ <default>[ 'cinnamon-settings.desktop', 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
+ <_summary>List of desktop file IDs for favorite applications</_summary>
+ <_description>
+ The applications corresponding to these identifiers
diff --git a/cinnamon-1.4.0_menu.patch b/cinnamon-1.4.0_menu.patch
new file mode 100644
index 0000000..87c46ea
--- /dev/null
+++ b/cinnamon-1.4.0_menu.patch
@@ -0,0 +1,158 @@
+--- a/files/etc/xdg/menus/cinnamon-applications.menu
++++ b/files/etc/xdg/menus/cinnamon-applications.menu
+@@ -4,12 +4,12 @@
+ <Menu>
+
+ <Name>Applications</Name>
+- <Directory>cinnamon-menu-applications.directory</Directory>
++ <Directory>X-GNOME-Menu-Applications.directory</Directory>
+
+ <!-- Scan legacy dirs first, as later items take priority -->
+ <KDELegacyDirs/>
+ <LegacyDir>/etc/X11/applnk</LegacyDir>
+- <LegacyDir>/usr/share/cinnamon/apps</LegacyDir>
++ <LegacyDir>/usr/share/gnome/apps</LegacyDir>
+
+ <!-- Read standard .directory and .desktop file locations -->
+ <DefaultAppDirs/>
+@@ -21,7 +21,7 @@
+ <!-- Accessories submenu -->
+ <Menu>
+ <Name>Accessories</Name>
+- <Directory>cinnamon-utility.directory</Directory>
++ <Directory>Utility.directory</Directory>
+ <Include>
+ <And>
+ <Category>Utility</Category>
+@@ -38,7 +38,7 @@
+ <!-- Accessibility submenu -->
+ <Menu>
+ <Name>Universal Access</Name>
+- <Directory>cinnamon-utility-accessibility.directory</Directory>
++ <Directory>Utility-Accessibility.directory</Directory>
+ <Include>
+ <And>
+ <Category>Accessibility</Category>
+@@ -50,7 +50,7 @@
+ <!-- Development Tools -->
+ <Menu>
+ <Name>Development</Name>
+- <Directory>cinnamon-development.directory</Directory>
++ <Directory>Development.directory</Directory>
+ <Include>
+ <And>
+ <Category>Development</Category>
+@@ -62,7 +62,7 @@
+ <!-- Education -->
+ <Menu>
+ <Name>Education</Name>
+- <Directory>cinnamon-education.directory</Directory>
++ <Directory>Education.directory</Directory>
+ <Include>
+ <And>
+ <Category>Education</Category>
+@@ -73,7 +73,7 @@
+ <!-- Games -->
+ <Menu>
+ <Name>Games</Name>
+- <Directory>cinnamon-game.directory</Directory>
++ <Directory>Game.directory</Directory>
+ <Include>
+ <And>
+ <Category>Game</Category>
+@@ -84,7 +84,7 @@
+ <!-- Graphics -->
+ <Menu>
+ <Name>Graphics</Name>
+- <Directory>cinnamon-graphics.directory</Directory>
++ <Directory>Graphics.directory</Directory>
+ <Include>
+ <And>
+ <Category>Graphics</Category>
+@@ -95,7 +95,7 @@
+ <!-- Internet -->
+ <Menu>
+ <Name>Internet</Name>
+- <Directory>cinnamon-network.directory</Directory>
++ <Directory>Network.directory</Directory>
+ <Include>
+ <And>
+ <Category>Network</Category>
+@@ -106,7 +106,7 @@
+ <!-- Multimedia -->
+ <Menu>
+ <Name>Multimedia</Name>
+- <Directory>cinnamon-audio-video.directory</Directory>
++ <Directory>AudioVideo.directory</Directory>
+ <Include>
+ <And>
+ <Category>AudioVideo</Category>
+@@ -117,7 +117,7 @@
+ <!-- Office -->
+ <Menu>
+ <Name>Office</Name>
+- <Directory>cinnamon-office.directory</Directory>
++ <Directory>Office.directory</Directory>
+ <Include>
+ <And>
+ <Category>Office</Category>
+@@ -128,7 +128,7 @@
+ <!-- System Tools-->
+ <Menu>
+ <Name>System</Name>
+- <Directory>cinnamon-system-tools.directory</Directory>
++ <Directory>System-Tools.directory</Directory>
+ <Include>
+ <And>
+ <Category>System</Category>
+@@ -140,13 +140,15 @@
+ <!-- Other -->
+ <Menu>
+ <Name>Other</Name>
+- <Directory>cinnamon-other.directory</Directory>
++ <Directory>X-GNOME-Other.directory</Directory>
+ <OnlyUnallocated/>
+ <Include>
+ <And>
+ <Not><Category>Core</Category></Not>
+ <Not><Category>Settings</Category></Not>
+ <Not><Category>Screensaver</Category></Not>
++ <Not><Category>X-GNOME-Settings-Panel</Category></Not>
++ <Not><Category>Documentation</Category></Not>
+ </And>
+ </Include>
+ </Menu> <!-- End Other -->
+@@ -154,10 +156,10 @@
+ <!-- Wine -->
+ <Menu>
+ <Name>Wine</Name>
+- <Directory>wine-wine.directory</Directory>
++ <Directory>Wine.directory</Directory>
+ <Include>
+ <And>
+- <Category>Wine</Category>
++ <Category>X-Wine</Category>
+ </And>
+ </Include>
+ </Menu> <!-- End Wine -->
+--- a/files/etc/xdg/menus/cinnamon-settings.menu
++++ b/files/etc/xdg/menus/cinnamon-settings.menu
+@@ -18,9 +18,14 @@
+ <Name>Preferences</Name>
+ <Directory>cinnamon-settings.directory</Directory>
+ <Include>
+- <And>
+- <Category>Settings</Category>
+- </And>
++ <And>
++ <Category>Settings</Category>
++ <Not>
++ <Or>
++ <Category>System</Category>
++ </Or>
++ </Not>
++ </And>
+ </Include>
+ </Menu>
+
+
diff --git a/cinnamon-1.4.0_settings.patch b/cinnamon-1.4.0_settings.patch
new file mode 100644
index 0000000..c01a1f7
--- /dev/null
+++ b/cinnamon-1.4.0_settings.patch
@@ -0,0 +1,369 @@
+--- a/files/usr/lib/cinnamon-settings/cinnamon-settings.py 2012-03-12 14:31:34.000000000 +0000
++++ b/files/usr/lib/cinnamon-settings/cinnamon-settings.py 2012-03-13 10:58:01.866049626 +0000
+@@ -11,9 +11,6 @@ try:
+ import gconf
+ import json
+ import dbus
+- import tz
+- import time
+- from datetime import datetime
+ from user import home
+ import thread
+ except Exception, detail:
+@@ -737,244 +734,7 @@ class GSettingsComboBox(Gtk.HBox):
+ value = self.model[tree_iter][0]
+ self.settings.set_string(self.key, value)
+
+-class TimeZoneSelectorWidget(Gtk.HBox):
+- def __init__(self):
+- super(TimeZoneSelectorWidget, self).__init__()
+-
+- proxy = dbus.SystemBus().get_object("org.gnome.SettingsDaemon.DateTimeMechanism", "/")
+- self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.gnome.SettingsDaemon.DateTimeMechanism")
+-
+- self.timezones = tz.load_db()
+-
+- self.selected_region, self.selected_city = self.get_selected_zone()
+-
+- region_label = Gtk.Label(_("Region"))
+- self.pack_start(region_label, False, False, 2)
+-
+- regions = self.timezones.keys()
+- regions.sort()
+- self.region_model = Gtk.ListStore(str, str)
+- selected_region_iter = None
+- for region in regions:
+- iter = self.region_model.insert_before(None, None)
+- self.region_model.set_value(iter, 0, region)
+- self.region_model.set_value(iter, 1, region.replace("_", " "))
+- if (region == self.selected_region):
+- selected_region_iter = iter
+-
+- self.region_widget = Gtk.ComboBox.new_with_model(self.region_model)
+- renderer_text = Gtk.CellRendererText()
+- self.region_widget.pack_start(renderer_text, True)
+- self.region_widget.add_attribute(renderer_text, "text", 1)
+- if selected_region_iter is not None:
+- self.region_widget.set_active_iter(selected_region_iter)
+- self.pack_start(self.region_widget, False, False, 2)
+-
+- city_label = Gtk.Label(_("City"))
+- self.pack_start(city_label, False, False, 2)
+-
+- self.city_model = Gtk.ListStore(str, str)
+- self.city_widget = Gtk.ComboBox.new_with_model(self.city_model)
+- renderer_text = Gtk.CellRendererText()
+- self.city_widget.pack_start(renderer_text, True)
+- self.city_widget.add_attribute(renderer_text, "text", 1)
+- self.pack_start(self.city_widget, False, False, 2)
+-
+- self.update_cities_list()
+-
+- self.region_widget.connect("changed", self.on_region_changed)
+- self.city_widget.connect("changed", self.on_city_changed)
+- def on_city_changed(self, widget):
+- tree_iter = widget.get_active_iter()
+- if tree_iter != None:
+- self.selected_city = self.city_model[tree_iter][0]
+- self.dbus_iface.SetTimezone(self.selected_region+"/"+self.selected_city)
+- def on_region_changed(self, widget):
+- tree_iter = widget.get_active_iter()
+- if tree_iter != None:
+- self.selected_region = self.region_model[tree_iter][0]
+- self.update_cities_list()
+- def update_cities_list(self):
+- self.city_model.clear()
+- if self.selected_region and self.selected_region in self.timezones.keys():
+- cities = self.timezones[self.selected_region]
+- cities.sort()
+- selected_city_iter = None
+- for city in cities:
+- iter = self.city_model.insert_before(None, None)
+- self.city_model.set_value(iter, 0, city)
+- self.city_model.set_value(iter, 1, city.replace("_", " "))
+- if (city == self.selected_city):
+- selected_city_iter = iter
+- if selected_city_iter is not None:
+- self.city_widget.set_active_iter(selected_city_iter)
+- def get_selected_zone(self):
+- tz = self.dbus_iface.GetTimezone()
+- if "/" in tz:
+- i = tz.index("/")
+- region = tz[:i]
+- city = tz[i+1:]
+- return region, city
+- else:
+- return "", ""
+-
+-class ChangeTimeWidget(Gtk.HBox):
+- def __init__(self):
+- super(ChangeTimeWidget, self).__init__()
+- proxy = dbus.SystemBus().get_object("org.gnome.SettingsDaemon.DateTimeMechanism", "/")
+- self.dbus_iface = dbus.Interface(proxy, dbus_interface="org.gnome.SettingsDaemon.DateTimeMechanism")
+-
+- # Ensures we are setting the system time only when the user changes it
+- self.changedOnTimeout = False
+-
+- # Ensures we do not update the values in the date/time fields during the DBus call to set the time
+- self._setting_time = False
+- self._setting_time_lock = thread.allocate()
+- self._time_to_set = None
+-
+- self.thirtyDays = [3, 5, 8, 10]
+- months = ['January','February','March','April','May','June','July','August','September','October','November','December']
+-
+- # Boxes
+- timeBox = Gtk.HBox()
+- dateBox = Gtk.HBox()
+-
+- # Combo Boxes
+- self.monthBox = Gtk.ComboBoxText()
+-
+- for month in months:
+- self.monthBox.append_text(month)
+-
+- # Adjustments
+- hourAdj = Gtk.Adjustment(0, 0, 23, 1, 1)
+- minAdj = Gtk.Adjustment(0, 0, 59, 1, 1)
+- yearAdj = Gtk.Adjustment(0, 0, 9999, 1, 5)
+- dayAdj = Gtk.Adjustment(0, 1, 31, 1, 1)
+-
+- # Spin buttons
+- self.hourSpin = Gtk.SpinButton()
+- self.minSpin = Gtk.SpinButton()
+- self.yearSpin = Gtk.SpinButton()
+- self.daySpin = Gtk.SpinButton()
+-
+- self.hourSpin.configure(hourAdj, 0.5, 0)
+- self.minSpin.configure(minAdj, 0.5, 0)
+- self.yearSpin.configure(yearAdj, 0.5, 0)
+- self.daySpin.configure(dayAdj, 0.5, 0)
+- self.hourSpin.set_editable(False)
+- self.minSpin.set_editable(False)
+- self.yearSpin.set_editable(False)
+- self.daySpin.set_editable(False)
+-
+- self.update_time()
+- GObject.timeout_add(1000, self.update_time)
+-
+- # Connect to callback
+- self.hourSpin.connect('changed', self._change_system_time)
+- self.minSpin.connect('changed', self._change_system_time)
+- self.monthBox.connect('changed', self._change_system_time)
+- self.yearSpin.connect('changed', self._change_system_time)
+- self.daySpin.connect('changed', self._change_system_time)
+-
+- timeBox.pack_start(self.hourSpin, False, False, 2)
+- timeBox.pack_start(Gtk.Label(_(":")), False, False, 2)
+- timeBox.pack_start(self.minSpin, False, False, 2)
+-
+- dateBox.pack_start(self.monthBox, False, False, 2)
+- dateBox.pack_start(self.daySpin, False, False, 2)
+- dateBox.pack_start(self.yearSpin, False, False, 2)
+-
+- self.pack_start(Gtk.Label(_("Date : ")), False, False, 2)
+- self.pack_start(dateBox, True, True, 2)
+- self.pack_start(Gtk.Label(_("Time : ")), False, False, 2)
+- self.pack_start(timeBox, True, True, 2)
+-
+- def update_time(self):
+- self._setting_time_lock.acquire()
+- do_update = not self._setting_time
+- self._setting_time_lock.release()
+-
+- if not do_update:
+- return True
+-
+- dt = datetime.now()
+-
+- self.changedOnTimeout = True
+-
+- # Time
+- self.hourSpin.set_value( dt.hour )
+- self.minSpin.set_value( dt.minute )
+-
+- # Date
+- self.monthBox.set_active( dt.month-1 )
+- self.daySpin.set_value( dt.day )
+- self.yearSpin.set_value( dt.year )
+-
+- self.changedOnTimeout = False
+-
+- # Update the max of the day spin box
+- maxDay = 31
+- if dt.month == 2:
+- if dt.year % 4 == 0:
+- maxDay = 29
+- else:
+- maxDay = 28
+- elif dt.month-1 in self.thirtyDays:
+- maxDay = 30
+-
+- self.daySpin.get_adjustment().set_upper(maxDay)
+-
+- return True
+-
+- def change_using_ntp(self, usingNtp):
+- # Check if we were using Ntp by seeing if the spin button
+- # is sensitive
+- self.set_sensitive(not usingNtp)
+-
+- def _do_change_system_time(self):
+- self._setting_time_lock.acquire()
+- do_set = not self._setting_time
+- self._setting_time = True
+- self._setting_time_lock.release()
+-
+- # If there is already another thread updating the time, we let it do the job
+- if not do_set:
+- return
+-
+- done = False
+- while not done:
+- self._setting_time_lock.acquire()
+- time_to_set = self._time_to_set
+- self._time_to_set = None
+- self._setting_time_lock.release()
+-
+- self.dbus_iface.SetTime(time_to_set)
+-
+- # Check whether another request to set the time was done since this thread started
+- self._setting_time_lock.acquire()
+- if self._time_to_set==None:
+- done = True
+- self._setting_time_lock.release()
+-
+- self._setting_time_lock.acquire()
+- self._setting_time = False
+- self._setting_time_lock.release()
+-
+- def _change_system_time(self, widget):
+- if not self.changedOnTimeout:
+- hour = int( self.hourSpin.get_value() )
+- minute = int( self.minSpin.get_value() )
+- month = self.monthBox.get_active() + 1
+- day = int( self.daySpin.get_value() )
+- year = int( self.yearSpin.get_value() )
+-
+- newDt = datetime(year, month, day, hour, minute)
+-
+- self._setting_time_lock.acquire()
+- self._time_to_set = time.mktime(newDt.utctimetuple())
+- self._setting_time_lock.release()
+-
+- thread.start_new_thread(self._do_change_system_time, ())
++
+
+ class TitleBarButtonsOrderSelector(Gtk.Table):
+ def __init__(self):
+@@ -1120,27 +880,12 @@ class MainWindow:
+ sidePage.add_widget(GSettingsCheckButton(_("Panel Launchers draggable"), "org.cinnamon", "panel-launchers-draggable"))
+
+ sidePage = SidePage(_("Calendar"), "clock.svg", self.content_box)
+- self.sidePages.append((sidePage, "calendar"))
+- self.changeTimeWidget = ChangeTimeWidget()
++ self.sidePages.append((sidePage, "calendar"))
+ sidePage.add_widget(GSettingsCheckButton(_("Show week dates in calendar"), "org.cinnamon.calendar", "show-weekdate"))
+ sidePage.add_widget(GSettingsEntry(_("Date format for the panel"), "org.cinnamon.calendar", "date-format"))
+ sidePage.add_widget(GSettingsEntry(_("Date format inside the date applet"), "org.cinnamon.calendar", "date-format-full"))
+ sidePage.add_widget(Gtk.LinkButton.new_with_label("http://www.foragoodstrftime.com/", _("Generate your own date formats")))
+- self.ntpCheckButton = None
+- try:
+- self.ntpCheckButton = DBusCheckButton(_("Use network time"), "org.gnome.SettingsDaemon.DateTimeMechanism", "/", "GetUsingNtp", "SetUsingNtp")
+- sidePage.add_widget(self.ntpCheckButton)
+- except:
+- pass
+- sidePage.add_widget(self.changeTimeWidget)
+- try:
+- sidePage.add_widget(TimeZoneSelectorWidget())
+- except:
+- pass
+-
+- if self.ntpCheckButton != None:
+- self.ntpCheckButton.connect('toggled', self._ntp_toggled)
+- self.changeTimeWidget.change_using_ntp( self.ntpCheckButton.get_active() )
++
+
+ sidePage = SidePage(_("Hot corner"), "overview.svg", self.content_box)
+ self.sidePages.append((sidePage, "hotcorner"))
+@@ -1314,7 +1059,6 @@ class MainWindow:
+ sidePage.add_widget(GSettingsFontButton(_("Default font"), "org.gnome.desktop.interface", "font-name"))
+ sidePage.add_widget(GSettingsFontButton(_("Document font"), "org.gnome.desktop.interface", "document-font-name"))
+ sidePage.add_widget(GSettingsFontButton(_("Monospace font"), "org.gnome.desktop.interface", "monospace-font-name"))
+- sidePage.add_widget(GConfFontButton(_("Window title font"), "/apps/metacity/general/titlebar_font"))
+ sidePage.add_widget(GSettingsComboBox(_("Hinting"), "org.gnome.settings-daemon.plugins.xsettings", "hinting", [(i, i.title()) for i in ("none", "slight", "medium", "full")]))
+ sidePage.add_widget(GSettingsComboBox(_("Antialiasing"), "org.gnome.settings-daemon.plugins.xsettings", "antialiasing", [(i, i.title()) for i in ("none", "grayscale", "rgba")]))
+
+@@ -1355,9 +1099,6 @@ class MainWindow:
+ self.content_box_sw.hide()
+ self.top_button_box.hide()
+ self.side_view_sw.show_all()
+-
+- def _ntp_toggled(self, widget):
+- self.changeTimeWidget.change_using_ntp( self.ntpCheckButton.get_active() )
+
+
+ if __name__ == "__main__":
+--- a/files/usr/lib/cinnamon-settings/tz.py 2012-03-12 14:31:34.000000000 +0000
++++ /dev/null 2012-03-13 10:08:58.957000026 +0000
+@@ -1,35 +0,0 @@
+-import os
+-
+-def load_db():
+- tz_db = {}
+-
+- filename = '/usr/share/zoneinfo/zone.tab'
+- if not os.path.exists(filename):
+- filename = '/usr/share/lib/zoneinfo/tab/zone_sun.tab'
+- if not os.path.exists(filename):
+- return {}
+-
+- tz_file = open(filename)
+-
+- for line in tz_file:
+- line = line.rstrip().lstrip()
+- if line=="" or line[0] == '#':
+- continue
+-
+- tz_info = line.split('\t')
+- if len(tz_info)<3:
+- continue
+- tz = tz_info[2]
+- if "/" in tz:
+- i = tz.index("/")
+- region = tz[:i]
+- zone = tz[i+1:]
+-
+- if region not in tz_db:
+- tz_db[region] = []
+-
+- tz_db[region].append(zone)
+-
+- tz_file.close()
+-
+- return tz_db
+--- a/files/usr/share/cinnamon/applets/calendar at cinnamon.org/applet.js 2012-03-12 14:31:34.000000000 +0000
++++ b/files/usr/share/cinnamon/applets/calendar at cinnamon.org/applet.js 2012-03-13 10:56:47.918048379 +0000
+@@ -62,9 +62,7 @@ MyApplet.prototype = {
+ this._calendar = new Calendar.Calendar(this._eventSource);
+ vbox.add(this._calendar.actor);
+
+- let item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"))
+- item.connect("activate", Lang.bind(this, this._onLaunchSettings));
+- //this.menu.addMenuItem(item);
++ item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
+ if (item) {
+ let separator = new PopupMenu.PopupSeparatorMenuItem();
+ separator.setColumnWidths(1);
+@@ -96,11 +94,6 @@ MyApplet.prototype = {
+ on_applet_clicked: function(event) {
+ this.menu.toggle();
+ },
+-
+- _onLaunchSettings: function() {
+- this.menu.close();
+- Util.spawnCommandLine("cinnamon-settings calendar");
+- },
+
+ _updateClockAndDate: function() {
+ let dateFormat = this._calendarSettings.get_string('date-format');
diff --git a/cinnamon-1.4.0_windowAttention.patch b/cinnamon-1.4.0_windowAttention.patch
new file mode 100644
index 0000000..f550b8e
--- /dev/null
+++ b/cinnamon-1.4.0_windowAttention.patch
@@ -0,0 +1,82 @@
+--- a/data/org.cinnamon.gschema.xml.in 2012-05-13 23:30:06.000000000 +0100
++++ b/data/org.cinnamon.gschema.xml.in 2012-05-28 16:37:33.136089480 +0100
+@@ -175,6 +175,11 @@
+ </_description>
+ </key>
+
++ <key type="i" name="notification-style">
++ <default>2</default>
++ <_summary>Notification style</_summary>
++ </key>
++
+ <key name="date-format" type="s">
+ <default>"YYYY-MM-DD"</default>
+ <_summary>Auto-hide panel</_summary>
+--- a/js/ui/windowAttentionHandler.js 2012-05-13 23:30:06.000000000 +0100
++++ b/js/ui/windowAttentionHandler.js 2012-05-28 16:36:22.797043548 +0100
+@@ -12,10 +12,21 @@ function WindowAttentionHandler() {
+
+ WindowAttentionHandler.prototype = {
+ _init : function() {
++ this.notification_style = global.settings.get_int("notification-style");
++ global.settings.connect("changed::notification-style", Lang.bind(this, function() {
++ this.notification_style = global.settings.get_int("notification-style");
++ }));
++
+ this._tracker = Cinnamon.WindowTracker.get_default();
+ global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
+ },
+
++ _getTitleAndBanner: function(app, window) {
++ let title = app.get_name();
++ let banner = _("'%s' is ready").format(window.get_title());
++ return [title, banner]
++ },
++
+ _onWindowDemandsAttention : function(display, window) {
+ // We don't want to show the notification when the window is already focused,
+ // because this is rather pointless.
+@@ -28,9 +39,41 @@ WindowAttentionHandler.prototype = {
+ if (!window || window.has_focus() || window.is_skip_taskbar())
+ return;
+
+- if (this._tracker.is_window_interesting(window)) {
+- window.activate(global.get_current_time());
++ switch (this.notification_style) {
++ case 0:
++ //nop
++ break;
++ case 1:
++ this.bringToFront(window);
++ break;
++ case 2:
++ this.showBanner(window);
++ break;
++ default:
++ global.log('Unknown notification style: ' + this.notification_style);
+ }
++ },
++
++ bringToFront : function(window) {
++ if (this._tracker.is_window_interesting(window)) {
++ window.activate(global.get_current_time());
++ }
++ },
++
++ showBanner : function(window) {
++ let app = this._tracker.get_window_app(window);
++ let source = new Source(app, window);
++ if (Main.messageTray) Main.messageTray.add(source);
++
++ let [title, banner] = this._getTitleAndBanner(app, window);
++
++ let notification = new MessageTray.Notification(source, title, banner);
++ source.notify(notification);
++
++ source.signalIDs.push(window.connect('notify::title', Lang.bind(this, function() {
++ let [title, banner] = this._getTitleAndBanner(app, window);
++ notification.update(title, banner);
++ })));
+ }
+ };
+
diff --git a/cinnamon-menu.patch b/cinnamon-menu.patch
new file mode 100644
index 0000000..4eda3e9
--- /dev/null
+++ b/cinnamon-menu.patch
@@ -0,0 +1,3410 @@
+--- 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>
diff --git a/cinnamon.desktop b/cinnamon.desktop
new file mode 100644
index 0000000..1b1a95c
--- /dev/null
+++ b/cinnamon.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Cinnamon
+Name[en_GB]=Cinnamon
+Comment=This session logs you into Cinnamon
+Comment[en_GB]=This session logs you into Cinnamon
+Exec=/usr/bin/gnome-session --session=cinnamon
+# no icon yet, only the top three are currently used
+Icon=
+Type=Application
diff --git a/cinnamon.session b/cinnamon.session
new file mode 100644
index 0000000..3abc24c
--- /dev/null
+++ b/cinnamon.session
@@ -0,0 +1,6 @@
+[GNOME Session]
+Name=Cinnamon
+RequiredComponents=cinnamon;gnome-settings-daemon;
+IsRunnableHelper=/usr/libexec/gnome-session-check-accelerated
+FallbackSession=gnome-fallback
+DesktopName=GNOME
diff --git a/cinnamon.spec b/cinnamon.spec
new file mode 100644
index 0000000..fe5bba2
--- /dev/null
+++ b/cinnamon.spec
@@ -0,0 +1,310 @@
+%global _internal_version af1653f
+
+%{?filter_setup:
+%filter_from_provides /^libcinnamon.so/d;
+%filter_from_requires /^libcinnamon.so/d;
+%filter_setup
+}
+
+Name: cinnamon
+Version: 1.4.0
+Release: 7.UP1%{?dist}
+Summary: Window management and application launching for GNOME
+
+Group: User Interface/Desktops
+License: GPLv2+
+URL: http://cinnamon.linuxmint.com
+# To generate tarball
+# wget https://github.com/linuxmint/Cinnamon/tarball/1.4-UP1 -O cinnamon-1.4.0.UP1.tar.gz
+Source0: cinnamon-%{version}.UP1.tar.gz
+Source1: cinnamon.desktop
+Source2: cinnamon.session
+Source3: menu.png
+
+
+# Fix menu structure
+Patch0: cinnamon-1.4.0_menu.patch
+# Replace mint favorites with fedora gnome-shell defaults
+Patch1: cinnamon-1.4.0_favourite-apps-firefox.patch
+Patch2: cinnamon-1.4.0_windowAttention.patch
+Patch3: cinnamon-1.4.0_f16_powerapplet.patch
+# https://github.com/linuxmint/Cinnamon/pull/929
+Patch4: cinnamon-menu.patch
+
+
+
+%global clutter_version 1.4.0
+%global gobject_introspection_version 0.10.1
+%global muffin_version 1.0.3
+%global eds_version 2.91.6
+%global json_glib_version 0.13.2
+
+
+BuildRequires: clutter-devel >= %{clutter_version}
+BuildRequires: dbus-glib-devel
+BuildRequires: desktop-file-utils
+BuildRequires: evolution-data-server-devel >= %{eds_version}
+BuildRequires: gjs-devel >= 0.7.14-6
+BuildRequires: glib2-devel
+BuildRequires: gnome-menus-devel >= 3.1.5-2.fc16
+BuildRequires: gnome-desktop3-devel
+BuildRequires: gobject-introspection >= %{gobject_introspection_version}
+BuildRequires: json-glib-devel >= %{json_glib_version}
+BuildRequires: upower-devel
+BuildRequires: NetworkManager-glib-devel
+BuildRequires: polkit-devel
+BuildRequires: telepathy-glib-devel
+BuildRequires: telepathy-logger-devel >= 0.2.6
+BuildRequires: GConf2
+BuildRequires: libgudev1-devel
+# for screencast recorder functionality
+BuildRequires: gstreamer-devel
+BuildRequires: gtk3-devel
+BuildRequires: intltool
+BuildRequires: libcanberra-devel
+BuildRequires: libcroco-devel
+BuildRequires: folks-devel
+
+# for barriers
+BuildRequires: libXfixes-devel >= 5.0
+# used in unused BigThemeImage
+BuildRequires: librsvg2-devel
+BuildRequires: muffin-devel >= %{muffin_version}
+BuildRequires: pulseaudio-libs-devel
+%ifnarch s390 s390x
+BuildRequires: gnome-bluetooth-libs-devel >= 2.91
+BuildRequires: gnome-bluetooth >= 2.91
+%endif
+# Bootstrap requirements
+BuildRequires: gtk-doc gnome-common
+Requires: gnome-menus%{?_isa} >= 3.0.0-2
+# wrapper script uses to restart old GNOME session if run --replace
+# from the command line
+Requires: gobject-introspection%{?_isa} >= %{gobject_introspection_version}
+# needed for loading SVG's via gdk-pixbuf
+Requires: librsvg2%{?_isa}
+# needed as it is now split from Clutter
+Requires: json-glib%{?_isa} >= %{json_glib_version}
+# might be still be needed.
+Requires: muffin%{?_isa} >= %{muffin_version}
+Requires: upower%{?_isa}
+Requires: polkit%{?_isa} >= 0.100
+# needed for session files
+Requires: gnome-session
+# needed for schemas
+Requires: at-spi2-atk%{?_isa}
+Requires(pre): GConf2
+Requires(post): GConf2
+Requires(preun): GConf2
+# needed for on-screen keyboard
+Requires: caribou%{?_isa}
+# needed for settings
+Requires: pygobject2
+Requires: dbus-python
+
+%description
+Cinnamon is a Linux desktop which provides advanced
+ innovative features and a traditional user experience.
+
+The desktop layout is similar to Gnome 2.
+The underlying technology is forked from Gnome Shell.
+The emphasis is put on making users feel at home and providing
+ them with an easy to use and comfortable desktop experience.
+
+%prep
+%setup -q -n linuxmint-Cinnamon-%{_internal_version}
+%patch0 -p1
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+
+
+# remove gschema
+rm -rf data/org.cinnamon.gschema.xml
+# make changes for settings move to /usr/share
+mv files/usr/lib/cinnamon-settings files%{_datadir}
+sed -i -e 's@/usr/lib@/usr/share at g' files%{_bindir}/cinnamon-settings \
+ files%{_datadir}/cinnamon-settings/cinnamon-settings.py
+# make changes for menu-editor move to /usr/share
+mv files/usr/lib/cinnamon-menu-editor files%{_datadir}
+rm -rf files/usr/lib
+sed -i -e 's@/usr/lib@/usr/share at g' files%{_bindir}/cinnamon-menu-editor \
+ files%{_datadir}/cinnamon-menu-editor/Alacarte/MainWindow.py
+# remove and replace the session files as they don't work with fedora (can't be bothered to patch it)
+rm -f files%{_bindir}/gnome-session-cinnamon \
+ files%{_datadir}/xsessions/cinnamon*.desktop \
+ files%{_datadir}/gnome-session/sessions/cinnamon*.session
+install -pm 644 %SOURCE1 files%{_datadir}/xsessions/
+install -pm 644 %SOURCE2 files%{_datadir}/gnome-session/sessions/
+# replace menu image
+rm -f data/theme/menu.png
+install -p %SOURCE3 data/theme/menu.png
+# files replaced with fedora files
+rm -f files%{_datadir}/desktop-directories/cinnamon-{menu-applications,utility,utility-accessibility,development,education,game,graphics,network,audio-video,office,system-tools,other}.directory
+# adjust font size
+sed -i -e 's,font-size: 9.5pt,font-size: 10pt,g' data/theme/cinnamon.css
+sed -i -e 's,font-size: 9pt,font-size: 10pt,g' data/theme/cinnamon.css
+sed -i -e 's,font-size: 8.5pt,font-size: 10pt,g' data/theme/cinnamon.css
+sed -i -e 's,font-size: 8pt,font-size: 10pt,g' data/theme/cinnamon.css
+sed -i -e 's,font-size: 7.5pt,font-size: 10pt,g' data/theme/cinnamon.css
+rm -f configure
+
+
+
+%build
+export CFLAGS="$RPM_OPT_FLAGS -Wno-error=deprecated-declarations"
+NOCONFIGURE=1 ./autogen.sh
+%configure --disable-static --enable-compile-warnings=minimum
+make V=1 %{?_smp_mflags}
+
+%install
+export GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL=1
+make install DESTDIR=$RPM_BUILD_ROOT
+
+# Remove .la file
+rm -rf $RPM_BUILD_ROOT/%{_libdir}/cinnamon/libcinnamon.la
+
+# Remove firefox plugin
+rm -rf $RPM_BUILD_ROOT/%{_libdir}/mozilla
+
+desktop-file-validate $RPM_BUILD_ROOT%{_datadir}/applications/cinnamon.desktop
+
+desktop-file-install \
+ --add-category="Utility" \
+ --remove-category="DesktopSettings" \
+ --remove-key="Encoding" \
+ --add-only-show-in="GNOME" \
+ --delete-original \
+ --dir=$RPM_BUILD_ROOT%{_datadir}/applications \
+ $RPM_BUILD_ROOT%{_datadir}/applications/cinnamon-settings.desktop
+
+%find_lang %{name}
+
+%pre
+%gconf_schema_prepare cinnamon
+
+%post
+%gconf_schema_upgrade cinnamon
+
+%preun
+%gconf_schema_remove cinnamon
+
+%postun
+if [ $1 -eq 0 ] ; then
+ /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
+fi
+
+%posttrans
+ /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
+
+%files -f %{name}.lang
+%doc COPYING README
+%{_bindir}/cinnamon
+%{_bindir}/cinnamon-menu-editor
+%{_bindir}/cinnamon-settings
+%{_bindir}/cinnamon-extension-tool
+%{_sysconfdir}/gconf/schemas/cinnamon.schemas
+%{_sysconfdir}/xdg/menus/cinnamon-applications.menu
+%{_sysconfdir}/xdg/menus/cinnamon-settings.menu
+%{_datadir}/desktop-directories/cinnamon-*.directory
+%{_datadir}/glib-2.0/schemas/*.xml
+%{_datadir}/applications/cinnamon.desktop
+%{_datadir}/applications/cinnamon-settings.desktop
+%{_datadir}/xsessions/cinnamon.desktop
+%{_datadir}/gnome-session/sessions/cinnamon.session
+%dir %{_datadir}/cinnamon
+%{_datadir}/cinnamon/applets/
+%{_datadir}/cinnamon/js/
+%{_datadir}/cinnamon/search_providers/
+%{_datadir}/cinnamon/shaders/
+%{_datadir}/cinnamon/theme/
+%{_datadir}/cinnamon-menu-editor/
+%{_datadir}/cinnamon-settings/
+%{_datadir}/dbus-1/services/org.Cinnamon.CalendarServer.service
+%{_datadir}/dbus-1/services/org.Cinnamon.HotplugSniffer.service
+%{_libdir}/cinnamon/
+%{_libexecdir}/cinnamon-calendar-server
+%{_libexecdir}/cinnamon-perf-helper
+%{_libexecdir}/cinnamon-hotplug-sniffer
+%{_mandir}/man1/%{name}.1.*
+
+
+%changelog
+* Fri Jul 20 2012 Leigh Scott <leigh123linux at googlemail.com> - 1.4.0-7.UP1
+- Hardcode version for patches
+- Rearrange patches
+- Use install rather than cp
+- Fix scriptlets
+- Remove hardcoded file name from %%prep
+- Preserve timestamps in %%install
+- Remove extension from manpage in %%files
+- Correct spelling mistake
+- Add descriptions for patches
+- Add -p to install
+
+* Tue May 28 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-6.UP1
+- filter provides and requires
+
+* Mon May 28 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-5.UP1
+- Silence glib-compile-schemas scriplets
+- fix firefox patch for f17
+- fix power applet for f16
+
+* Mon May 28 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-4.UP1
+- add notification patch
+
+* Mon May 28 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-3.UP1
+- change %%define to %%global
+- fix files listed twice in %%files section
+- version patches
+- remove %%config from files (gnome-shell and gnome-menus doesn't use them
+ for the equivalent files)
+- drop login theme patch
+
+* Sun May 27 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-2.UP1
+- add configure option so it compiles on F17
+- fix release tag
+
+* Sun May 27 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-1.UP1
+- update to 1.4.0.UP1-1
+
+* Wed Mar 14 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-2
+- fix un-themed shutdown
+
+* Tue Mar 13 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.4.0-1
+- update to 1.4.0
+
+* Mon Feb 20 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.3.1-1
+- update to 1.3.1
+- remove static lib
+- remove mozilla plugin
+
+* Fri Feb 17 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.3.0-1
+- update to 1.3.0 release
+
+* Mon Jan 22 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.2.0-1
+- update to 1.2.0 release
+- add build requires muffin-devel
+- add Br libgudev1-devel
+- add only-show-in=GNOME to settings desktop file
+- make changes for source changes, applets, settings and session added
+- delete session files and use my own
+- move settings from lib to usr (it had no libs)
+- replace menu icon
+- change description
+
+* Wed Jan 04 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.1.3-2
+- add requires gnome-session
+- clean up spec file ready for review
+
+* Mon Jan 02 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.1.3-1
+- update to version 1.1.3
+
+* Sun Jan 01 2012 Leigh Scott <leigh123linux at fedoraproject.org> - 1.1.2-2
+- fix firefox launchers
+
+* Fri Dec 30 2011 Leigh Scott <leigh123linux at fedoraproject.org> - 1.1.2-1
+- first build based on gnome-shell srpm
+- add session files
+
diff --git a/menu.png b/menu.png
new file mode 100644
index 0000000..19225d9
Binary files /dev/null and b/menu.png differ
diff --git a/sources b/sources
index e69de29..e6f0ff4 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+03a9546c73793fa69a40139b3b38f66f cinnamon-1.4.0.UP1.tar.gz
More information about the scm-commits
mailing list