summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Dejonckheere2015-02-05 10:07:46 +0100
committerFlorian Dejonckheere2015-02-05 10:07:46 +0100
commitab1b2d468bdb61129289eb7724e69b45778f29ef (patch)
tree1c9bc78c20b886e10595ba18ff0deff97ad3b451
downloadaur-ab1b2d468bdb61129289eb7724e69b45778f29ef.tar.gz
Flatten directory structure
-rw-r--r--.SRCINFO24
-rwxr-xr-xPKGBUILD47
-rwxr-xr-xcompiz-deskmenu-editor1082
-rw-r--r--compiz-deskmenu.install26
-rwxr-xr-xhuge.patch3504
5 files changed, 4683 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..3c8de94d3c84
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,24 @@
+pkgbase = compiz-deskmenu
+ pkgdesc = Compiz Fusion deskmenu plugin
+ pkgver = 20130330
+ pkgrel = 1
+ url = http://opencompositing.org
+ install = compiz-deskmenu.install
+ arch = i686
+ arch = x86_64
+ license = GPL
+ makedepends = git
+ makedepends = intltool
+ depends = libwnck
+ depends = dbus-glib
+ depends = python-lxml
+ depends = pyxdg
+ source = git://anongit.compiz.org/users/crdlb/compiz-deskmenu
+ source = huge.patch
+ source = compiz-deskmenu-editor
+ md5sums = SKIP
+ md5sums = 9515713c15f412fb22cd2f735291fc74
+ md5sums = f9b8d902d85f7f690c32e330807d0e86
+
+pkgname = compiz-deskmenu
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100755
index 000000000000..ec11c716ccec
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,47 @@
+# Maintainer: Florian Dejonckheere <florian@floriandejonckheere.be>
+# Contributor: Julien MISCHKOWITZ <wain@archlinux.fr>
+# Contributor: Alessio Biancalana <dottorblaster@gmail.com>
+# Contributor: ShadowKyogre <shadowkyogre@aim.com>
+
+pkgname=compiz-deskmenu
+pkgver=20130330
+pkgrel=1
+pkgdesc="Compiz Fusion deskmenu plugin"
+arch=('i686' 'x86_64')
+url="http://opencompositing.org"
+license=('GPL')
+depends=('libwnck' 'dbus-glib' 'python-lxml' 'pyxdg')
+makedepends=('git' 'intltool')
+install=${pkgname}.install
+source=(git://anongit.compiz.org/users/crdlb/compiz-deskmenu
+ huge.patch
+ compiz-deskmenu-editor)
+md5sums=('SKIP'
+ '9515713c15f412fb22cd2f735291fc74'
+ 'f9b8d902d85f7f690c32e330807d0e86')
+
+pkgver()
+{
+ cd "${srcdir}/${pkgname}"
+ git describe --long | sed -E 's/([^-]*-g)/r\1/;s/-/./g'
+}
+
+package()
+{
+ cd "${srcdir}/${pkgname}"
+ patch -Np1 -i $srcdir/huge.patch
+ #rm -v "${srcdir}/${pkgname}/{deskmenu.c,deskmenu-common.h,org.compiz_fusion.deskmenu.service,deskmenu-service.xml}"
+}
+
+build()
+{
+ cd "${srcdir}/${pkgname}"
+ make
+}
+
+package()
+{
+ cd "${srcdir}/${pkgname}"
+ make DESTDIR="${pkgdir}" install
+ install -m 755 "${srcdir}/${pkgname}/autoconfig-compiz.py" "${pkgdir}/usr/bin/compiz-deskmenu-autoconfig"
+}
diff --git a/compiz-deskmenu-editor b/compiz-deskmenu-editor
new file mode 100755
index 000000000000..c5c16d1ac4f1
--- /dev/null
+++ b/compiz-deskmenu-editor
@@ -0,0 +1,1082 @@
+#!/usr/bin/env python2
+#TODO: upcoming pipemenu stuff
+import gtk, os
+from lxml import etree
+from xdg import BaseDirectory
+import re #This is to autoset file mode for *.desktop icons
+import ConfigParser
+
+try:
+ import dbus
+except ImportError:
+ dbus = None
+
+class DeskmenuEditor(gtk.Window):
+
+ def __init__(self):
+ gtk.Window.__init__(self)
+
+ self.props.title = 'Compiz Deskmenu Editor'
+ self.props.icon_name = 'gtk-edit'
+ self.props.border_width = 12
+ self.set_size_request(400, 400)
+ self.model = gtk.TreeStore(object)
+ self.add_menu(menu)
+
+ vbox = gtk.VBox(spacing=12)
+
+ scrolled = gtk.ScrolledWindow()
+ scrolled.props.hscrollbar_policy = gtk.POLICY_NEVER
+ scrolled.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
+ treeview = gtk.TreeView(self.model)
+ treeview.set_reorderable(True)
+ cell = gtk.CellRendererText()
+ elements = gtk.TreeViewColumn('Item', cell)
+ elements.set_cell_data_func(cell, self.get_type)
+ treeview.append_column(elements)
+
+ name = gtk.TreeViewColumn('Name')
+
+ cell = gtk.CellRendererPixbuf()
+ name.pack_start(cell, False)
+ name.set_cell_data_func(cell, self.get_icon)
+
+ cell = gtk.CellRendererText()
+ name.pack_start(cell)
+ name.set_cell_data_func(cell, self.get_name)
+
+ treeview.append_column(name)
+ scrolled.add(treeview)
+ vbox.pack_start(scrolled, True, True)
+ targets = [
+ ('deskmenu-element', gtk.TARGET_SAME_WIDGET, 0),
+ ('text/uri-list', 0, 1),
+ ]
+ treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_MOVE)
+ treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_MOVE)
+
+ treeview.connect('drag-data-get', self.on_drag_data_get)
+ treeview.connect('drag-data-received', self.on_drag_data_received)
+
+ treeview.connect('row-activated', self.on_row_activated)
+
+ treeview.connect('button-press-event', self.on_treeview_button_press_event)
+ treeview.expand_all()
+
+ self.selection = treeview.get_selection()
+ self.selection.connect('changed', self.on_selection_changed)
+
+ buttonbox = gtk.HButtonBox()
+ vbox.pack_end(buttonbox, False, False)
+
+ new = gtk.Button(stock=gtk.STOCK_NEW)
+ new.connect('clicked', self.on_new_clicked)
+ buttonbox.pack_start(new)
+ self.edit = gtk.Button(stock=gtk.STOCK_EDIT)
+ self.edit.connect('clicked', self.on_edit_clicked)
+ buttonbox.pack_start(self.edit)
+ self.delete = gtk.Button(stock=gtk.STOCK_DELETE)
+ self.delete.connect('clicked', self.on_delete_clicked)
+ buttonbox.pack_start(self.delete)
+ close = gtk.Button(stock=gtk.STOCK_CLOSE)
+ close.connect('clicked', self.on_close_clicked)
+ buttonbox.pack_end(close)
+
+ self.add(vbox)
+
+ self.popup = gtk.Menu()
+ self.edit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_EDIT)
+ self.edit_menu.connect('activate', self.on_edit_clicked)
+ self.popup.append(self.edit_menu)
+ self.delete_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_DELETE)
+ self.delete_menu.connect('activate', self.on_delete_clicked)
+ self.popup.append(self.delete_menu)
+ self.popup.show_all()
+
+ self.connect('destroy', self.on_close_clicked)
+
+ self.show_all()
+
+ def add_menu(self, m, parent=None):
+ for item in m.children:
+ iter = self.model.append(parent, [item])
+ if item.node.tag == 'menu':
+ self.add_menu(item, iter)
+
+ def get_name(self, column, cell, model, iter):
+ name = model.get_value(iter, 0).get_name()
+ if name is None:
+ name = ''
+ cell.set_property('text', name)
+
+ def get_type(self, column, cell, model, iter):
+ typ = model.get_value(iter, 0).get_type()
+ if typ is None:
+ typ = ''
+ cell.set_property('text', typ)
+
+ def get_icon(self, column, cell, model, iter):
+ icon = model.get_value(iter, 0).get_icon()
+ icon_mode = model.get_value(iter, 0).get_icon_mode()
+ if icon is not None:
+ if icon_mode is not None:
+ w = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
+ cell.set_property('pixbuf', gtk.gdk.pixbuf_new_from_file_at_size(icon, w[0], w[0]))
+ cell.set_property('icon-name', None) #possibly reduntant safety measure
+ else:
+ cell.set_property('icon-name', icon)
+ cell.set_property('pixbuf', None) #possibly reduntant safety measure
+ else:
+ cell.set_property('icon-name', None)
+ cell.set_property('pixbuf', None)
+
+ def on_new_clicked(self, widget):
+
+ NewItemDialog(*self.selection.get_selected())
+
+ def on_edit_clicked(self, widget):
+
+ EditItemDialog(*self.selection.get_selected())
+
+ def on_delete_clicked(self, widget):
+
+ model, row = self.selection.get_selected()
+
+ parent = None
+ if row:
+ current = model[row][0].node
+
+ if current.tag == 'menu' and len(current):
+ warning = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE, 'Delete menu element with %s children?' %len(current))
+ warning.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
+ if warning.run() != gtk.RESPONSE_ACCEPT:
+ warning.destroy()
+ return
+ warning.destroy()
+
+ parent = model[row].parent
+ if parent is not None:
+ parent = parent[0].node
+ else:
+ parent = menu.node
+ parent.remove(current)
+ model.remove(row)
+
+ write_menu()
+
+ def on_close_clicked(self, widget):
+
+ write_menu()
+ gtk.main_quit()
+
+ def on_drag_data_get(self, treeview, context, selection, target_id,
+ etime):
+ treeselection = treeview.get_selection()
+ model, iter = treeselection.get_selected()
+ data = model.get_string_from_iter(iter)
+ selection.set(selection.target, 8, data)
+
+ def on_drag_data_received(self, treeview, context, x, y, selection,
+ info, etime):
+ model = treeview.get_model()
+ data = selection.data
+
+ drop_info = treeview.get_dest_row_at_pos(x, y)
+ if selection.type == 'deskmenu-element':
+ source = model[data][0]
+ if drop_info:
+ path, position = drop_info
+ siter = model.get_iter(data)
+ diter = model.get_iter(path)
+
+ if model.get_path(model.get_iter_from_string(data)) == path:
+ return
+
+ dest = model[path][0]
+ if context.action == gtk.gdk.ACTION_MOVE:
+ source.node.getparent().remove(source.node)
+
+ if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+ gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
+ dest.node.append(source.node)
+ fiter = model.append(diter, row=(source,))
+ else:
+ i = dest.node.getparent().index(dest.node)
+ if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+ gtk.TREE_VIEW_DROP_BEFORE):
+ dest.node.getparent().insert(i, source.node)
+ fiter = model.insert_before(None, diter, row=(source,))
+ else:
+ dest.node.getparent().insert(i+1, source.node)
+ fiter = model.insert_after(None, diter, row=(source,))
+
+ if model.iter_has_child(siter):
+ citer = model.iter_children(siter)
+ while citer is not None:
+ model.append(fiter, row=(model[citer][0],))
+ citer = model.iter_next(citer)
+ if context.action == gtk.gdk.ACTION_MOVE:
+ context.finish(True, True, etime)
+
+ elif selection.type == 'text/uri-list':
+ if drop_info:
+ path, position = drop_info
+ uri = selection.data.replace('file:///', '/').strip()
+ entry = ConfigParser.ConfigParser()
+ entry.read(uri)
+ launcher = Launcher()
+ launcher.name = etree.SubElement(launcher.node, 'name')
+ launcher.icon = etree.SubElement(launcher.node, 'icon')
+ launcher.command = etree.SubElement(launcher.node, 'command')
+ try:
+ launcher.name.text = entry.get('Desktop Entry', 'Name')
+ if re.search("/", entry.get('Desktop Entry', 'Icon')):
+ launcher.icon.attrib['mode1'] = 'file'
+ launcher.icon.text = entry.get('Desktop Entry', 'Icon')
+ #launcher.icon.text = entry.get('Desktop Entry', 'Icon').split('.')[0]
+ launcher.command.text = entry.get('Desktop Entry', 'Exec').split('%')[0]
+ except ConfigParser.Error:
+ return
+ dest = model[path][0]
+ diter = model.get_iter(path)
+ if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+ gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
+ dest.node.append(launcher.node)
+ fiter = model.append(diter, row=(launcher,))
+ else:
+ i = dest.node.getparent().index(dest.node)
+ if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+ gtk.TREE_VIEW_DROP_BEFORE):
+ dest.node.getparent().insert(i, launcher.node)
+ fiter = model.insert_before(None, diter, row=(launcher,))
+ else:
+ dest.node.getparent().insert(i+1, launcher.node)
+ fiter = model.insert_after(None, diter, row=(launcher,))
+ if context.action == gtk.gdk.ACTION_MOVE:
+ context.finish(True, True, etime)
+
+ write_menu()
+
+ return
+
+ def on_selection_changed(self, selection):
+
+ model, row = selection.get_selected()
+
+ sensitive = row and model.get_value(row, 0).editable
+
+ self.edit.props.sensitive = sensitive
+ self.edit_menu.props.sensitive = sensitive
+ self.delete.props.sensitive = row
+ self.delete_menu.props.sensitive = row
+
+ def on_row_activated(self, treeview, path, view_column):
+
+ model = treeview.get_model()
+ EditItemDialog(model, model.get_iter(path))
+
+ def on_treeview_button_press_event(self, treeview, event):
+ if event.button == 3:
+ pthinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
+ if pthinfo is not None:
+ path, col, cellx, celly = pthinfo
+ treeview.grab_focus()
+ treeview.set_cursor(path, col, 0)
+ self.popup.popup(None, None, None, event.button, event.time)
+ return 1
+
+
+class NewItemDialog(gtk.Dialog):
+
+ elementlist = ['Launcher', 'Menu', 'Separator', 'Windows List',
+ 'Viewports List', 'Reload']
+
+ def __init__(self, model, row):
+ gtk.Dialog.__init__(self, 'New Item', None, 0, None)
+
+ self.set_size_request(250, 250)
+
+ self.props.border_width = 6
+ self.vbox.props.spacing = 6
+ self.set_has_separator(False)
+ self.treeview = self.make_treeview()
+
+ scroll = gtk.ScrolledWindow()
+ scroll.add(self.treeview)
+ scroll.props.hscrollbar_policy = gtk.POLICY_NEVER
+ scroll.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
+ self.vbox.pack_start(scroll, True, True)
+
+ self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT)
+
+ self.action_area.props.border_width = 0
+
+ self.show_all()
+
+ element = None
+
+ if self.run() == gtk.RESPONSE_ACCEPT:
+ m, r = self.treeview.get_selection().get_selected()
+ if r:
+ elementname = m[r][0]
+ else:
+ self.destroy()
+ return
+ parent = sibling = None
+ if row:
+ current = model[row][0]
+ if current.node.tag == 'menu':
+ parent = row
+ else:
+ parent = model[row].parent
+ if parent is not None:
+ parent = parent.iter
+ sibling = row
+ if parent:
+ parentelement = model[parent][0]
+ else:
+ parentelement = menu
+
+ element = elementsbyname[elementname]()
+ if sibling:
+ position = parentelement.node.index(current.node) + 1
+ parentelement.node.insert(position, element.node)
+ model.insert_after(parent, sibling, row=(element,))
+ else:
+ model.append(parent, row=(element,))
+ parentelement.node.append(element.node)
+
+ self.destroy()
+ write_menu()
+
+ if element and element.editable:
+ EditItemDialog(element=element)
+
+ def make_treeview(self):
+ model = gtk.ListStore(str)
+ for el in self.elementlist:
+ model.append([el])
+
+ treeview = gtk.TreeView(model)
+ column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
+ treeview.set_headers_visible(False)
+ treeview.append_column(column)
+
+ treeview.connect('row-activated', self.on_row_activated)
+ return treeview
+
+ def on_row_activated(self, treeview, path, view_column):
+ self.response(gtk.RESPONSE_ACCEPT)
+
+
+class EditItemDialog(gtk.Dialog):
+
+ def __init__(self, model=None, row=None, element=None):
+ gtk.Dialog.__init__(self, 'Edit Item', None, 0, None)
+
+ self.set_size_request(300, -1)
+
+ self.props.border_width = 6
+ self.vbox.props.spacing = 6
+ self.set_has_separator(False)
+
+ if element is None:
+ if not row:
+ return
+ element = model.get_value(row, 0)
+
+ if not element.editable:
+ return
+
+ for widget in element.get_options():
+ self.vbox.pack_start(widget, False, False)
+
+ self.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+
+ self.show_all()
+ self.run()
+ self.destroy()
+ write_menu()
+
+
+
+class Item(object):
+
+ def __init__(self, node=None, parent=None, type=None):
+
+ self.editable = False
+
+ if node is None:
+ self.node = etree.Element('item')
+ if type is not None:
+ self.node.attrib['type'] = type
+
+ else:
+ self.node = node
+
+ def get_name(self):
+ return None
+
+ def get_type(self):
+ return 'Item'
+
+ def get_icon(self):
+ return None
+
+ def get_icon_mode(self):
+ return None
+
+class Launcher(Item):
+
+ def __init__(self, node=None):
+
+ Item.__init__(self, node)
+
+ if node is None:
+ self.node = etree.Element('item', type='launcher')
+
+ self.editable = True
+
+ def get_name(self):
+
+ subnode = self.node.find('name')
+ if subnode is not None:
+ name = subnode.text
+ if subnode.attrib.get('mode') == 'exec':
+ name = 'exec: %s' %name
+ return name
+ else:
+ return None
+
+ def get_type(self):
+ return 'Launcher'
+
+ def get_icon(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ return iconnode.text
+ else:
+ return None
+
+ def get_icon_mode(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ if iconnode.attrib.get('mode1') == 'file':
+ return iconnode.attrib.get('mode1')
+ else:
+ return None
+
+ def get_options(self):
+
+ retlist = []
+ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Name:</b>')
+ widget = gtk.Entry()
+
+ namenode = self.node.find('name')
+ if namenode is not None:
+ name = namenode.text
+ else:
+ name = ''
+ widget.props.text = name
+ widget.connect('changed', self.on_subnode_changed, 'name')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Name mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('Execute')
+ widget.props.active = namenode is not None and namenode.attrib.get('mode') == 'exec'
+ widget.connect('changed', self.on_name_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon:</b>')
+ widget = gtk.Entry()
+
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ icon = iconnode.text
+ else:
+ icon = ''
+ widget.props.text = icon
+ widget.connect('changed', self.on_subnode_changed, 'icon')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('File Path')
+ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
+ widget.connect('changed', self.on_icon_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Command:</b>')
+ widget = gtk.Entry()
+ commandnode = self.node.find('command')
+ if commandnode is not None:
+ command = commandnode.text
+ else:
+ command = ''
+ widget.props.text = command
+ widget.connect('changed', self.on_subnode_changed, 'command')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ return retlist
+
+ def on_subnode_changed(self, widget, tag):
+ text = widget.props.text
+ subnode = self.node.find(tag)
+ if text:
+ if subnode is None:
+ subnode = etree.SubElement(self.node, tag)
+ subnode.text = text
+ else:
+ if subnode is not None:
+ self.node.remove(subnode)
+
+ def on_name_mode_changed(self, widget):
+ namenode = self.node.find('name')
+ if widget.props.active:
+ if namenode is None:
+ namenode = etree.SubElement(self.node, 'name')
+ namenode.attrib['mode'] = 'exec'
+ elif 'mode' in namenode.attrib:
+ del namenode.attrib['mode']
+
+ def on_icon_mode_changed(self, widget):
+ iconnode = self.node.find('icon')
+ if widget.props.active:
+ if iconnode is None:
+ iconnode = etree.SubElement(self.node, 'icon')
+ iconnode.attrib['mode1'] = 'file'
+ elif 'mode1' in iconnode.attrib:
+ del iconnode.attrib['mode1']
+
+class Windowlist(Item):
+
+ def __init__(self, node=None):
+ Item.__init__(self, node, type='windowlist')
+ self.editable = True
+
+ def get_icon(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ return iconnode.text
+ else:
+ return None
+
+ def get_icon_mode(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ if iconnode.attrib.get('mode1') == 'file':
+ return iconnode.attrib.get('mode1')
+ else:
+ return None
+
+ def get_options(self):
+ retlist = []
+ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon:</b>')
+ widget = gtk.Entry()
+
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ icon = iconnode.text
+ else:
+ icon = ''
+ widget.props.text = icon
+ widget.connect('changed', self.on_subnode_changed, 'icon')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('File Path')
+ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
+ widget.connect('changed', self.on_icon_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ return retlist
+
+ def on_subnode_changed(self, widget, tag):
+ text = widget.props.text
+ subnode = self.node.find(tag)
+ if text:
+ if subnode is None:
+ subnode = etree.SubElement(self.node, tag)
+ subnode.text = text
+ else:
+ if subnode is not None:
+ self.node.remove(subnode)
+
+ def on_icon_mode_changed(self, widget):
+ iconnode = self.node.find('icon')
+ if widget.props.active:
+ if iconnode is None:
+ iconnode = etree.SubElement(self.node, 'icon')
+ iconnode.attrib['mode1'] = 'file'
+ elif 'mode1' in iconnode.attrib:
+ del iconnode.attrib['mode1']
+
+ def get_type(self):
+ return 'Windows list'
+
+class Viewportlist(Item):
+
+ def __init__(self, node=None): #change to be an attribute of viewportlist
+ Item.__init__(self, node, type='viewportlist')
+ self.editable = True
+ self.wrap = self.node.find('wrap')
+
+ def get_type(self):
+ return 'Viewports list'
+
+ def get_icon(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ return iconnode.text
+ else:
+ return None
+
+ def get_icon_mode(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ if iconnode.attrib.get('mode1') == 'file':
+ return iconnode.attrib.get('mode1')
+ else:
+ return None
+
+
+ def get_options(self):
+ retlist = []
+ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+ widget = gtk.CheckButton("Wrap Viewports")
+ widget.props.active = self.get_wrap()
+ widget.connect('toggled', self.on_wrap_changed)
+ retlist.append(widget)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon:</b>')
+ widget = gtk.Entry()
+
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ icon = iconnode.text
+ else:
+ icon = ''
+ widget.props.text = icon
+ widget.connect('changed', self.on_subnode_changed, 'icon')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('File Path')
+ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
+ widget.connect('changed', self.on_icon_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ return retlist
+
+ def get_wrap(self):
+ if self.wrap is not None:
+ return self.wrap.text == 'true'
+ return False
+
+ def on_subnode_changed(self, widget, tag):
+ text = widget.props.text
+ subnode = self.node.find(tag)
+ if text:
+ if subnode is None:
+ subnode = etree.SubElement(self.node, tag)
+ subnode.text = text
+ else:
+ if subnode is not None:
+ self.node.remove(subnode)
+
+ def on_wrap_changed(self, widget):
+ if self.wrap is None:
+ self.wrap = etree.SubElement(self.node, 'wrap')
+ if widget.props.active:
+ text = 'true'
+ else:
+ text = 'false'
+ self.wrap.text = text
+
+ def on_icon_mode_changed(self, widget):
+ iconnode = self.node.find('icon')
+ if widget.props.active:
+ if iconnode is None:
+ iconnode = etree.SubElement(self.node, 'icon')
+ iconnode.attrib['mode1'] = 'file'
+ elif 'mode1' in iconnode.attrib:
+ del iconnode.attrib['mode1']
+
+class Reload(Item):
+
+ def __init__(self, node=None):
+ Item.__init__(self, node, type='reload')
+ self.editable = True
+
+ def get_type(self):
+ return 'Reload'
+
+ def get_icon(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ return iconnode.text
+ else:
+ return None
+
+ def get_icon_mode(self):
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ if iconnode.attrib.get('mode1') == 'file':
+ return iconnode.attrib.get('mode1')
+ else:
+ return None
+
+
+ def get_options(self):
+ retlist = []
+ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon:</b>')
+ widget = gtk.Entry()
+
+ iconnode = self.node.find('icon')
+ if iconnode is not None:
+ icon = iconnode.text
+ else:
+ icon = ''
+ widget.props.text = icon
+ widget.connect('changed', self.on_subnode_changed, 'icon')
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('File Path')
+ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
+ widget.connect('changed', self.on_icon_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ return retlist
+
+ def on_subnode_changed(self, widget, tag):
+ text = widget.props.text
+ subnode = self.node.find(tag)
+ if text:
+ if subnode is None:
+ subnode = etree.SubElement(self.node, tag)
+ subnode.text = text
+ else:
+ if subnode is not None:
+ self.node.remove(subnode)
+
+ def on_icon_mode_changed(self, widget):
+ iconnode = self.node.find('icon')
+ if widget.props.active:
+ if iconnode is None:
+ iconnode = etree.SubElement(self.node, 'icon')
+ iconnode.attrib['mode1'] = 'file'
+ elif 'mode1' in iconnode.attrib:
+ del iconnode.attrib['mode1']
+
+class Separator(object):
+
+ def __init__(self, node=None, parent=None):
+ self.node = node
+ self.editable = False
+ if self.node is None:
+ self.node = etree.Element('separator')
+
+ def get_name(self):
+ return '---'
+
+ def get_type(self):
+ return 'Separator'
+
+ def get_icon(self):
+ return None
+
+ def get_icon_mode(self):
+ return None
+
+class Menu(object):
+
+ def __init__(self, node=None):
+ self.node = node
+ self.children = []
+ self.editable = True
+
+ if node is None:
+ self.node = etree.Element('menu', name='')
+
+ for child in self.node.getchildren():
+ try:
+ self.children.append(self.make_child(child))
+ except KeyError:
+ pass
+
+
+ def make_child(self, child):
+ return elements[child.tag](child)
+
+ def get_name(self):
+ name = self.node.attrib.get('name', '')
+ if self.node.attrib.get('mode') == 'exec':
+ name = 'exec: %s' %name
+ return name
+
+#TODO: make this say exec: $name when it detects mode
+ def get_icon(self):
+ return self.node.attrib.get('icon', '')
+
+ def get_icon_mode(self):
+ if self.node.attrib.get('mode1') == 'file':
+ return self.node.attrib.get('mode1')
+ else:
+ return None
+
+ def get_type(self):
+ return 'Menu'
+
+ def get_options(self):
+
+ retlist = []
+ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ label.set_markup('<b>Name:</b>')
+ widget = gtk.Entry()
+ widget.props.text = self.node.attrib.get('name', '')
+ widget.connect('changed', self.on_name_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Name mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('Execute')
+ widget.props.active = self.node.attrib.get('name') is not None and self.node.attrib.get('mode') == 'exec'
+ widget.connect('changed', self.on_name_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ #TODO:Make this actually edit icon
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon:</b>')
+ widget = gtk.Entry()
+ if self.node.attrib.get('icon') is not None:
+ widget.props.text = self.node.attrib.get('icon')
+ else:
+ widget.props.text = ''
+ #widget.props.text = self.node.attrib.get('icon')
+ widget.connect('changed', self.on_icon_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ label = gtk.Label()
+ label.set_alignment(0, 0.5)
+ sgroup.add_widget(label)
+ label.set_markup('<b>Icon mode:</b>')
+ widget = gtk.combo_box_new_text()
+ widget.append_text('Normal')
+ widget.append_text('File Path')
+ widget.props.active = self.node.attrib.get('icon') is not None and self.node.attrib.get('mode1') == 'file'
+ widget.connect('changed', self.on_icon_mode_changed)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label)
+ hbox.pack_start(widget, True, True)
+ retlist.append(hbox)
+
+ return retlist
+
+ def on_name_changed(self, widget):
+ self.node.attrib['name'] = widget.props.text
+ def on_name_mode_changed(self, widget): #EDIT THIS
+ if widget.props.active:
+ self.node.attrib['mode'] = 'exec'
+ elif 'mode' in self.node.attrib:
+ del self.node.attrib['mode']
+ def on_icon_changed(self, widget):
+ if widget.props.text != '':
+ self.node.attrib['icon'] = widget.props.text
+ else:
+ del self.node.attrib['icon']
+ def on_icon_mode_changed(self, widget): #EDIT THIS
+ if widget.props.active:
+ self.node.attrib['mode1'] = 'file'
+ elif 'mode1' in self.node.attrib:
+ del self.node.attrib['mode1']
+
+itemtypes = {
+ 'launcher': Launcher,
+ 'windowlist': Windowlist,
+ 'viewportlist': Viewportlist,
+ 'reload': Reload,
+}
+
+
+def make_item(node):
+
+ itemtype = node.attrib.get('type')
+ return itemtypes.get(itemtype, Item)(node)
+
+
+def indent(elem, level=0):
+ i = "\n" + level*" "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ for e in elem:
+ indent(e, level+1)
+ if not e.tail or not e.tail.strip():
+ e.tail = i + " "
+ if not e.tail or not e.tail.strip():
+ e.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
+
+def write_menu():
+
+ indent(menu.node)
+ menufile.write(open(os.path.join(configdir, 'menu.xml'), 'w'))
+
+ if bus is not None:
+ try:
+ bus.get_object('org.compiz_fusion.deskmenu',
+ '/org/compiz_fusion/deskmenu/menu').reload()
+ except dbus.DBusException:
+ return
+
+elements = {'menu': Menu, 'item': make_item, 'separator': Separator}
+
+elementsbyname = {
+ 'Launcher': Launcher,
+ 'Windows List': Windowlist,
+ 'Viewports List': Viewportlist,
+ 'Reload': Reload,
+ 'Separator': Separator,
+ 'Menu': Menu,
+}
+
+
+if __name__ == '__main__':
+
+ if dbus:
+ try:
+ bus = dbus.SessionBus()
+ except dbus.DBusException:
+ bus = None
+ else:
+ bus = None
+
+ filename = BaseDirectory.load_first_config('compiz/deskmenu/menu.xml')
+ menufile = etree.parse(filename)
+ root = menufile.getroot()
+ menu = Menu(root)
+ configdir = BaseDirectory.save_config_path('compiz/deskmenu')
+ DeskmenuEditor()
+ gtk.main()
diff --git a/compiz-deskmenu.install b/compiz-deskmenu.install
new file mode 100644
index 000000000000..3f61f9128257
--- /dev/null
+++ b/compiz-deskmenu.install
@@ -0,0 +1,26 @@
+post_install() {
+cat <<-EndOfMessage
+==> compiz-deskmenu usage:
+you can run "compiz-deskmenu-autoconfig" or manually set following in ccsm:
+ + General Options > Commands > Commands > Command line 0 to "compiz-deskmenu"
+ + General Options > Commands > Key bindings > Run command 0 to "<Control>space"
+ + Viewport Switcher > Desktop-based Viewport Switching > Plugin for initiate
+ action to "core"
+ + Viewport Switcher > Desktop-based Viewport Switching > Action name for
+ initiate to "run_command0_key"
+EndOfMessage
+cat <<-EndOfMessage
+==> You will also need to delete any reload items you have via a text editor
+(your menu should be in ~/.config/compiz/deskmenu/menu.xml)
+in order to make sure you don't have an excess element floating around.
+The changes that were made to make pipeitems possible make this item obselete.
+EndOfMessage
+cat <<-EndOfMessage
+==> This update removes dbus dependencies, just so you know.
+EndOfMessage
+/bin/true
+}
+
+post_upgrade() {
+ post_install
+}
diff --git a/huge.patch b/huge.patch
new file mode 100755
index 000000000000..71ee56a6eaec
--- /dev/null
+++ b/huge.patch
@@ -0,0 +1,3504 @@
+diff -aur compiz-deskmenu/Makefile compiz-deskmenu3/Makefile
+--- compiz-deskmenu/Makefile 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/Makefile 2010-11-15 15:58:20.000000000 -0800
+@@ -1,34 +1,24 @@
+ PREFIX := /usr
+
+-CPPFLAGS := `pkg-config --cflags dbus-glib-1 gdk-2.0 gtk+-2.0 libwnck-1.0`
+-CPPFLAGS_CLIENT := `pkg-config --cflags dbus-glib-1`
++CPPFLAGS := `pkg-config --cflags gdk-2.0 gtk+-2.0 libwnck-1.0`
+ WARNINGS := -Wall -Wextra -Wno-unused-parameter
+ CFLAGS := -O2 $(WARNINGS)
+-LDFLAGS := `pkg-config --libs dbus-glib-1 gdk-2.0 gtk+-2.0 libwnck-1.0`
++LDFLAGS := `pkg-config --libs gdk-2.0 gtk+-2.0 libwnck-1.0`
+ LDFLAGS_CLIENT := `pkg-config --libs dbus-glib-1`
+
+-all: compiz-deskmenu-menu compiz-deskmenu
++all: compiz-deskmenu
+
+-compiz-deskmenu: deskmenu.c deskmenu-common.h
+- $(CC) $(CPPFLAGS_CLIENT) $(CFLAGS) $(LDFLAGS_CLIENT) -o $@ $<
+-
+-compiz-deskmenu-menu: deskmenu-menu.c deskmenu-wnck.c deskmenu-wnck.h deskmenu-glue.h deskmenu-common.h deskmenu-menu.h
++compiz-deskmenu: deskmenu-menu.c deskmenu-wnck.c deskmenu-wnck.h deskmenu-menu.h
+
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ deskmenu-menu.c deskmenu-wnck.c
+
+-deskmenu-glue.h: deskmenu-service.xml
+- dbus-binding-tool --mode=glib-server --prefix=deskmenu --output=$@ $^
+-
+ install: all
+ mkdir -p $(DESTDIR)$(PREFIX)/bin/
+ install compiz-deskmenu $(DESTDIR)$(PREFIX)/bin/
+- install compiz-deskmenu-menu $(DESTDIR)$(PREFIX)/bin/
+ install compiz-deskmenu-editor $(DESTDIR)$(PREFIX)/bin/
+ mkdir -p $(DESTDIR)/etc/xdg/compiz/deskmenu/
+ install menu.xml $(DESTDIR)/etc/xdg/compiz/deskmenu/
+- mkdir -p $(DESTDIR)$(PREFIX)/share/dbus-1/services/
+- install org.compiz_fusion.deskmenu.service $(DESTDIR)$(PREFIX)/share/dbus-1/services/
+
+ clean:
+- rm -f compiz-deskmenu compiz-deskmenu-menu deskmenu-glue.h
++ rm -f compiz-deskmenu
+
+diff -aur compiz-deskmenu/README compiz-deskmenu3/README
+--- compiz-deskmenu/README 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/README 2010-11-16 17:55:54.000000000 -0800
+@@ -1,10 +1,11 @@
+ Compiz Deskmenu
++== Credits ==
++Kudos to Christopher Williams for the excellent program. This new version contains several updates.
+
+ === Requirements ===
+
+ * A recent version of GLib 2.x and GTK+ 2.x
+ * libwnck (2.20 is probably best, but not strictly required)
+- * dbus (you must have a running session bus) and dbus-glib
+
+ It does not strictly require compiz (but it does need a recent version of the
+ vpswitch plugin for 'Initiate on Desktop' to work)
+@@ -48,15 +49,13 @@
+ * separator: a simple GtkSeparatorMenuItem
+ * windowlist: libwnck-based window list menu.
+ * viewportlist: libwnck-based viewport list menu.
+- * reload: reload button (it actually quits the menu)
++ * pipe: dynamically creates any other menu item, as long as the script
++ you make formats your entries properly
+
+-Menu editor coming soon.
++Also, you can edit other menu files by doing this:
++compiz-deskmenu-editor /path/to/file
+
+ === Implementation ===
+
+-It compiles into two binaries, compiz-deskmenu and compiz-deskmenu-menu.
+-compiz-deskmenu is a simple dbus client that connects
+-to org.compiz_fusion.deskmenu and calls the show method. The actual menu is
+-compiz-deskmenu-menu, but you shouldn't ever need to manually launch it; the
+-dbus service file will cause it to be automatically spawned when the name is
+-requested.
++Compiz-deskmenu compiles into one binary, which is what parses and
++displays the menu. If demanded, an apwal-esque version of this will be on its way.
+diff -aur compiz-deskmenu/compiz-deskmenu-editor compiz-deskmenu3/compiz-deskmenu-editor
+--- compiz-deskmenu/compiz-deskmenu-editor 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/compiz-deskmenu-editor 2010-11-16 17:53:13.000000000 -0800
+@@ -1,734 +1,1070 @@
+-#!/usr/bin/env python
+-
++#!/usr/bin/env python2
++#TODO: An actual icon dialog and editing non-default files
++import sys
+ import gtk, os
+ from lxml import etree
+ from xdg import BaseDirectory
++import re #This is to autoset file mode for *.desktop icons
+ import ConfigParser
+
+-try:
+- import dbus
+-except ImportError:
+- dbus = None
+-
+ class DeskmenuEditor(gtk.Window):
+
+- def __init__(self):
+- gtk.Window.__init__(self)
+-
+- self.props.title = 'Compiz Deskmenu Editor'
+- self.props.icon_name = 'gtk-edit'
+- self.props.border_width = 12
+- self.set_size_request(400, 400)
+- self.model = gtk.TreeStore(object)
+- self.add_menu(menu)
+-
+- vbox = gtk.VBox(spacing=12)
+-
+- scrolled = gtk.ScrolledWindow()
+- scrolled.props.hscrollbar_policy = gtk.POLICY_NEVER
+- scrolled.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
+- treeview = gtk.TreeView(self.model)
+- treeview.set_reorderable(True)
+- cell = gtk.CellRendererText()
+- elements = gtk.TreeViewColumn('Item', cell)
+- elements.set_cell_data_func(cell, self.get_type)
+- treeview.append_column(elements)
+-
+- name = gtk.TreeViewColumn('Name')
+-
+- cell = gtk.CellRendererPixbuf()
+- name.pack_start(cell, False)
+- name.set_cell_data_func(cell, self.get_icon)
+-
+- cell = gtk.CellRendererText()
+- name.pack_start(cell)
+- name.set_cell_data_func(cell, self.get_name)
+-
+- treeview.append_column(name)
+- scrolled.add(treeview)
+- vbox.pack_start(scrolled, True, True)
+- targets = [
+- ('deskmenu-element', gtk.TARGET_SAME_WIDGET, 0),
+- ('text/uri-list', 0, 1),
+- ]
+- treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_MOVE)
+- treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_MOVE)
+-
+- treeview.connect('drag-data-get', self.on_drag_data_get)
+- treeview.connect('drag-data-received', self.on_drag_data_received)
+-
+- treeview.connect('row-activated', self.on_row_activated)
+-
+- treeview.connect('button-press-event', self.on_treeview_button_press_event)
+- treeview.expand_all()
+-
+- self.selection = treeview.get_selection()
+- self.selection.connect('changed', self.on_selection_changed)
+-
+- buttonbox = gtk.HButtonBox()
+- vbox.pack_end(buttonbox, False, False)
+-
+- new = gtk.Button(stock=gtk.STOCK_NEW)
+- new.connect('clicked', self.on_new_clicked)
+- buttonbox.pack_start(new)
+- self.edit = gtk.Button(stock=gtk.STOCK_EDIT)
+- self.edit.connect('clicked', self.on_edit_clicked)
+- buttonbox.pack_start(self.edit)
+- self.delete = gtk.Button(stock=gtk.STOCK_DELETE)
+- self.delete.connect('clicked', self.on_delete_clicked)
+- buttonbox.pack_start(self.delete)
+- close = gtk.Button(stock=gtk.STOCK_CLOSE)
+- close.connect('clicked', self.on_close_clicked)
+- buttonbox.pack_end(close)
+-
+- self.add(vbox)
+-
+- self.popup = gtk.Menu()
+- self.edit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_EDIT)
+- self.edit_menu.connect('activate', self.on_edit_clicked)
+- self.popup.append(self.edit_menu)
+- self.delete_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_DELETE)
+- self.delete_menu.connect('activate', self.on_delete_clicked)
+- self.popup.append(self.delete_menu)
+- self.popup.show_all()
+-
+- self.connect('destroy', self.on_close_clicked)
+-
+- self.show_all()
+-
+- def add_menu(self, m, parent=None):
+- for item in m.children:
+- iter = self.model.append(parent, [item])
+- if item.node.tag == 'menu':
+- self.add_menu(item, iter)
+-
+- def get_name(self, column, cell, model, iter):
+- name = model.get_value(iter, 0).get_name()
+- if name is None:
+- name = ''
+- cell.set_property('text', name)
+-
+- def get_type(self, column, cell, model, iter):
+- typ = model.get_value(iter, 0).get_type()
+- if typ is None:
+- typ = ''
+- cell.set_property('text', typ)
+-
+- def get_icon(self, column, cell, model, iter):
+- icon = model.get_value(iter, 0).get_icon()
+- if icon is not None:
+- cell.set_property('icon-name', icon)
+- else:
+- cell.set_property('icon-name', None)
+-
+- def on_new_clicked(self, widget):
+-
+- NewItemDialog(*self.selection.get_selected())
+-
+- def on_edit_clicked(self, widget):
+-
+- EditItemDialog(*self.selection.get_selected())
+-
+- def on_delete_clicked(self, widget):
+-
+- model, row = self.selection.get_selected()
+-
+- parent = None
+- if row:
+- current = model[row][0].node
+-
+- if current.tag == 'menu' and len(current):
+- warning = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE, 'Delete menu element with %s children?' %len(current))
+- warning.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
+- if warning.run() != gtk.RESPONSE_ACCEPT:
+- warning.destroy()
+- return
+- warning.destroy()
+-
+- parent = model[row].parent
+- if parent is not None:
+- parent = parent[0].node
+- else:
+- parent = menu.node
+- parent.remove(current)
+- model.remove(row)
+-
+- write_menu()
+-
+- def on_close_clicked(self, widget):
+-
+- write_menu()
+- gtk.main_quit()
+-
+- def on_drag_data_get(self, treeview, context, selection, target_id,
+- etime):
+- treeselection = treeview.get_selection()
+- model, iter = treeselection.get_selected()
+- data = model.get_string_from_iter(iter)
+- selection.set(selection.target, 8, data)
+-
+- def on_drag_data_received(self, treeview, context, x, y, selection,
+- info, etime):
+- model = treeview.get_model()
+- data = selection.data
+-
+- drop_info = treeview.get_dest_row_at_pos(x, y)
+- if selection.type == 'deskmenu-element':
+- source = model[data][0]
+- if drop_info:
+- path, position = drop_info
+- siter = model.get_iter(data)
+- diter = model.get_iter(path)
+-
+- if model.get_path(model.get_iter_from_string(data)) == path:
+- return
+-
+- dest = model[path][0]
+- if context.action == gtk.gdk.ACTION_MOVE:
+- source.node.getparent().remove(source.node)
+-
+- if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+- gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
+- dest.node.append(source.node)
+- fiter = model.append(diter, row=(source,))
+- else:
+- i = dest.node.getparent().index(dest.node)
+- if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+- gtk.TREE_VIEW_DROP_BEFORE):
+- dest.node.getparent().insert(i, source.node)
+- fiter = model.insert_before(None, diter, row=(source,))
+- else:
+- dest.node.getparent().insert(i+1, source.node)
+- fiter = model.insert_after(None, diter, row=(source,))
+-
+- if model.iter_has_child(siter):
+- citer = model.iter_children(siter)
+- while citer is not None:
+- model.append(fiter, row=(model[citer][0],))
+- citer = model.iter_next(citer)
+- if context.action == gtk.gdk.ACTION_MOVE:
+- context.finish(True, True, etime)
+-
+- elif selection.type == 'text/uri-list':
+- if drop_info:
+- path, position = drop_info
+- uri = selection.data.replace('file:///', '/').strip()
+- entry = ConfigParser.ConfigParser()
+- entry.read(uri)
+- launcher = Launcher()
+- launcher.name = etree.SubElement(launcher.node, 'name')
+- launcher.icon = etree.SubElement(launcher.node, 'icon')
+- launcher.command = etree.SubElement(launcher.node, 'command')
+- try:
+- launcher.name.text = entry.get('Desktop Entry', 'Name')
+- launcher.icon.text = entry.get('Desktop Entry', 'Icon').split('.')[0]
+- launcher.command.text = entry.get('Desktop Entry', 'Exec').split('%')[0]
+- except ConfigParser.Error:
+- return
+- dest = model[path][0]
+- diter = model.get_iter(path)
+- if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+- gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
+- dest.node.append(launcher.node)
+- fiter = model.append(diter, row=(launcher,))
+- else:
+- i = dest.node.getparent().index(dest.node)
+- if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
+- gtk.TREE_VIEW_DROP_BEFORE):
+- dest.node.getparent().insert(i, launcher.node)
+- fiter = model.insert_before(None, diter, row=(launcher,))
+- else:
+- dest.node.getparent().insert(i+1, launcher.node)
+- fiter = model.insert_after(None, diter, row=(launcher,))
+- if context.action == gtk.gdk.ACTION_MOVE:
+- context.finish(True, True, etime)
++ def __init__(self):
++ gtk.Window.__init__(self)
++
++ self.props.title = 'Compiz Deskmenu Editor'
++ self.props.icon_name = 'gtk-edit'
++ self.props.border_width = 12
++ self.set_size_request(400, 400)
++ self.model = gtk.TreeStore(object)
++ self.add_menu(menu)
++
++ vbox = gtk.VBox(spacing=12)
++
++ scrolled = gtk.ScrolledWindow()
++ scrolled.props.hscrollbar_policy = gtk.POLICY_NEVER
++ scrolled.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
++ treeview = gtk.TreeView(self.model)
++ treeview.set_reorderable(True)
++ cell = gtk.CellRendererText()
++ elements = gtk.TreeViewColumn('Item', cell)
++ elements.set_cell_data_func(cell, self.get_type)
++ treeview.append_column(elements)
++
++ name = gtk.TreeViewColumn('Name')
++
++ cell = gtk.CellRendererPixbuf()
++ name.pack_start(cell, False)
++ name.set_cell_data_func(cell, self.get_icon)
++
++ cell = gtk.CellRendererText()
++ name.pack_start(cell)
++ name.set_cell_data_func(cell, self.get_name)
++
++ treeview.append_column(name)
++ scrolled.add(treeview)
++ vbox.pack_start(scrolled, True, True)
++ targets = [
++ ('deskmenu-element', gtk.TARGET_SAME_WIDGET, 0),
++ ('text/uri-list', 0, 1),
++ ]
++ treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, targets, gtk.gdk.ACTION_DEFAULT|gtk.gdk.ACTION_MOVE)
++ treeview.enable_model_drag_dest(targets, gtk.gdk.ACTION_MOVE)
++
++ treeview.connect('drag-data-get', self.on_drag_data_get)
++ treeview.connect('drag-data-received', self.on_drag_data_received)
++
++ treeview.connect('row-activated', self.on_row_activated)
++
++ treeview.connect('button-press-event', self.on_treeview_button_press_event)
++ treeview.expand_all()
++
++ self.selection = treeview.get_selection()
++ self.selection.connect('changed', self.on_selection_changed)
++
++ buttonbox = gtk.HButtonBox()
++ vbox.pack_end(buttonbox, False, False)
++
++ new = gtk.Button(stock=gtk.STOCK_NEW)
++ new.connect('clicked', self.on_new_clicked)
++ buttonbox.pack_start(new)
++ self.edit = gtk.Button(stock=gtk.STOCK_EDIT)
++ self.edit.connect('clicked', self.on_edit_clicked)
++ buttonbox.pack_start(self.edit)
++ self.delete = gtk.Button(stock=gtk.STOCK_DELETE)
++ self.delete.connect('clicked', self.on_delete_clicked)
++ buttonbox.pack_start(self.delete)
++ close = gtk.Button(stock=gtk.STOCK_CLOSE)
++ close.connect('clicked', self.on_close_clicked)
++ buttonbox.pack_end(close)
++
++ self.add(vbox)
++
++ self.popup = gtk.Menu()
++ self.edit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_EDIT)
++ self.edit_menu.connect('activate', self.on_edit_clicked)
++ self.popup.append(self.edit_menu)
++ self.delete_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_DELETE)
++ self.delete_menu.connect('activate', self.on_delete_clicked)
++ self.popup.append(self.delete_menu)
++ self.popup.show_all()
++
++ self.connect('destroy', self.on_close_clicked)
++
++ self.show_all()
++
++ def add_menu(self, m, parent=None):
++ for item in m.children:
++ iter = self.model.append(parent, [item])
++ if item.node.tag == 'menu':
++ self.add_menu(item, iter)
++
++ def get_name(self, column, cell, model, iter):
++ name = model.get_value(iter, 0).get_name()
++ if name is None:
++ name = ''
++ cell.set_property('text', name)
++
++ def get_type(self, column, cell, model, iter):
++ typ = model.get_value(iter, 0).get_type()
++ if typ is None:
++ typ = ''
++ cell.set_property('text', typ)
++
++ def get_icon(self, column, cell, model, iter):
++ icon = model.get_value(iter, 0).get_icon()
++ icon_mode = model.get_value(iter, 0).get_icon_mode()
++ if icon is not None:
++ if icon_mode is not None:
++ w = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU)
++ cell.set_property('pixbuf', gtk.gdk.pixbuf_new_from_file_at_size(os.path.expanduser(icon), w[0], w[0]))
++ cell.set_property('icon-name', None) #possibly reduntant safety measure
++ else:
++ cell.set_property('icon-name', icon)
++ cell.set_property('pixbuf', None) #possibly reduntant safety measure
++ else:
++ cell.set_property('icon-name', None)
++ cell.set_property('pixbuf', None)
++
++ def on_new_clicked(self, widget):
++
++ NewItemDialog(*self.selection.get_selected())
++
++ def on_edit_clicked(self, widget):
++
++ EditItemDialog(*self.selection.get_selected())
++
++ def on_delete_clicked(self, widget):
++
++ model, row = self.selection.get_selected()
++
++ parent = None
++ if row:
++ current = model[row][0].node
++
++ if current.tag == 'menu' and len(current):
++ warning = gtk.MessageDialog(self, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_NONE, 'Delete menu element with %s children?' %len(current))
++ warning.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_DELETE, gtk.RESPONSE_ACCEPT)
++ if warning.run() != gtk.RESPONSE_ACCEPT:
++ warning.destroy()
++ return
++ warning.destroy()
++
++ parent = model[row].parent
++ if parent is not None:
++ parent = parent[0].node
++ else:
++ parent = menu.node
++ parent.remove(current)
++ model.remove(row)
++
++ write_menu()
++
++ def on_close_clicked(self, widget):
++
++ write_menu()
++ gtk.main_quit()
++
++ def on_drag_data_get(self, treeview, context, selection, target_id,
++ etime):
++ treeselection = treeview.get_selection()
++ model, iter = treeselection.get_selected()
++ data = model.get_string_from_iter(iter)
++ selection.set(selection.target, 8, data)
++
++ def on_drag_data_received(self, treeview, context, x, y, selection,
++ info, etime):
++ model = treeview.get_model()
++ data = selection.data
++
++ drop_info = treeview.get_dest_row_at_pos(x, y)
++ if selection.type == 'deskmenu-element':
++ source = model[data][0]
++ if drop_info:
++ path, position = drop_info
++ siter = model.get_iter(data)
++ diter = model.get_iter(path)
++
++ if model.get_path(model.get_iter_from_string(data)) == path:
++ return
++
++ dest = model[path][0]
++ if context.action == gtk.gdk.ACTION_MOVE:
++ source.node.getparent().remove(source.node)
++
++ if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
++ gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
++ dest.node.append(source.node)
++ fiter = model.append(diter, row=(source,))
++ else:
++ i = dest.node.getparent().index(dest.node)
++ if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
++ gtk.TREE_VIEW_DROP_BEFORE):
++ dest.node.getparent().insert(i, source.node)
++ fiter = model.insert_before(None, diter, row=(source,))
++ else:
++ dest.node.getparent().insert(i+1, source.node)
++ fiter = model.insert_after(None, diter, row=(source,))
++
++ if model.iter_has_child(siter):
++ citer = model.iter_children(siter)
++ while citer is not None:
++ model.append(fiter, row=(model[citer][0],))
++ citer = model.iter_next(citer)
++ if context.action == gtk.gdk.ACTION_MOVE:
++ context.finish(True, True, etime)
++
++ elif selection.type == 'text/uri-list':
++ if drop_info:
++ path, position = drop_info
++ uri = selection.data.replace('file:///', '/').strip()
++ entry = ConfigParser.ConfigParser()
++ entry.read(uri)
++ launcher = Launcher()
++ launcher.name = etree.SubElement(launcher.node, 'name')
++ launcher.icon = etree.SubElement(launcher.node, 'icon')
++ launcher.command = etree.SubElement(launcher.node, 'command')
++ try:
++ launcher.name.text = entry.get('Desktop Entry', 'Name')
++ if re.search("/", entry.get('Desktop Entry', 'Icon')):
++ launcher.icon.attrib['mode1'] = 'file'
++ launcher.icon.text = entry.get('Desktop Entry', 'Icon')
++ launcher.command.text = entry.get('Desktop Entry', 'Exec').split('%')[0]
++ except ConfigParser.Error:
++ return
++ dest = model[path][0]
++ diter = model.get_iter(path)
++ if dest.node.tag == 'menu' and position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
++ gtk.TREE_VIEW_DROP_INTO_OR_AFTER):
++ dest.node.append(launcher.node)
++ fiter = model.append(diter, row=(launcher,))
++ else:
++ i = dest.node.getparent().index(dest.node)
++ if position in (gtk.TREE_VIEW_DROP_INTO_OR_BEFORE,
++ gtk.TREE_VIEW_DROP_BEFORE):
++ dest.node.getparent().insert(i, launcher.node)
++ fiter = model.insert_before(None, diter, row=(launcher,))
++ else:
++ dest.node.getparent().insert(i+1, launcher.node)
++ fiter = model.insert_after(None, diter, row=(launcher,))
++ if context.action == gtk.gdk.ACTION_MOVE:
++ context.finish(True, True, etime)
+
+- write_menu()
++ write_menu()
+
+- return
++ return
+
+- def on_selection_changed(self, selection):
++ def on_selection_changed(self, selection):
+
+- model, row = selection.get_selected()
++ model, row = selection.get_selected()
+
+- sensitive = row and model.get_value(row, 0).editable
++ sensitive = row and model.get_value(row, 0).editable
+
+- self.edit.props.sensitive = sensitive
+- self.edit_menu.props.sensitive = sensitive
+- self.delete.props.sensitive = row
+- self.delete_menu.props.sensitive = row
+-
+- def on_row_activated(self, treeview, path, view_column):
+-
+- model = treeview.get_model()
+- EditItemDialog(model, model.get_iter(path))
+-
+- def on_treeview_button_press_event(self, treeview, event):
+- if event.button == 3:
+- pthinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
+- if pthinfo is not None:
+- path, col, cellx, celly = pthinfo
+- treeview.grab_focus()
+- treeview.set_cursor(path, col, 0)
+- self.popup.popup(None, None, None, event.button, event.time)
+- return 1
++ self.edit.props.sensitive = sensitive
++ self.edit_menu.props.sensitive = sensitive
++ self.delete.props.sensitive = row
++ self.delete_menu.props.sensitive = row
++
++ def on_row_activated(self, treeview, path, view_column):
++
++ model = treeview.get_model()
++ EditItemDialog(model, model.get_iter(path))
++
++ def on_treeview_button_press_event(self, treeview, event):
++ if event.button == 3:
++ pthinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
++ if pthinfo is not None:
++ path, col, cellx, celly = pthinfo
++ treeview.grab_focus()
++ treeview.set_cursor(path, col, 0)
++ self.popup.popup(None, None, None, event.button, event.time)
++ return 1
+
+
+ class NewItemDialog(gtk.Dialog):
+
+- elementlist = ['Launcher', 'Menu', 'Separator', 'Windows List',
+- 'Viewports List', 'Reload']
++ elementlist = ['Launcher', 'Menu', 'Separator', 'Windows List',
++ 'Viewports List', 'Pipeitem']
+
+- def __init__(self, model, row):
+- gtk.Dialog.__init__(self, 'New Item', None, 0, None)
++ def __init__(self, model, row):
++ gtk.Dialog.__init__(self, 'New Item', None, 0, None)
+
+- self.set_size_request(250, 250)
++ self.set_size_request(250, 250)
+
+- self.props.border_width = 6
+- self.vbox.props.spacing = 6
+- self.set_has_separator(False)
+- self.treeview = self.make_treeview()
+-
+- scroll = gtk.ScrolledWindow()
+- scroll.add(self.treeview)
+- scroll.props.hscrollbar_policy = gtk.POLICY_NEVER
+- scroll.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
+- self.vbox.pack_start(scroll, True, True)
+-
+- self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+- gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT)
+-
+- self.action_area.props.border_width = 0
+-
+- self.show_all()
+-
+- element = None
+-
+- if self.run() == gtk.RESPONSE_ACCEPT:
+- m, r = self.treeview.get_selection().get_selected()
+- if r:
+- elementname = m[r][0]
+- else:
+- self.destroy()
+- return
+- parent = sibling = None
+- if row:
+- current = model[row][0]
+- if current.node.tag == 'menu':
+- parent = row
+- else:
+- parent = model[row].parent
+- if parent is not None:
+- parent = parent.iter
+- sibling = row
+- if parent:
+- parentelement = model[parent][0]
+- else:
+- parentelement = menu
+-
+- element = elementsbyname[elementname]()
+- if sibling:
+- position = parentelement.node.index(current.node) + 1
+- parentelement.node.insert(position, element.node)
+- model.insert_after(parent, sibling, row=(element,))
+- else:
+- model.append(parent, row=(element,))
+- parentelement.node.append(element.node)
+-
+- self.destroy()
+- write_menu()
+-
+- if element and element.editable:
+- EditItemDialog(element=element)
+-
+- def make_treeview(self):
+- model = gtk.ListStore(str)
+- for el in self.elementlist:
+- model.append([el])
+-
+- treeview = gtk.TreeView(model)
+- column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
+- treeview.set_headers_visible(False)
+- treeview.append_column(column)
++ self.props.border_width = 6
++ self.vbox.props.spacing = 6
++ self.set_has_separator(False)
++ self.treeview = self.make_treeview()
++
++ scroll = gtk.ScrolledWindow()
++ scroll.add(self.treeview)
++ scroll.props.hscrollbar_policy = gtk.POLICY_NEVER
++ scroll.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
++ self.vbox.pack_start(scroll, True, True)
++
++ self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
++ gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT)
++
++ self.action_area.props.border_width = 0
++
++ self.show_all()
++
++ element = None
++
++ if self.run() == gtk.RESPONSE_ACCEPT:
++ m, r = self.treeview.get_selection().get_selected()
++ if r:
++ elementname = m[r][0]
++ else:
++ self.destroy()
++ return
++ parent = sibling = None
++ if row:
++ current = model[row][0]
++ if current.node.tag == 'menu':
++ parent = row
++ else:
++ parent = model[row].parent
++ if parent is not None:
++ parent = parent.iter
++ sibling = row
++ if parent:
++ parentelement = model[parent][0]
++ else:
++ parentelement = menu
++
++ element = elementsbyname[elementname]()
++ if sibling:
++ position = parentelement.node.index(current.node) + 1
++ parentelement.node.insert(position, element.node)
++ model.insert_after(parent, sibling, row=(element,))
++ else:
++ model.append(parent, row=(element,))
++ parentelement.node.append(element.node)
++
++ self.destroy()
++ write_menu()
++
++ if element and element.editable:
++ EditItemDialog(element=element)
++
++ def make_treeview(self):
++ model = gtk.ListStore(str)
++ for el in self.elementlist:
++ model.append([el])
++
++ treeview = gtk.TreeView(model)
++ column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
++ treeview.set_headers_visible(False)
++ treeview.append_column(column)
+
+- treeview.connect('row-activated', self.on_row_activated)
+- return treeview
++ treeview.connect('row-activated', self.on_row_activated)
++ return treeview
+
+- def on_row_activated(self, treeview, path, view_column):
+- self.response(gtk.RESPONSE_ACCEPT)
++ def on_row_activated(self, treeview, path, view_column):
++ self.response(gtk.RESPONSE_ACCEPT)
+
+
+ class EditItemDialog(gtk.Dialog):
+
+- def __init__(self, model=None, row=None, element=None):
+- gtk.Dialog.__init__(self, 'Edit Item', None, 0, None)
+-
+- self.set_size_request(300, -1)
++ def __init__(self, model=None, row=None, element=None):
++ gtk.Dialog.__init__(self, 'Edit Item', None, 0, None)
+
+- self.props.border_width = 6
+- self.vbox.props.spacing = 6
+- self.set_has_separator(False)
++ self.set_size_request(300, -1)
+
+- if element is None:
+- if not row:
+- return
+- element = model.get_value(row, 0)
++ self.props.border_width = 6
++ self.vbox.props.spacing = 6
++ self.set_has_separator(False)
+
+- if not element.editable:
+- return
++ if element is None:
++ if not row:
++ return
++ element = model.get_value(row, 0)
+
+- for widget in element.get_options():
+- self.vbox.pack_start(widget, False, False)
++ if not element.editable:
++ return
+
+- self.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
++ for widget in element.get_options():
++ self.vbox.pack_start(widget, False, False)
+
+- self.show_all()
+- self.run()
+- self.destroy()
+- write_menu()
++ self.add_buttons(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+
++ self.show_all()
++ self.run()
++ self.destroy()
++ write_menu()
+
+
+ class Item(object):
+-
+- def __init__(self, node=None, parent=None, type=None):
++
++ def __init__(self, node=None, parent=None, type=None):
+
+- self.editable = False
++ self.editable = False
+
+- if node is None:
+- self.node = etree.Element('item')
+- if type is not None:
+- self.node.attrib['type'] = type
+-
+- else:
+- self.node = node
++ if node is None:
++ self.node = etree.Element('item')
++ if type is not None:
++ self.node.attrib['type'] = type
++
++ else:
++ self.node = node
++
++ def get_name(self):
++ return None
+
+- def get_name(self):
+- return None
++ def get_type(self):
++ return 'Item'
+
+- def get_type(self):
+- return 'Item'
++ def get_icon(self):
++ return None
+
+- def get_icon(self):
+- return None
++ def get_icon_mode(self):
++ return None
+
+ class Launcher(Item):
+-
+- def __init__(self, node=None):
++
++ def __init__(self, node=None):
+
+- Item.__init__(self, node)
+-
+- if node is None:
+- self.node = etree.Element('item', type='launcher')
+-
+- self.editable = True
+-
+- def get_name(self):
+-
+- subnode = self.node.find('name')
+- if subnode is not None:
+- name = subnode.text
+- if subnode.attrib.get('mode') == 'exec':
+- name = 'exec: %s' %name
+- return name
+- else:
+- return None
+-
+- def get_type(self):
+- return 'Launcher'
+-
+- def get_icon(self):
+- iconnode = self.node.find('icon')
+- if iconnode is not None:
+- return iconnode.text
+- else:
+- return None
+-
+- def get_options(self):
+-
+- retlist = []
+- sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+-
+- label = gtk.Label()
+- label.set_alignment(0, 0.5)
+- sgroup.add_widget(label)
+- label.set_markup('<b>Name:</b>')
+- widget = gtk.Entry()
+-
+- namenode = self.node.find('name')
+- if namenode is not None:
+- name = namenode.text
+- else:
+- name = ''
+- widget.props.text = name
+- widget.connect('changed', self.on_subnode_changed, 'name')
+-
+- hbox = gtk.HBox()
+- hbox.pack_start(label)
+- hbox.pack_start(widget, True, True)
+- retlist.append(hbox)
+-
+- label = gtk.Label()
+- label.set_alignment(0, 0.5)
+- sgroup.add_widget(label)
+- label.set_markup('<b>Name mode:</b>')
+- widget = gtk.combo_box_new_text()
+- widget.append_text('Normal')
+- widget.append_text('Execute')
+- widget.props.active = namenode is not None and namenode.attrib.get('mode') == 'exec'
+- widget.connect('changed', self.on_name_mode_changed)
+-
+- hbox = gtk.HBox()
+- hbox.pack_start(label)
+- hbox.pack_start(widget, True, True)
+- retlist.append(hbox)
+-
+- label = gtk.Label()
+- label.set_alignment(0, 0.5)
+- sgroup.add_widget(label)
+- label.set_markup('<b>Icon:</b>')
+- widget = gtk.Entry()
+- iconnode = self.node.find('icon')
+- if iconnode is not None:
+- icon = iconnode.text
+- else:
+- icon = ''
+- widget.props.text = icon
+- widget.connect('changed', self.on_subnode_changed, 'icon')
+-
+- hbox = gtk.HBox()
+- hbox.pack_start(label)
+- hbox.pack_start(widget, True, True)
+- retlist.append(hbox)
+-
+- label = gtk.Label()
+- label.set_alignment(0, 0.5)
+- sgroup.add_widget(label)
+- label.set_markup('<b>Command:</b>')
+- widget = gtk.Entry()
+- commandnode = self.node.find('command')
+- if commandnode is not None:
+- command = commandnode.text
+- else:
+- command = ''
+- widget.props.text = command
+- widget.connect('changed', self.on_subnode_changed, 'command')
+-
+- hbox = gtk.HBox()
+- hbox.pack_start(label)
+- hbox.pack_start(widget, True, True)
+- retlist.append(hbox)
+-
+- return retlist
+-
+- def on_subnode_changed(self, widget, tag):
+- text = widget.props.text
+- subnode = self.node.find(tag)
+- if text:
+- if subnode is None:
+- subnode = etree.SubElement(self.node, tag)
+- subnode.text = text
+- else:
+- if subnode is not None:
+- self.node.remove(subnode)
+-
+- def on_name_mode_changed(self, widget):
+- namenode = self.node.find('name')
+- if widget.props.active:
+- if namenode is None:
+- namenode = etree.SubElement(self.node, 'name')
+- namenode.attrib['mode'] = 'exec'
+- elif 'mode' in self.name.attrib:
+- namenode.attrib['mode'] = 'normal'
++ Item.__init__(self, node)
+
++ if node is None:
++ self.node = etree.Element('item', type='launcher')
++
++ self.editable = True
++
++ def get_name(self):
++
++ subnode = self.node.find('name')
++ if subnode is not None:
++ name = subnode.text
++ if subnode.attrib.get('mode') == 'exec':
++ name = 'exec: %s' %name
++ return name
++ else:
++ return None
++
++ def get_type(self):
++ return 'Launcher'
++
++ def get_icon(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ return iconnode.text
++ else:
++ return None
++
++ def get_icon_mode(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ if iconnode.attrib.get('mode1') == 'file':
++ return iconnode.attrib.get('mode1')
++ else:
++ return None
++
++ def get_options(self):
++
++ retlist = []
++ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Name:</b>')
++ widget = gtk.Entry()
++
++ namenode = self.node.find('name')
++ if namenode is not None:
++ name = namenode.text
++ else:
++ name = ''
++ widget.props.text = name
++ widget.connect('changed', self.on_subnode_changed, 'name')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Name mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('Execute')
++ widget.props.active = namenode is not None and namenode.attrib.get('mode') == 'exec'
++ widget.connect('changed', self.on_name_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon:</b>')
++ widget = gtk.Entry()
++
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ icon = iconnode.text
++ else:
++ icon = ''
++ widget.props.text = icon
++ widget.connect('changed', self.on_subnode_changed, 'icon')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('File Path')
++ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
++ widget.connect('changed', self.on_icon_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Command:</b>')
++ widget = gtk.Entry()
++ commandnode = self.node.find('command')
++ if commandnode is not None:
++ command = commandnode.text
++ else:
++ command = ''
++ widget.props.text = command
++ widget.connect('changed', self.on_subnode_changed, 'command')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ return retlist
++
++ def on_subnode_changed(self, widget, tag):
++ text = widget.props.text
++ subnode = self.node.find(tag)
++ if text:
++ if subnode is None:
++ subnode = etree.SubElement(self.node, tag)
++ subnode.text = text
++ else:
++ if subnode is not None:
++ self.node.remove(subnode)
++
++ def on_name_mode_changed(self, widget):
++ namenode = self.node.find('name')
++ if widget.props.active:
++ if namenode is None:
++ namenode = etree.SubElement(self.node, 'name')
++ namenode.attrib['mode'] = 'exec'
++ elif 'mode' in namenode.attrib:
++ del namenode.attrib['mode']
++
++ def on_icon_mode_changed(self, widget):
++ iconnode = self.node.find('icon')
++ if widget.props.active:
++ if iconnode is None:
++ iconnode = etree.SubElement(self.node, 'icon')
++ iconnode.attrib['mode1'] = 'file'
++ elif 'mode1' in iconnode.attrib:
++ del iconnode.attrib['mode1']
+
+ class Windowlist(Item):
+-
+- def __init__(self, node=None):
+- Item.__init__(self, node, type='windowlist')
+-
+- def get_type(self):
+- return 'Windows list'
++
++ def __init__(self, node=None):
++ Item.__init__(self, node, type='windowlist')
++ self.editable = True
++
++ def get_icon(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ return iconnode.text
++ else:
++ return None
++
++ def get_icon_mode(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ if iconnode.attrib.get('mode1') == 'file':
++ return iconnode.attrib.get('mode1')
++ else:
++ return None
++
++ def get_options(self):
++ retlist = []
++ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon:</b>')
++ widget = gtk.Entry()
++
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ icon = iconnode.text
++ else:
++ icon = ''
++ widget.props.text = icon
++ widget.connect('changed', self.on_subnode_changed, 'icon')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('File Path')
++ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
++ widget.connect('changed', self.on_icon_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ return retlist
++
++ def on_subnode_changed(self, widget, tag):
++ text = widget.props.text
++ subnode = self.node.find(tag)
++ if text:
++ if subnode is None:
++ subnode = etree.SubElement(self.node, tag)
++ subnode.text = text
++ else:
++ if subnode is not None:
++ self.node.remove(subnode)
++
++ def on_icon_mode_changed(self, widget):
++ iconnode = self.node.find('icon')
++ if widget.props.active:
++ if iconnode is None:
++ iconnode = etree.SubElement(self.node, 'icon')
++ iconnode.attrib['mode1'] = 'file'
++ elif 'mode1' in iconnode.attrib:
++ del iconnode.attrib['mode1']
+
++ def get_type(self):
++ return 'Windows list'
+
+ class Viewportlist(Item):
+-
+- def __init__(self, node=None):
+- Item.__init__(self, node, type='viewportlist')
+- self.editable = True
+- self.wrap = self.node.find('wrap')
+-
+- def get_type(self):
+- return 'Viewports list'
+-
+- def get_options(self):
+- retlist = []
+- widget = gtk.CheckButton("Wrap Viewports")
+- widget.props.active = self.get_wrap()
+- widget.connect('toggled', self.on_wrap_changed)
+- retlist.append(widget)
+-
+- return retlist
+-
+- def get_wrap(self):
+- if self.wrap is not None:
+- return self.wrap.text == 'true'
+- return False
+-
+- def on_wrap_changed(self, widget):
+- if self.wrap is None:
+- self.wrap = etree.SubElement(self.node, 'wrap')
+- if widget.props.active:
+- text = 'true'
+- else:
+- text = 'false'
+- self.wrap.text = text
+-
+-
+-class Reload(Item):
+-
+- def __init__(self, node=None):
+- Item.__init__(self, node, type='reload')
+-
+- def get_type(self):
+- return 'Reload'
+-
++
++ def __init__(self, node=None): #change to be an attribute of viewportlist
++ Item.__init__(self, node, type='viewportlist')
++ self.editable = True
++ self.wrap = self.node.find('wrap')
++
++ def get_type(self):
++ return 'Viewports list'
++
++ def get_icon(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ return iconnode.text
++ else:
++ return None
++
++ def get_icon_mode(self):
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ if iconnode.attrib.get('mode1') == 'file':
++ return iconnode.attrib.get('mode1')
++ else:
++ return None
++
++
++ def get_options(self):
++ retlist = []
++ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
++ widget = gtk.CheckButton("Wrap Viewports")
++ widget.props.active = self.get_wrap()
++ widget.connect('toggled', self.on_wrap_changed)
++ retlist.append(widget)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon:</b>')
++ widget = gtk.Entry()
++
++ iconnode = self.node.find('icon')
++ if iconnode is not None:
++ icon = iconnode.text
++ else:
++ icon = ''
++ widget.props.text = icon
++ widget.connect('changed', self.on_subnode_changed, 'icon')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('File Path')
++ widget.props.active = iconnode is not None and iconnode.attrib.get('mode1') == 'file'
++ widget.connect('changed', self.on_icon_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Viewport Icon:</b>')
++ widget = gtk.Entry()
++
++ vpiconnode = self.node.find('vpicon')
++ if vpiconnode is not None:
++ icon = vpiconnode.text
++ else:
++ icon = ''
++ widget.props.text = icon
++ widget.connect('changed', self.on_subnode_changed, 'vpicon')
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Viewport Icon mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('File Path')
++ widget.props.active = vpiconnode is not None and vpiconnode.attrib.get('mode1') == 'file'
++ widget.connect('changed', self.on_vpicon_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ return retlist
++
++ def get_wrap(self):
++ if self.wrap is not None:
++ return self.wrap.text == 'true'
++ return False
++
++ def on_subnode_changed(self, widget, tag):
++ text = widget.props.text
++ subnode = self.node.find(tag)
++ if text:
++ if subnode is None:
++ subnode = etree.SubElement(self.node, tag)
++ subnode.text = text
++ else:
++ if subnode is not None:
++ self.node.remove(subnode)
++
++ def on_wrap_changed(self, widget):
++ if self.wrap is None:
++ self.wrap = etree.SubElement(self.node, 'wrap')
++ if widget.props.active:
++ text = 'true'
++ else:
++ text = 'false'
++ self.wrap.text = text
++
++ def on_icon_mode_changed(self, widget):
++ iconnode = self.node.find('icon')
++ if widget.props.active:
++ if iconnode is None:
++ iconnode = etree.SubElement(self.node, 'icon')
++ iconnode.attrib['mode1'] = 'file'
++ elif 'mode1' in iconnode.attrib:
++ del iconnode.attrib['mode1']
++
++ def on_vpicon_mode_changed(self, widget):
++ iconnode = self.node.find('vpicon')
++ if widget.props.active:
++ if iconnode is None:
++ iconnode = etree.SubElement(self.node, 'vpicon')
++ iconnode.attrib['mode1'] = 'file'
++ elif 'mode1' in iconnode.attrib:
++ del iconnode.attrib['mode1']
+
+ class Separator(object):
+-
+- def __init__(self, node=None, parent=None):
+- self.node = node
+- self.editable = False
+- if self.node is None:
+- self.node = etree.Element('separator')
+-
+- def get_name(self):
+- return '---'
+-
+- def get_type(self):
+- return 'Separator'
++
++ def __init__(self, node=None, parent=None):
++ self.node = node
++ self.editable = False
++ if self.node is None:
++ self.node = etree.Element('separator')
++
++ def get_name(self):
++ return '---'
++
++ def get_type(self):
++ return 'Separator'
++
++ def get_icon(self):
++ return None
++
++ def get_icon_mode(self):
++ return None
++
++class Pipe(object):
++
++ def __init__(self, node=None):
++ self.node = node
++ self.children = []
++ self.editable = True
++
++ if node is None:
++ self.node = etree.Element('pipe', command='')
++
++ for child in self.node.getchildren():
++ try:
++ self.children.append(self.make_child(child))
++ except KeyError:
++ pass
++
++
++ def make_child(self, child):
++ return elements[child.tag](child)
++
++ def get_name(self):
++ name = self.node.attrib.get('command', '')
++ return name
++
++ def get_icon(self):
++ return None
++
++ def get_icon_mode(self):
++ return None
++
++ def get_type(self):
++ return 'Pipeitem'
++
++ def get_options(self):
++
++ retlist = []
++ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ label.set_markup('<b>Command:</b>')
++ widget = gtk.Entry()
++ widget.props.text = self.node.attrib.get('command', '')
++ widget.connect('changed', self.on_command_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
+
+- def get_icon(self):
+- return None
++ return retlist
+
++ def on_command_changed(self, widget):
++ self.node.attrib['command'] = widget.props.text
+
+ class Menu(object):
+
+- def __init__(self, node=None):
+- self.node = node
+- self.children = []
+- self.editable = True
+-
+- if node is None:
+- self.node = etree.Element('menu', name='')
+-
+- for child in self.node.getchildren():
+- try:
+- self.children.append(self.make_child(child))
+- except KeyError:
+- pass
+-
+-
+- def make_child(self, child):
+- return elements[child.tag](child)
+-
+- def get_name(self):
+- return self.node.attrib.get('name', '')
+-
+- def get_type(self):
+- return 'Menu'
+-
+- def get_icon(self):
+- return None
+-
+- def get_options(self):
+-
+- retlist = []
+- label = gtk.Label()
+- label.set_alignment(0, 0.5)
+- label.set_markup('<b>Name:</b>')
+- widget = gtk.Entry()
+- widget.props.text = self.node.attrib.get('name', '')
+- widget.connect('changed', self.on_name_changed)
+- hbox = gtk.HBox()
+- hbox.pack_start(label)
+- hbox.pack_start(widget, True, True)
+- retlist.append(hbox)
+-
+- return retlist
+-
+- def on_name_changed(self, widget):
+- self.node.attrib['name'] = widget.props.text
+-
++ def __init__(self, node=None):
++ self.node = node
++ self.children = []
++ self.editable = True
++
++ if node is None:
++ self.node = etree.Element('menu', name='')
++
++ for child in self.node.getchildren():
++ try:
++ self.children.append(self.make_child(child))
++ except KeyError:
++ pass
++
++
++ def make_child(self, child):
++ return elements[child.tag](child)
++
++ def get_name(self):
++ name = self.node.attrib.get('name', '')
++ if self.node.attrib.get('mode') == 'exec':
++ name = 'exec: %s' %name
++ return name
++
++ def get_icon(self):
++ return self.node.attrib.get('icon', '')
++
++ def get_icon_mode(self):
++ if self.node.attrib.get('mode1') == 'file':
++ return self.node.attrib.get('mode1')
++ else:
++ return None
++
++ def get_type(self):
++ return 'Menu'
++
++ def get_options(self):
++
++ retlist = []
++ sgroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ label.set_markup('<b>Name:</b>')
++ widget = gtk.Entry()
++ widget.props.text = self.node.attrib.get('name', '')
++ widget.connect('changed', self.on_name_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Name mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('Execute')
++ widget.props.active = self.node.attrib.get('name') is not None and self.node.attrib.get('mode') == 'exec'
++ widget.connect('changed', self.on_name_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ #TODO:Make this actually edit icon
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon:</b>')
++ widget = gtk.Entry()
++ if self.node.attrib.get('icon') is not None:
++ widget.props.text = self.node.attrib.get('icon')
++ else:
++ widget.props.text = ''
++ #widget.props.text = self.node.attrib.get('icon')
++ widget.connect('changed', self.on_icon_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ label = gtk.Label()
++ label.set_alignment(0, 0.5)
++ sgroup.add_widget(label)
++ label.set_markup('<b>Icon mode:</b>')
++ widget = gtk.combo_box_new_text()
++ widget.append_text('Normal')
++ widget.append_text('File Path')
++ widget.props.active = self.node.attrib.get('icon') is not None and self.node.attrib.get('mode1') == 'file'
++ widget.connect('changed', self.on_icon_mode_changed)
++
++ hbox = gtk.HBox()
++ hbox.pack_start(label)
++ hbox.pack_start(widget, True, True)
++ retlist.append(hbox)
++
++ return retlist
++
++ def on_name_changed(self, widget):
++ self.node.attrib['name'] = widget.props.text
++ def on_name_mode_changed(self, widget): #EDIT THIS
++ if widget.props.active:
++ self.node.attrib['mode'] = 'exec'
++ elif 'mode' in self.node.attrib:
++ del self.node.attrib['mode']
++ def on_icon_changed(self, widget):
++ if widget.props.text != '':
++ self.node.attrib['icon'] = widget.props.text
++ else:
++ del self.node.attrib['icon']
++ def on_icon_mode_changed(self, widget): #EDIT THIS
++ if widget.props.active:
++ self.node.attrib['mode1'] = 'file'
++ elif 'mode1' in self.node.attrib:
++ del self.node.attrib['mode1']
+
+ itemtypes = {
+- 'launcher': Launcher,
+- 'windowlist': Windowlist,
+- 'viewportlist': Viewportlist,
+- 'reload': Reload,
++ 'launcher': Launcher,
++ 'windowlist': Windowlist,
++ 'viewportlist': Viewportlist,
+ }
+
+
+ def make_item(node):
+
+- itemtype = node.attrib.get('type')
+- return itemtypes.get(itemtype, Item)(node)
++ itemtype = node.attrib.get('type')
++ return itemtypes.get(itemtype, Item)(node)
+
+
+ def indent(elem, level=0):
+- i = "\n" + level*" "
+- if len(elem):
+- if not elem.text or not elem.text.strip():
+- elem.text = i + " "
+- for e in elem:
+- indent(e, level+1)
+- if not e.tail or not e.tail.strip():
+- e.tail = i + " "
+- if not e.tail or not e.tail.strip():
+- e.tail = i
+- else:
+- if level and (not elem.tail or not elem.tail.strip()):
+- elem.tail = i
++ i = "\n" + level*" "
++ if len(elem):
++ if not elem.text or not elem.text.strip():
++ elem.text = i + " "
++ for e in elem:
++ indent(e, level+1)
++ if not e.tail or not e.tail.strip():
++ e.tail = i + " "
++ if not e.tail or not e.tail.strip():
++ e.tail = i
++ else:
++ if level and (not elem.tail or not elem.tail.strip()):
++ elem.tail = i
+
+ def write_menu():
+
+- indent(menu.node)
+- menufile.write(open(os.path.join(configdir, 'menu.xml'), 'w'))
++ indent(menu.node)
++ menufile.write(open(filename, 'w'))
+
+- if bus is not None:
+- try:
+- bus.get_object('org.compiz_fusion.deskmenu',
+- '/org/compiz_fusion/deskmenu/menu').reload()
+- except dbus.DBusException:
+- return
+-
+-elements = {'menu': Menu, 'item': make_item, 'separator': Separator}
++elements = {'menu': Menu, 'item': make_item, 'separator': Separator, 'pipe': Pipe}
+
+ elementsbyname = {
+- 'Launcher': Launcher,
+- 'Windows List': Windowlist,
+- 'Viewports List': Viewportlist,
+- 'Reload': Reload,
+- 'Separator': Separator,
+- 'Menu': Menu,
++ 'Launcher': Launcher,
++ 'Windows List': Windowlist,
++ 'Viewports List': Viewportlist,
++ 'Separator': Separator,
++ 'Menu': Menu,
++ 'Pipeitem': Pipe
+ }
+
+
+ if __name__ == '__main__':
+-
+- if dbus:
+- try:
+- bus = dbus.SessionBus()
+- except dbus.DBusException:
+- bus = None
+- else:
+- bus = None
+-
+- filename = BaseDirectory.load_first_config('compiz/deskmenu/menu.xml')
+- menufile = etree.parse(filename)
+- root = menufile.getroot()
+- menu = Menu(root)
+- configdir = BaseDirectory.save_config_path('compiz/deskmenu')
+- DeskmenuEditor()
+- gtk.main()
+-
++ if len(sys.argv) == 2 :
++ if open(sys.argv[1]):
++ filename = sys.argv[1]
++ else:
++ filename = BaseDirectory.load_first_config('compiz/deskmenu/menu.xml')
++ menufile = etree.parse(filename)
++ root = menufile.getroot()
++ menu = Menu(root)
++ DeskmenuEditor()
++ gtk.main()
+diff -aur compiz-deskmenu/deskmenu-menu.c compiz-deskmenu3/deskmenu-menu.c
+--- compiz-deskmenu/deskmenu-menu.c 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/deskmenu-menu.c 2010-11-16 15:41:15.000000000 -0800
+@@ -1,658 +1,774 @@
+-/*
+- * compiz-deskmenu is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * compiz-deskmenu is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+- *
+- * Copyright 2008 Christopher Williams <christopherw@verizon.net>
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <dbus/dbus-glib-lowlevel.h>
+-#include <gtk/gtk.h>
+-
+-#define HAVE_WNCK 1
+-
+-#if HAVE_WNCK
+-#include "deskmenu-wnck.h"
+-#endif
+-
+-#include "deskmenu-menu.h"
+-#include "deskmenu-glue.h"
+-
+-
+-G_DEFINE_TYPE(Deskmenu, deskmenu, G_TYPE_OBJECT)
+-
+-GQuark
+-deskmenu_error_quark (void)
+-{
+- static GQuark quark = 0;
+- if (!quark)
+- quark = g_quark_from_static_string ("deskmenu_error");
+- return quark;
+-}
+-
+-static void
+-quit (GtkWidget *widget,
+- gpointer data)
+-{
+- gtk_main_quit ();
+-}
+-
+-static void
+-launcher_activated (GtkWidget *widget,
+- gchar *command)
+-{
+- GError *error = NULL;
+- Deskmenu *deskmenu;
+-
+- deskmenu = g_object_get_data (G_OBJECT (widget), "deskmenu");
+-
+- if (!gdk_spawn_on_screen (gdk_screen_get_default (),
+- g_get_home_dir (),
+- g_strsplit (command, " ", 0),
+- deskmenu->envp, G_SPAWN_SEARCH_PATH,
+- NULL, NULL, NULL, &error))
+- {
+- GtkWidget *message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR,
+- GTK_BUTTONS_CLOSE, "%s", error->message);
+- gtk_dialog_run (GTK_DIALOG (message));
+- gtk_widget_destroy (message);
+- }
+-
+-}
+-
+-static void
+-launcher_name_exec_update (GtkWidget *label)
+-{
+- gchar *exec, *stdout;
+- exec = g_object_get_data (G_OBJECT (label), "exec");
+- if (g_spawn_command_line_sync (exec, &stdout, NULL, NULL, NULL))
+- gtk_label_set_text (GTK_LABEL (label), g_strstrip(stdout));
+- else
+- gtk_label_set_text (GTK_LABEL (label), "execution error");
+- g_free (stdout);
+-}
+-
+-static void
+-deskmenu_construct_item (Deskmenu *deskmenu)
+-{
+- DeskmenuItem *item = deskmenu->current_item;
+- GtkWidget *menu_item;
+- gchar *name, *icon, *command;
+-
+- switch (item->type)
+- {
+- case DESKMENU_ITEM_LAUNCHER:
+- if (item->name_exec)
+- {
+- GtkWidget *label;
+- GHook *hook;
+-
+- name = g_strstrip (item->name->str);
+-
+- menu_item = gtk_image_menu_item_new ();
+- label = gtk_label_new_with_mnemonic (NULL);
+- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+-
+- g_object_set_data (G_OBJECT (label), "exec", g_strdup (name));
+- gtk_container_add (GTK_CONTAINER (menu_item), label);
+- hook = g_hook_alloc (deskmenu->show_hooks);
+-
+- hook->data = (gpointer) label;
+- hook->func = (GHookFunc *) launcher_name_exec_update;
+- g_hook_append (deskmenu->show_hooks, hook);
+- }
+- else
+- {
+- if (item->name)
+- name = g_strstrip (item->name->str);
+- else
+- name = "";
+-
+- menu_item = gtk_image_menu_item_new_with_mnemonic (name);
+-
+- }
+-
+- if (item->icon)
+- {
+- icon = g_strstrip (item->icon->str);
+- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+- gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU));
+- }
+-
+- if (item->command)
+- {
+- command = g_strstrip (item->command->str);
+- g_object_set_data (G_OBJECT (menu_item), "deskmenu", deskmenu);
+- g_signal_connect (G_OBJECT (menu_item), "activate",
+- G_CALLBACK (launcher_activated), g_strdup (command));
+- }
+-
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- menu_item);
+- break;
+-
+-#if HAVE_WNCK
+- case DESKMENU_ITEM_WINDOWLIST:
+- menu_item = gtk_menu_item_new_with_mnemonic ("_Windows");
+-
+- DeskmenuWindowlist *windowlist = deskmenu_windowlist_new ();
+-
+- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
+- windowlist->menu);
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- menu_item);
+- break;
+-
+- case DESKMENU_ITEM_VIEWPORTLIST:
+- menu_item = gtk_menu_item_new_with_mnemonic ("_Viewports");
+-
+- DeskmenuVplist *vplist = deskmenu_vplist_new ();
+-
+- if (item->wrap
+- && strcmp (g_strstrip (item->wrap->str), "true") == 0)
+- vplist->wrap = TRUE;
+-
+- gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
+- vplist->menu);
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- menu_item);
+- break;
+-#endif
+- case DESKMENU_ITEM_RELOAD:
+- menu_item = gtk_image_menu_item_new_with_mnemonic ("_Reload");
+- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+- gtk_image_new_from_stock (GTK_STOCK_REFRESH,
+- GTK_ICON_SIZE_MENU));
+- g_signal_connect (G_OBJECT (menu_item), "activate",
+- G_CALLBACK (quit), NULL);
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- menu_item);
+- break;
+-
+- default:
+- break;
+- }
+-
+-}
+-/* The handler functions. */
+-
+-static void
+-start_element (GMarkupParseContext *context,
+- const gchar *element_name,
+- const gchar **attr_names,
+- const gchar **attr_values,
+- gpointer user_data,
+- GError **error)
+-{
+- Deskmenu *deskmenu = DESKMENU (user_data);
+- DeskmenuElementType element_type;
+- const gchar **ncursor = attr_names, **vcursor = attr_values;
+- GtkWidget *item, *menu;
+-
+- element_type = GPOINTER_TO_INT (g_hash_table_lookup
+- (deskmenu->element_hash, element_name));
+-
+- if ((deskmenu->menu && !deskmenu->current_menu)
+- || (!deskmenu->menu && element_type != DESKMENU_ELEMENT_MENU))
+- {
+- gint line_num, char_num;
+- g_markup_parse_context_get_position (context, &line_num, &char_num);
+- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+- "Error on line %d char %d: Element '%s' declared outside of "
+- "toplevel menu element", line_num, char_num, element_name);
+- return;
+- }
+-
+- switch (element_type)
+- {
+- case DESKMENU_ELEMENT_MENU:
+-
+- if (deskmenu->current_item != NULL)
+- {
+- gint line_num, char_num;
+- g_markup_parse_context_get_position (context, &line_num,
+- &char_num);
+- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+- "Error on line %d char %d: Element 'menu' cannot be nested "
+- "inside of an item element", line_num, char_num);
+- return;
+- }
+-
+- if (!deskmenu->menu)
+- {
+- deskmenu->menu = gtk_menu_new ();
+- g_object_set_data (G_OBJECT (deskmenu->menu), "parent menu",
+- NULL);
+- deskmenu->current_menu = deskmenu->menu;
+- }
+- else
+- {
+- gchar *name = NULL;
+- while (*ncursor)
+- {
+- if (strcmp (*ncursor, "name") == 0)
+- name = g_strdup (*vcursor);
+- else
+- g_set_error (error, G_MARKUP_ERROR,
+- G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+- "Unknown attribute: %s", *ncursor);
+- ncursor++;
+- vcursor++;
+- }
+- if (name)
+- item = gtk_menu_item_new_with_mnemonic (name);
+- else
+- item = gtk_menu_item_new_with_mnemonic ("");
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- item);
+- menu = gtk_menu_new ();
+- g_object_set_data (G_OBJECT (menu), "parent menu",
+- deskmenu->current_menu);
+- deskmenu->current_menu = menu;
+- gtk_menu_item_set_submenu (GTK_MENU_ITEM (item),
+- deskmenu->current_menu);
+- g_free (name);
+- }
+- break;
+-
+- case DESKMENU_ELEMENT_SEPARATOR:
+- break; /* build it in the end function */
+-
+- case DESKMENU_ELEMENT_ITEM:
+-
+- if (deskmenu->current_item != NULL)
+- {
+- gint line_num, char_num;
+- g_markup_parse_context_get_position (context, &line_num,
+- &char_num);
+- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+- "Error on line %d char %d: Element 'item' cannot be nested "
+- "inside of another item element", line_num, char_num);
+- return;
+- }
+-
+- deskmenu->current_item = g_slice_new0 (DeskmenuItem);
+- while (*ncursor)
+- {
+- if (strcmp (*ncursor, "type") == 0)
+- deskmenu->current_item->type = GPOINTER_TO_INT
+- (g_hash_table_lookup (deskmenu->item_hash, *vcursor));
+- else
+- g_set_error (error, G_MARKUP_ERROR,
+- G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+- "Unknown attribute: %s", *ncursor);
+- ncursor++;
+- vcursor++;
+- }
+- break;
+-
+- case DESKMENU_ELEMENT_NAME:
+- while (*ncursor)
+- {
+- if ((strcmp (*ncursor, "mode") == 0)
+- && (strcmp (*vcursor, "exec") == 0))
+- deskmenu->current_item->name_exec = TRUE;
+- ncursor++;
+- vcursor++;
+- } /* no break here to let it fall through */
+- case DESKMENU_ELEMENT_ICON:
+- case DESKMENU_ELEMENT_COMMAND:
+- case DESKMENU_ELEMENT_WRAP:
+- if (deskmenu->current_item)
+- deskmenu->current_item->current_element = element_type;
+- break;
+-
+- default:
+- g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+- "Unknown element: %s", element_name);
+- break;
+- }
+-}
+-
+-static void
+-text (GMarkupParseContext *context,
+- const gchar *text,
+- gsize text_len,
+- gpointer user_data,
+- GError **error)
+-{
+- Deskmenu *deskmenu = DESKMENU (user_data);
+- DeskmenuItem *item = deskmenu->current_item;
+-
+- if (!(item && item->current_element))
+- return;
+-
+- switch (item->current_element)
+- {
+- case DESKMENU_ELEMENT_NAME:
+- if (!item->name)
+- item->name = g_string_new_len (text, text_len);
+- else
+- g_string_append_len (item->name, text, text_len);
+- break;
+-
+- case DESKMENU_ELEMENT_ICON:
+- if (!item->icon)
+- item->icon = g_string_new_len (text, text_len);
+- else
+- g_string_append_len (item->icon, text, text_len);
+- break;
+-
+- case DESKMENU_ELEMENT_COMMAND:
+- if (!item->command)
+- item->command = g_string_new_len (text, text_len);
+- else
+- g_string_append_len (item->command, text, text_len);
+- break;
+-
+- case DESKMENU_ELEMENT_WRAP:
+- if (!item->wrap)
+- item->wrap = g_string_new_len (text, text_len);
+- else
+- g_string_append_len (item->wrap, text, text_len);
+- break;
+-
+- default:
+- break;
+- }
+-
+-}
+-
+-static void
+-end_element (GMarkupParseContext *context,
+- const gchar *element_name,
+- gpointer user_data,
+- GError **error)
+-{
+-
+- DeskmenuElementType element_type;
+- Deskmenu *deskmenu = DESKMENU (user_data);
+- GtkWidget *parent, *item;
+- element_type = GPOINTER_TO_INT (g_hash_table_lookup
+- (deskmenu->element_hash, element_name));
+-
+- switch (element_type)
+- {
+- case DESKMENU_ELEMENT_MENU:
+-
+- g_return_if_fail (deskmenu->current_item == NULL);
+-
+- parent = g_object_get_data (G_OBJECT (deskmenu->current_menu),
+- "parent menu");
+-
+- deskmenu->current_menu = parent;
+-
+- break;
+-
+- case DESKMENU_ELEMENT_SEPARATOR:
+- item = gtk_separator_menu_item_new ();
+- gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
+- item);
+- break;
+-
+- case DESKMENU_ELEMENT_ITEM:
+-
+- g_return_if_fail (deskmenu->current_item != NULL);
+-
+- /* finally make the item ^_^ */
+- deskmenu_construct_item (deskmenu);
+-
+- /* free data used to make it */
+- if (deskmenu->current_item->name)
+- g_string_free (deskmenu->current_item->name, TRUE);
+- if (deskmenu->current_item->icon)
+- g_string_free (deskmenu->current_item->icon, TRUE);
+- if (deskmenu->current_item->command)
+- g_string_free (deskmenu->current_item->command, TRUE);
+- if (deskmenu->current_item->wrap)
+- g_string_free (deskmenu->current_item->wrap, TRUE);
+- g_slice_free (DeskmenuItem, deskmenu->current_item);
+- deskmenu->current_item = NULL;
+- break;
+-
+- default:
+- break;
+- }
+-}
+-
+-/* The list of what handler does what. */
+-static GMarkupParser parser = {
+- start_element,
+- end_element,
+- text,
+- NULL,
+- NULL
+-};
+-
+-
+-/* Class init */
+-static void
+-deskmenu_class_init (DeskmenuClass *deskmenu_class)
+-{
+- dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (deskmenu_class),
+- &dbus_glib_deskmenu_object_info);
+-}
+-
+-/* Instance init */
+-static void
+-deskmenu_init (Deskmenu *deskmenu)
+-{
+-
+- deskmenu->show_hooks = g_slice_new0 (GHookList);
+-
+- g_hook_list_init (deskmenu->show_hooks, sizeof (GHook));
+-
+-
+- deskmenu->menu = NULL;
+- deskmenu->current_menu = NULL;
+- deskmenu->current_item = NULL;
+-
+- deskmenu->envp = NULL;
+-
+- deskmenu->item_hash = g_hash_table_new (g_str_hash, g_str_equal);
+-
+- g_hash_table_insert (deskmenu->item_hash, "launcher",
+- GINT_TO_POINTER (DESKMENU_ITEM_LAUNCHER));
+-#if HAVE_WNCK
+- g_hash_table_insert (deskmenu->item_hash, "windowlist",
+- GINT_TO_POINTER (DESKMENU_ITEM_WINDOWLIST));
+- g_hash_table_insert (deskmenu->item_hash, "viewportlist",
+- GINT_TO_POINTER (DESKMENU_ITEM_VIEWPORTLIST));
+-#endif
+- g_hash_table_insert (deskmenu->item_hash, "reload",
+- GINT_TO_POINTER (DESKMENU_ITEM_RELOAD));
+-
+- deskmenu->element_hash = g_hash_table_new (g_str_hash, g_str_equal);
+-
+- g_hash_table_insert (deskmenu->element_hash, "menu",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_MENU));
+- g_hash_table_insert (deskmenu->element_hash, "separator",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_SEPARATOR));
+- g_hash_table_insert (deskmenu->element_hash, "item",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_ITEM));
+- g_hash_table_insert (deskmenu->element_hash, "name",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_NAME));
+- g_hash_table_insert (deskmenu->element_hash, "icon",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_ICON));
+- g_hash_table_insert (deskmenu->element_hash, "command",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_COMMAND));
+- g_hash_table_insert (deskmenu->element_hash, "wrap",
+- GINT_TO_POINTER (DESKMENU_ELEMENT_WRAP));
+-
+-}
+-
+-static void
+-deskmenu_parse_file (Deskmenu *deskmenu,
+- gchar *configpath)
+-{
+- GError *error = NULL;
+- gboolean success = FALSE;
+- gchar *text;
+- gsize length;
+-
+- if (!configpath)
+- configpath = g_build_path (G_DIR_SEPARATOR_S,
+- g_get_user_config_dir (),
+- "compiz",
+- "deskmenu",
+- "menu.xml",
+- NULL);
+-
+- GMarkupParseContext *context = g_markup_parse_context_new (&parser,
+- 0, deskmenu, NULL);
+-
+- if (!g_file_get_contents (configpath, &text, &length, NULL))
+- {
+- const gchar* const *cursor = g_get_system_config_dirs ();
+- gchar *path = NULL;
+- while (*cursor)
+- {
+- g_free (configpath);
+- g_free (path);
+- path = g_strdup (*cursor);
+- configpath = g_build_path (G_DIR_SEPARATOR_S,
+- path,
+- "compiz",
+- "deskmenu",
+- "menu.xml",
+- NULL);
+-
+- if (g_file_get_contents (configpath, &text, &length, NULL))
+- {
+- success = TRUE;
+- g_free (path);
+- break;
+- }
+- cursor++;
+- }
+- }
+- else
+- {
+- success = TRUE;
+- }
+-
+- if (!success)
+- {
+- g_printerr ("Couldn't find a menu file\n");
+- exit (1);
+- }
+-
+- if (!g_markup_parse_context_parse (context, text, length, &error)
+- || !g_markup_parse_context_end_parse (context, &error))
+- {
+- g_printerr ("Parse of %s failed with message: %s \n",
+- configpath, error->message);
+- g_error_free (error);
+- exit (1);
+- }
+-
+- g_free(text);
+- g_free (configpath);
+- g_markup_parse_context_free (context);
+-
+- gtk_widget_show_all (deskmenu->menu);
+-}
+-
+-/* The show method */
+-gboolean
+-deskmenu_show (Deskmenu *deskmenu,
+- gchar **env,
+- GError **error)
+-{
+- g_hook_list_invoke (deskmenu->show_hooks, FALSE);
+-
+- if (deskmenu->envp)
+- g_strfreev (deskmenu->envp);
+-
+- deskmenu->envp = g_strdupv (env);
+-
+- gtk_menu_popup (GTK_MENU (deskmenu->menu),
+- NULL, NULL, NULL, NULL,
+- 0, 0);
+- return TRUE;
+-}
+-
+-/* The reload method */
+-gboolean
+-deskmenu_reload (Deskmenu *deskmenu,
+- GError **error)
+-{
+- gtk_main_quit ();
+- return TRUE;
+-}
+-
+-int
+-main (int argc,
+- char **argv)
+-{
+- DBusGConnection *connection;
+- GError *error = NULL;
+- GObject *deskmenu;
+-
+- g_type_init ();
+-
+- gchar *filename = NULL;
+- GOptionContext *context;
+- GOptionEntry entries[] =
+- {
+- { "menu", 'm', 0, G_OPTION_ARG_FILENAME, &filename,
+- "Use FILE instead of the default menu file", "FILE" },
+- { NULL, 0, 0, 0, NULL, NULL, NULL }
+- };
+-
+- context = g_option_context_new (NULL);
+- g_option_context_add_main_entries (context, entries, NULL);
+- g_option_context_add_group (context, gtk_get_option_group (TRUE));
+- if (!g_option_context_parse (context, &argc, &argv, &error))
+- {
+- g_printerr ("option parsing failed: %s", error->message);
+- g_error_free (error);
+- exit (1);
+- }
+- g_option_context_free (context);
+-
+- /* Obtain a connection to the session bus */
+- connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+- if (connection == NULL)
+- {
+- g_printerr ("Failed to open connection to bus: %s", error->message);
+- g_error_free (error);
+- exit (1);
+- }
+-
+-#if HAVE_WNCK
+- wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER);
+-#endif
+-
+- gtk_init (&argc, &argv);
+-
+- deskmenu = g_object_new (DESKMENU_TYPE, NULL);
+-
+- deskmenu_parse_file (DESKMENU (deskmenu), filename);
+-
+- dbus_g_connection_register_g_object (connection,
+- DESKMENU_PATH_DBUS,
+- deskmenu);
+-
+- if (!dbus_bus_request_name (dbus_g_connection_get_connection (connection),
+- DESKMENU_SERVICE_DBUS,
+- DBUS_NAME_FLAG_REPLACE_EXISTING,
+- NULL))
+- return 1;
+-
+- gtk_main ();
+-
+- return 0;
+-}
+-
++/*
++ * compiz-deskmenu is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * compiz-deskmenu is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Copyright 2008 Christopher Williams <christopherw@verizon.net>
++ */
++
++ /*
++Roadmap:
++Necessary:
++ TODO: Add a viewport # indicator for the window list for accesiblity reasons difficulty: hard
++ TODO: Add configuration for menu icon size difficulty: easy
++ TODO: Add toggle of tearables difficulty: easy
++ TODO: Add a sane icon dialog difficulty: medium-hard
++ TODO: Make the editor able to edit non-default files
++For fun, might not implement:
++TODO: Add ability to call up menus from the menu.xml file by name, if this is really, really needed or requested
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <gtk/gtk.h>
++
++#define HAVE_WNCK 1
++
++#if HAVE_WNCK
++#include "deskmenu-wnck.h"
++#endif
++
++#include "deskmenu-menu.h"
++
++
++G_DEFINE_TYPE(Deskmenu, deskmenu, G_TYPE_OBJECT) //this is calling deskmenu_class_init
++
++GQuark
++deskmenu_error_quark (void)
++{
++ static GQuark quark = 0;
++ if (!quark)
++ quark = g_quark_from_static_string ("deskmenu_error");
++ return quark;
++}
++
++static void
++quit (GtkWidget *widget,
++ gpointer data)
++{
++ gtk_main_quit ();
++}
++
++//stolen from openbox, possibly move this outside in order to make it a function to parse launchers and icon location
++gchar *parse_expand_tilde(const gchar *f)
++{
++gchar *ret;
++GRegex *regex;
++
++if (!f)
++ return NULL;
++
++regex = g_regex_new("(?:^|(?<=[ \\t]))~(?:(?=[/ \\t])|$)",
++ G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
++ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL);
++g_regex_unref(regex);
++
++return ret;
++}
++//end stolen
++
++//This is how menu command is launched
++static void
++launcher_activated (GtkWidget *widget,
++ gchar *command)
++{
++ GError *error = NULL;
++ Deskmenu *deskmenu;
++
++ deskmenu = g_object_get_data (G_OBJECT (widget), "deskmenu");
++ if (!gdk_spawn_command_line_on_screen (gdk_screen_get_default (), parse_expand_tilde(command), &error))
++ {
++ GtkWidget *message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR,
++ GTK_BUTTONS_CLOSE, "%s", error->message);
++ gtk_dialog_run (GTK_DIALOG (message));
++ gtk_widget_destroy (message);
++ }
++
++}
++
++//place & in front of stdout for standard stdout, how a command is launched as an exec
++static void
++launcher_name_exec_update (GtkWidget *label)
++{
++ gchar *exec, *stdout;
++ exec = g_object_get_data (G_OBJECT (label), "exec");
++ if (g_spawn_command_line_sync (parse_expand_tilde(exec), &stdout, NULL, NULL, NULL))
++ gtk_label_set_text (GTK_LABEL (label), g_strstrip(stdout));
++ else
++ gtk_label_set_text (GTK_LABEL (label), "execution error");
++ g_free (stdout);
++}
++
++static void
++deskmenu_construct_item (Deskmenu *deskmenu)
++{
++ DeskmenuItem *item = deskmenu->current_item;
++ GtkWidget *menu_item;
++ gchar *name, *icon, *command, *vpicon;
++ gint w, h;
++//constructs the items in menu
++ switch (item->type)
++ {
++ case DESKMENU_ITEM_LAUNCHER:
++ if (item->name_exec)
++ {
++ GtkWidget *label;
++ GHook *hook;
++
++ name = g_strstrip (item->name->str);
++
++ menu_item = gtk_image_menu_item_new ();
++ label = gtk_label_new_with_mnemonic (NULL);
++ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
++
++ g_object_set_data (G_OBJECT (label), "exec", g_strdup (name));
++ gtk_container_add (GTK_CONTAINER (menu_item), label);
++ hook = g_hook_alloc (deskmenu->show_hooks);
++
++ hook->data = (gpointer) label;
++ hook->func = (GHookFunc *) launcher_name_exec_update;
++ g_hook_append (deskmenu->show_hooks, hook);
++ }
++ else
++ {
++ if (item->name)
++ name = g_strstrip (item->name->str);
++ else
++ name = "";
++
++ menu_item = gtk_image_menu_item_new_with_mnemonic (name);
++
++ }
++ if (item->icon)
++ {
++ icon = g_strstrip (item->icon->str);
++ if (item->icon_file) {
++ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
++ (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL)));
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
++ gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU));
++ }
++ }
++
++ if (item->command)
++ {
++
++ command = g_strstrip (item->command->str);
++ g_object_set_data (G_OBJECT (menu_item), "deskmenu", deskmenu);
++ g_signal_connect (G_OBJECT (menu_item), "activate",
++ G_CALLBACK (launcher_activated), g_strdup (command));
++ }
++
++ gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
++ menu_item);
++ break;
++#if HAVE_WNCK
++ case DESKMENU_ITEM_WINDOWLIST:
++ menu_item = gtk_image_menu_item_new_with_mnemonic ("_Windows");
++
++ DeskmenuWindowlist *windowlist = deskmenu_windowlist_new ();
++ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
++ windowlist->menu);
++ gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
++ menu_item);
++ if (item->icon)
++ {
++ windowlist->images = TRUE;
++ icon = g_strstrip (item->icon->str);
++ if (item->icon_file) {
++ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
++ (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL)));
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
++ gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU));
++ }
++ }
++ break;
++
++ case DESKMENU_ITEM_VIEWPORTLIST:
++ menu_item = gtk_image_menu_item_new_with_mnemonic ("_Viewports");
++
++ DeskmenuVplist *vplist = deskmenu_vplist_new ();
++ if (item->wrap
++ && strcmp (g_strstrip (item->wrap->str), "true") == 0)
++ vplist->wrap = TRUE;
++
++ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
++ vplist->menu);
++ gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
++ menu_item);
++ if (item->icon)
++ {
++ vplist->images = TRUE;
++ icon = g_strstrip (item->icon->str);
++ if (item->icon_file) {
++ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
++ (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL)));
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
++ gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU));
++ }
++ }
++ if (item->vpicon)
++ {
++ vpicon = g_strstrip (parse_expand_tilde(item->vpicon->str));
++ if (item->vpicon_file) {
++ vplist->file = TRUE;
++ }
++ vplist->icon = vpicon;
++ }
++ break;
++#endif
++
++ default:
++ break;
++ }
++
++}
++/* The handler functions. */
++
++static void
++start_element (GMarkupParseContext *context,
++ const gchar *element_name,
++ const gchar **attr_names,
++ const gchar **attr_values,
++ gpointer user_data,
++ GError **error)
++{
++ Deskmenu *deskmenu = DESKMENU (user_data);
++ DeskmenuElementType element_type;
++ const gchar **ncursor = attr_names, **vcursor = attr_values;
++ GtkWidget *item, *menu;
++
++ element_type = GPOINTER_TO_INT (g_hash_table_lookup
++ (deskmenu->element_hash, element_name));
++
++ //TODO: maybe this could be edited to see the new compiz-deskmenu element, if this is needed in order to make pipes
++ if ((deskmenu->menu && !deskmenu->current_menu)
++ || (!deskmenu->menu && element_type != DESKMENU_ELEMENT_MENU))
++ {
++ gint line_num, char_num;
++ g_markup_parse_context_get_position (context, &line_num, &char_num);
++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
++ "Error on line %d char %d: Element '%s' declared outside of "
++ "toplevel menu element", line_num, char_num, element_name);
++ return;
++ }
++
++ switch (element_type)
++ {
++ case DESKMENU_ELEMENT_MENU:
++
++ if (deskmenu->current_item != NULL)
++ {
++ gint line_num, char_num;
++ g_markup_parse_context_get_position (context, &line_num,
++ &char_num);
++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
++ "Error on line %d char %d: Element 'menu' cannot be nested "
++ "inside of an item element", line_num, char_num);
++ return;
++ }
++ if (!deskmenu->menu)
++ {
++ deskmenu->menu = gtk_menu_new ();
++ g_object_set_data (G_OBJECT (deskmenu->menu), "parent menu",
++ NULL);
++ deskmenu->current_menu = deskmenu->menu;
++ }
++ else
++ {
++ gchar *name = NULL;
++ gchar *icon = NULL;
++ gboolean name_exec = FALSE;
++ gboolean icon_file = FALSE;
++ gint w, h;
++ while (*ncursor)
++ {
++ if (strcmp (*ncursor, "name") == 0)
++ name = g_strdup (*vcursor);
++ else if (strcmp (*ncursor, "icon") == 0)
++ icon = g_strdup (*vcursor);
++ else if ((strcmp (*ncursor, "mode") == 0)
++ && (strcmp (*vcursor, "exec") == 0))
++ name_exec = TRUE;
++ else if ((strcmp (*ncursor, "mode1") == 0)
++ && (strcmp (*vcursor, "file") == 0))
++ icon_file = TRUE;
++ else
++ g_set_error (error, G_MARKUP_ERROR,
++ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
++ "Unknown attribute: %s", *ncursor);
++ ncursor++;
++ vcursor++;
++ }
++ if (name_exec)
++ {
++ GtkWidget *label;
++ GHook *hook;
++
++ item = gtk_image_menu_item_new ();
++ label = gtk_label_new_with_mnemonic (NULL);
++ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
++
++ g_object_set_data (G_OBJECT (label), "exec", g_strdup (name));
++ gtk_container_add (GTK_CONTAINER (item), label);
++ hook = g_hook_alloc (deskmenu->show_hooks);
++
++ hook->data = (gpointer) label;
++ hook->func = (GHookFunc *) launcher_name_exec_update;
++ g_hook_append (deskmenu->show_hooks, hook);
++ }
++ else
++ {
++ if (name)
++ item = gtk_image_menu_item_new_with_mnemonic (name); //allow menus to have icons
++
++ else
++ item = gtk_image_menu_item_new_with_mnemonic ("");
++ gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
++ item);
++ }
++ if (icon)
++ {
++ if (icon_file) {
++ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
++ (item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL)));
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
++ gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU));
++ }
++ }
++ menu = gtk_menu_new ();
++ g_object_set_data (G_OBJECT (menu), "parent menu",
++ deskmenu->current_menu);
++ deskmenu->current_menu = menu;
++ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item),
++ deskmenu->current_menu);
++ g_free (name);
++ g_free (icon);
++ }
++ break;
++
++ case DESKMENU_ELEMENT_SEPARATOR:
++ break; /* build it in the end function */
++
++ case DESKMENU_ELEMENT_ITEM:
++
++ if (deskmenu->current_item != NULL)
++ {
++ gint line_num, char_num;
++ g_markup_parse_context_get_position (context, &line_num,
++ &char_num);
++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
++ "Error on line %d char %d: Element 'item' cannot be nested "
++ "inside of another item element", line_num, char_num);
++ return;
++ }
++
++ deskmenu->current_item = g_slice_new0 (DeskmenuItem);
++ while (*ncursor)
++ {
++ if (strcmp (*ncursor, "type") == 0)
++ deskmenu->current_item->type = GPOINTER_TO_INT
++ (g_hash_table_lookup (deskmenu->item_hash, *vcursor));
++ else
++ g_set_error (error, G_MARKUP_ERROR,
++ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
++ "Unknown attribute: %s", *ncursor);
++ ncursor++;
++ vcursor++;
++ }
++ break;
++
++ case DESKMENU_ELEMENT_NAME:
++ while (*ncursor)
++ {
++ if ((strcmp (*ncursor, "mode") == 0)
++ && (strcmp (*vcursor, "exec") == 0))
++ deskmenu->current_item->name_exec = TRUE;
++ ncursor++;
++ vcursor++;
++ } /* no break here to let it fall through */
++ case DESKMENU_ELEMENT_ICON:
++ while (*ncursor)
++ {
++ if ((strcmp (*ncursor, "mode1") == 0)
++ && (strcmp (*vcursor, "file") == 0))
++ deskmenu->current_item->icon_file = TRUE;
++ ncursor++;
++ vcursor++;
++ } /* no break here to let it fall through */
++ case DESKMENU_ELEMENT_VPICON:
++ while (*ncursor)
++ {
++ if ((strcmp (*ncursor, "mode1") == 0)
++ && (strcmp (*vcursor, "file") == 0))
++ deskmenu->current_item->vpicon_file = TRUE;
++ ncursor++;
++ vcursor++;
++ } /* no break here to let it fall through */
++ case DESKMENU_ELEMENT_COMMAND:
++ case DESKMENU_ELEMENT_WRAP:
++ if (deskmenu->current_item)
++ deskmenu->current_item->current_element = element_type;
++ break;
++ default:
++ g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
++ "Unknown element: %s", element_name);
++ break;
++ }
++}
++//dealing with empty attributes
++static void
++text (GMarkupParseContext *context,
++ const gchar *text,
++ gsize text_len,
++ gpointer user_data,
++ GError **error)
++{
++ Deskmenu *deskmenu = DESKMENU (user_data);
++ DeskmenuItem *item = deskmenu->current_item;
++
++ if (!(item && item->current_element))
++ return;
++
++ switch (item->current_element)
++ {
++ case DESKMENU_ELEMENT_NAME:
++ if (!item->name)
++ item->name = g_string_new_len (text, text_len);
++ else
++ g_string_append_len (item->name, text, text_len);
++ break;
++
++ case DESKMENU_ELEMENT_ICON:
++ if (!item->icon)
++ item->icon = g_string_new_len (text, text_len);
++ else
++ g_string_append_len (item->icon, text, text_len);
++ break;
++
++ case DESKMENU_ELEMENT_VPICON:
++ if (!item->vpicon)
++ item->vpicon = g_string_new_len (text, text_len);
++ else
++ g_string_append_len (item->vpicon, text, text_len);
++ break;
++
++ case DESKMENU_ELEMENT_COMMAND:
++ if (!item->command)
++ item->command = g_string_new_len (text, text_len);
++ else
++ g_string_append_len (item->command, text, text_len);
++ break;
++
++ case DESKMENU_ELEMENT_WRAP:
++ if (!item->wrap)
++ item->wrap = g_string_new_len (text, text_len);
++ else
++ g_string_append_len (item->wrap, text, text_len);
++ break;
++
++ default:
++ break;
++ }
++
++}
++
++static void
++end_element (GMarkupParseContext *context,
++ const gchar *element_name,
++ gpointer user_data,
++ GError **error)
++{
++
++ DeskmenuElementType element_type;
++ Deskmenu *deskmenu = DESKMENU (user_data);
++ GtkWidget *parent, *item;
++ element_type = GPOINTER_TO_INT (g_hash_table_lookup
++ (deskmenu->element_hash, element_name));
++
++ switch (element_type)
++ {
++ case DESKMENU_ELEMENT_MENU:
++
++ g_return_if_fail (deskmenu->current_item == NULL);
++
++ parent = g_object_get_data (G_OBJECT (deskmenu->current_menu),
++ "parent menu");
++
++ deskmenu->current_menu = parent;
++
++ break;
++
++ case DESKMENU_ELEMENT_SEPARATOR:
++ item = gtk_separator_menu_item_new ();
++ gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu),
++ item);
++ break;
++
++ case DESKMENU_ELEMENT_ITEM:
++
++ g_return_if_fail (deskmenu->current_item != NULL);
++
++ /* finally make the item ^_^ */
++ deskmenu_construct_item (deskmenu);
++
++ /* free data used to make it */
++ if (deskmenu->current_item->name)
++ g_string_free (deskmenu->current_item->name, TRUE);
++ if (deskmenu->current_item->icon)
++ g_string_free (deskmenu->current_item->icon, TRUE);
++ if (deskmenu->current_item->command)
++ g_string_free (deskmenu->current_item->command, TRUE);
++ if (deskmenu->current_item->wrap)
++ g_string_free (deskmenu->current_item->wrap, TRUE);
++ if (deskmenu->current_item->vpicon)
++ g_string_free (deskmenu->current_item->vpicon, TRUE);
++ g_slice_free (DeskmenuItem, deskmenu->current_item);
++ deskmenu->current_item = NULL;
++ break;
++ default:
++ break;
++ }
++}
++
++/* The list of what handler does what. */
++//this parses menus
++static GMarkupParser parser = {
++ start_element,
++ end_element,
++ text,
++ NULL,
++ NULL
++};
++
++
++// Class init
++static void
++deskmenu_class_init (DeskmenuClass *deskmenu_class)
++{
++ //G_TYPE_FROM_CLASS (deskmenu_class); //I have NO idea what's going on here, but apparently, just initializing this function makes it work
++}
++
++/* Instance init, matches up words to types, note how there's no handler for pipe since it's replaced in its own chunk */
++static void
++deskmenu_init (Deskmenu *deskmenu)
++{
++
++ deskmenu->show_hooks = g_slice_new0 (GHookList);
++
++ g_hook_list_init (deskmenu->show_hooks, sizeof (GHook));
++
++
++ deskmenu->menu = NULL;
++ deskmenu->current_menu = NULL;
++ deskmenu->current_item = NULL;
++
++ deskmenu->item_hash = g_hash_table_new (g_str_hash, g_str_equal);
++
++ g_hash_table_insert (deskmenu->item_hash, "launcher",
++ GINT_TO_POINTER (DESKMENU_ITEM_LAUNCHER));
++#if HAVE_WNCK
++ g_hash_table_insert (deskmenu->item_hash, "windowlist",
++ GINT_TO_POINTER (DESKMENU_ITEM_WINDOWLIST));
++ g_hash_table_insert (deskmenu->item_hash, "viewportlist",
++ GINT_TO_POINTER (DESKMENU_ITEM_VIEWPORTLIST));
++#endif
++
++ deskmenu->element_hash = g_hash_table_new (g_str_hash, g_str_equal);
++
++ g_hash_table_insert (deskmenu->element_hash, "menu",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_MENU));
++ g_hash_table_insert (deskmenu->element_hash, "separator",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_SEPARATOR));
++ g_hash_table_insert (deskmenu->element_hash, "item",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_ITEM));
++ g_hash_table_insert (deskmenu->element_hash, "name",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_NAME));
++ g_hash_table_insert (deskmenu->element_hash, "icon",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_ICON));
++ g_hash_table_insert (deskmenu->element_hash, "vpicon",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_VPICON));
++ g_hash_table_insert (deskmenu->element_hash, "command",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_COMMAND));
++ g_hash_table_insert (deskmenu->element_hash, "wrap",
++ GINT_TO_POINTER (DESKMENU_ELEMENT_WRAP));
++
++}
++
++static void
++deskmenu_parse_file (Deskmenu *deskmenu,
++ gchar *configpath)
++{
++ GError *error = NULL;
++ gboolean success = FALSE;
++ gchar *text, *exec, *stdout;
++ gsize length;
++ GRegex *regex, *command;
++ int i;
++
++ if (!configpath)
++ configpath = g_build_path (G_DIR_SEPARATOR_S,
++ g_get_user_config_dir (),
++ "compiz",
++ "deskmenu",
++ "menu.xml",
++ NULL);
++
++ GMarkupParseContext *context = g_markup_parse_context_new (&parser,
++ 0, deskmenu, NULL);
++
++ if (!g_file_get_contents (configpath, &text, &length, NULL))
++ {
++ const gchar* const *cursor = g_get_system_config_dirs ();
++ gchar *path = NULL;
++ while (*cursor)
++ {
++ g_free (configpath);
++ g_free (path);
++ path = g_strdup (*cursor);
++ configpath = g_build_path (G_DIR_SEPARATOR_S,
++ path,
++ "compiz",
++ "deskmenu",
++ "menu.xml",
++ NULL);
++
++ if (g_file_get_contents (configpath, &text, &length, NULL))
++ {
++ success = TRUE;
++ g_free (path);
++ break;
++ }
++ cursor++;
++ }
++ }
++ else
++ {
++ success = TRUE;
++ }
++
++ if (!success)
++ {
++ g_printerr ("Couldn't find a menu file\n");
++ exit (1);
++ }
++
++ i = 0;
++ regex = g_regex_new("(<pipe command=\".*\"/>)", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
++ command = g_regex_new("<pipe command=\"|\"/>", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL);
++ gchar **menu_chunk = g_regex_split (regex, text, 0); //this splits the menu into parsable chunks, needed for pipe item capabilities
++
++ g_free(text); //we no longer need the loaded file
++
++ //this loop will replace the pipeitem chunk with its output, other chunks are let through as is
++ while (menu_chunk[i])
++ {
++ if (g_regex_match (regex,menu_chunk[i],0,0))
++ {
++ exec = parse_expand_tilde(g_strstrip(g_regex_replace_literal(command, menu_chunk[i], -1, 0, "", 0, NULL)));
++ g_spawn_command_line_sync (exec, &stdout, NULL, NULL, NULL);
++ menu_chunk[i] = stdout;
++ }
++ i++;
++ }
++ g_regex_unref(regex); //free the pipeitem chunk checker
++ g_regex_unref(command); //free the pipe command extractor
++
++ i = 0;
++ while (menu_chunk[i])
++ {
++ g_markup_parse_context_parse (context, menu_chunk[i], strlen(menu_chunk[i]), &error);
++ //fix error reporting here, otherwise, it works fine
++ i++;
++ }
++
++ g_strfreev(menu_chunk); //free the menu chunks and their container
++ g_free (configpath); //free the file path
++ g_markup_parse_context_free (context); //free the parser
++
++ gtk_widget_show_all (deskmenu->menu);
++}
++
++/* The show method */
++gboolean
++deskmenu_show (Deskmenu *deskmenu,
++ GError **error)
++{
++ g_hook_list_invoke (deskmenu->show_hooks, FALSE);
++
++//sloppy fix until dbus is removed
++ g_signal_connect_swapped (GTK_MENU (deskmenu->menu), "deactivate",
++ G_CALLBACK (quit), NULL);
++
++ gtk_menu_popup (GTK_MENU (deskmenu->menu),
++ NULL, NULL, NULL, NULL,
++ 0, 0);
++ return TRUE;
++}
++
++int
++main (int argc,
++ char **argv)
++{
++ GError *error = NULL;
++ GObject *deskmenu;
++
++ g_type_init ();
++
++ gchar *filename = NULL;
++ GOptionContext *context;
++ GOptionEntry entries[] =
++ {
++ { "menu", 'm', 0, G_OPTION_ARG_FILENAME, &filename,
++ "Use FILE instead of the default menu file", "FILE" },
++ { NULL, 0, 0, 0, NULL, NULL, NULL }
++ };
++
++ context = g_option_context_new (NULL);
++ g_option_context_add_main_entries (context, entries, NULL);
++ g_option_context_add_group (context, gtk_get_option_group (TRUE));
++ if (!g_option_context_parse (context, &argc, &argv, &error))
++ {
++ g_printerr ("option parsing failed: %s", error->message);
++ g_error_free (error);
++ exit (1);
++ }
++ g_option_context_free (context);
++
++#if HAVE_WNCK
++ wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER);
++#endif
++
++ gtk_init (&argc, &argv);
++
++ deskmenu = g_object_new (DESKMENU_TYPE, NULL);
++
++ deskmenu_parse_file (DESKMENU (deskmenu), filename);
++
++ deskmenu_show (DESKMENU (deskmenu), &error);
++
++ gtk_main ();
++
++ return 0;
++}
+diff -aur compiz-deskmenu/deskmenu-menu.h compiz-deskmenu3/deskmenu-menu.h
+--- compiz-deskmenu/deskmenu-menu.h 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/deskmenu-menu.h 2010-11-16 15:39:49.000000000 -0800
+@@ -15,8 +15,6 @@
+ * Copyright 2008 Christopher Williams <christopherw@verizon.net>
+ */
+
+-#include "deskmenu-common.h"
+-
+ typedef struct Deskmenu Deskmenu;
+ typedef struct DeskmenuClass DeskmenuClass;
+ typedef struct DeskmenuItem DeskmenuItem;
+@@ -28,8 +26,7 @@
+ DESKMENU_ITEM_NONE = 0,
+ DESKMENU_ITEM_LAUNCHER,
+ DESKMENU_ITEM_WINDOWLIST,
+- DESKMENU_ITEM_VIEWPORTLIST,
+- DESKMENU_ITEM_RELOAD
++ DESKMENU_ITEM_VIEWPORTLIST
+ } DeskmenuItemType;
+
+ typedef enum
+@@ -41,7 +38,8 @@
+ DESKMENU_ELEMENT_NAME,
+ DESKMENU_ELEMENT_ICON,
+ DESKMENU_ELEMENT_COMMAND,
+- DESKMENU_ELEMENT_WRAP
++ DESKMENU_ELEMENT_WRAP,
++ DESKMENU_ELEMENT_VPICON
+ } DeskmenuElementType;
+
+
+@@ -51,12 +49,14 @@
+ DeskmenuElementType current_element;
+ GString *name;
+ gboolean name_exec;
++ gboolean icon_file;
++ gboolean vpicon_file;
+ GString *icon;
++ GString *vpicon;
+ GString *command;
+ GString *wrap;
+ };
+
+-
+ struct Deskmenu
+ {
+ GObject parent;
+@@ -69,7 +69,6 @@
+ GHashTable *item_hash;
+ GHashTable *element_hash;
+ GHookList *show_hooks;
+- gchar **envp;
+ };
+
+ struct DeskmenuClass
+@@ -79,8 +78,8 @@
+
+
+ #define DESKMENU_TYPE (deskmenu_get_type ())
+-#define DESKMENU(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DESKMENU_TYPE, Deskmenu))
+-#define DESKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DESKMENU_TYPE, DeskmenuClass))
++#define DESKMENU(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DESKMENU_TYPE, Deskmenu)) //looks to see if it isn't a deskmenu already, if it isn't, make it one
++#define DESKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DESKMENU_TYPE, DeskmenuClass)) //looks to see if class cast, if it isn't, cast it
+ #define IS_DESKMENU(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DESKMENU_TYPE))
+ #define IS_DESKMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DESKMENU_TYPE))
+ #define DESKMENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DESKMENU_TYPE, DeskMenu))
+@@ -92,9 +91,3 @@
+
+ GQuark deskmenu_error_quark (void);
+ #define DESKMENU_ERROR deskmenu_error_quark ()
+-
+-
+-gboolean deskmenu_show (Deskmenu *deskmenu, gchar **env, GError **error);
+-gboolean deskmenu_reload (Deskmenu *deskmenu, GError **error);
+-
+-
+diff -aur compiz-deskmenu/deskmenu-wnck.c compiz-deskmenu3/deskmenu-wnck.c
+--- compiz-deskmenu/deskmenu-wnck.c 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/deskmenu-wnck.c 2010-11-15 18:46:24.000000000 -0800
+@@ -113,18 +113,28 @@
+ mnemonic = "_";
+ else
+ mnemonic = "";
+-
++ //wnck_window_get_workspace (dmwin->window)
++ //TODO: get this to calculate right
++ /*
++ column = wnck_workspace_get_viewport_x (wnck_window_get_workspace (dmwin->window));
++ width = wnck_workspace_get_width (wnck_screen_get_workspace (wnck_screen_get_default (), 0))/wnck_screen_get_width (wnck_screen_get_default ());
++ row = wnck_workspace_get_viewport_y (wnck_window_get_workspace (dmwin->window));
++ vpidNumber = column + ((row) * width);
++ vpid = g_strdup_printf ("%d %d %d %d :",vpidNumber,width,column,row);
++ g_print(vpid);
++ decorated_name = g_strconcat ("[",vpid, "] ", ante, mnemonic, name->str, post, NULL);
++ */
+ decorated_name = g_strconcat (ante, mnemonic, name->str, post, NULL);
+
+ unescaped = g_strconcat (ante, wnck_window_get_name (dmwin->window),
+ post, NULL);
+-
+ gtk_label_set_text_with_mnemonic (GTK_LABEL (dmwin->label), decorated_name);
+
+ gtk_widget_set_size_request (dmwin->label,
+ wnck_selector_get_width (dmwin->windowlist->menu, unescaped), -1);
+
+ g_string_free (name, TRUE);
++ //g_free (vpid);
+ g_free (decorated_name);
+ g_free (unescaped);
+ }
+@@ -171,19 +181,18 @@
+ {
+ GdkPixbuf *pixbuf;
+ gboolean free_pixbuf;
+-
+- pixbuf = wnck_window_get_mini_icon (window);
+- free_pixbuf = FALSE;
+- if (wnck_window_is_minimized (window))
+- {
+- pixbuf = wnck_selector_dimm_icon (pixbuf);
+- free_pixbuf = TRUE;
+- }
+-
+- gtk_image_set_from_pixbuf (GTK_IMAGE (dmwin->image), pixbuf);
+-
+- if (free_pixbuf)
+- g_object_unref (pixbuf);
++ pixbuf = wnck_window_get_mini_icon (window);
++ free_pixbuf = FALSE;
++ if (wnck_window_is_minimized (window))
++ {
++ pixbuf = wnck_selector_dimm_icon (pixbuf);
++ free_pixbuf = TRUE;
++ }
++
++ gtk_image_set_from_pixbuf (GTK_IMAGE (dmwin->image), pixbuf);
++
++ if (free_pixbuf)
++ g_object_unref (pixbuf);
+ }
+
+ static void
+@@ -257,10 +266,11 @@
+ gtk_label_set_ellipsize (GTK_LABEL (dmwin->label), PANGO_ELLIPSIZE_END);
+
+ dmwin->image = gtk_image_new ();
+-
++ if(windowlist->images) {
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (dmwin->item),
+ dmwin->image);
+-
++ }
++
+ g_signal_connect (G_OBJECT (dmwin->item), "activate",
+ G_CALLBACK (activate_window), window);
+
+@@ -466,8 +476,8 @@
+ {
+ GtkWidget *item;
+ item = gtk_image_menu_item_new_with_mnemonic (name);
+- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+- gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU));
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
++ gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU));
+ g_object_set_data (G_OBJECT (item), "direction",
+ GINT_TO_POINTER (direction));
+ g_signal_connect (G_OBJECT (item), "activate",
+@@ -520,6 +530,17 @@
+ gtk_widget_hide (vplist->go_up);
+ gtk_widget_hide (vplist->go_down);
+
++ if(!vplist->images) //this rips off those arrows if you don't want images AT ALL
++ {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (vplist->go_left),
++ gtk_image_new_from_stock ("", GTK_ICON_SIZE_MENU));
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (vplist->go_right),
++ gtk_image_new_from_stock ("", GTK_ICON_SIZE_MENU));
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (vplist->go_up),
++ gtk_image_new_from_stock ("", GTK_ICON_SIZE_MENU));
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (vplist->go_up),
++ gtk_image_new_from_stock ("", GTK_ICON_SIZE_MENU));
++ }
+ gtk_widget_set_no_show_all (vplist->go_left,
+ !deskmenu_vplist_can_move (vplist, WNCK_MOTION_LEFT));
+ gtk_widget_set_no_show_all (vplist->go_right,
+@@ -531,15 +552,34 @@
+
+ GtkWidget *item;
+ guint i;
++ gint w, h;
++ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
+
+ if (new_count > vplist->old_count)
+ {
+ gchar *text;
+-
++//TODO: edit this section to add thumnbnails for ports, possibly just set the thumbnails to be viewport wallpapers?
+ for (i = vplist->old_count; i < new_count; i++)
+ {
+ text = g_strdup_printf ("Viewport _%i", i + 1);
+- item = gtk_menu_item_new_with_mnemonic (text);
++ item = gtk_image_menu_item_new_with_mnemonic (text);
++ if (vplist->images)
++ { //this'll set viewport thumbnail later if I can figure out how
++ if (vplist->icon){
++ if (vplist->file) {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM
++ (item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (vplist->icon, w, h, NULL)));
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
++ gtk_image_new_from_icon_name (vplist->icon, GTK_ICON_SIZE_MENU));
++ }
++ }
++ else {
++ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
++ gtk_image_new_from_icon_name ("user-desktop", GTK_ICON_SIZE_MENU));
++ }
++ }
+ g_object_set_data (G_OBJECT (item), "viewport",
+ GUINT_TO_POINTER (i + 1));
+ g_signal_connect (G_OBJECT (item), "activate",
+@@ -613,4 +653,3 @@
+
+ return vplist;
+ }
+-
+diff -aur compiz-deskmenu/deskmenu-wnck.h compiz-deskmenu3/deskmenu-wnck.h
+--- compiz-deskmenu/deskmenu-wnck.h 2008-03-14 16:25:23.000000000 -0700
++++ compiz-deskmenu3/deskmenu-wnck.h 2010-11-15 17:34:36.000000000 -0800
+@@ -8,6 +8,7 @@
+ GtkWidget *menu;
+ GtkWidget *empty_item;
+ GList *windows;
++ gboolean images; //toggles use of icons
+ } DeskmenuWindowlist;
+
+ typedef struct DeskmenuWindow
+@@ -30,6 +31,8 @@
+ GtkWidget *go_down;
+ GPtrArray *goto_items;
+ gboolean wrap;
++ gboolean images; //toggles use of icons
++ gboolean file; // whether the icon of choice is from theme or not
+ /* store some calculations */
+ guint hsize; /* 1-indexed horizontal viewport count */
+ guint vsize;
+@@ -43,6 +46,7 @@
+ guint workspace_height;
+ guint old_count; /* store old hsize * vsize */
+ guint old_vpid; /* store old viewport number */
++ gchar *icon; /* stores viewport icon of choice */
+ } DeskmenuVplist;
+
+ DeskmenuWindowlist* deskmenu_windowlist_new (void);