From ab1b2d468bdb61129289eb7724e69b45778f29ef Mon Sep 17 00:00:00 2001 From: Florian Dejonckheere Date: Thu, 5 Feb 2015 10:07:46 +0100 Subject: [PATCH] Flatten directory structure --- .SRCINFO | 24 + PKGBUILD | 47 + compiz-deskmenu-editor | 1082 ++++++++++++ compiz-deskmenu.install | 26 + huge.patch | 3504 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 4683 insertions(+) create mode 100644 .SRCINFO create mode 100755 PKGBUILD create mode 100755 compiz-deskmenu-editor create mode 100644 compiz-deskmenu.install create mode 100755 huge.patch 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 +# Contributor: Julien MISCHKOWITZ +# Contributor: Alessio Biancalana +# Contributor: ShadowKyogre + +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('Name:') + 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('Name mode:') + 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('Icon:') + 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('Icon mode:') + 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('Command:') + 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('Icon:') + 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('Icon mode:') + 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('Icon:') + 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('Icon mode:') + 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('Icon:') + 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('Icon mode:') + 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('Name:') + 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('Name mode:') + 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('Icon:') + 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('Icon mode:') + 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 "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('Name:') +- 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('Name mode:') +- 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('Icon:') +- 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('Command:') +- 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('Name:') ++ 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('Name mode:') ++ 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('Icon:') ++ 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('Icon mode:') ++ 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('Command:') ++ 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('Icon:') ++ 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('Icon mode:') ++ 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('Icon:') ++ 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('Icon mode:') ++ 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('Viewport Icon:') ++ 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('Viewport Icon mode:') ++ 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('Command:') ++ 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('Name:') +- 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('Name:') ++ 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('Name mode:') ++ 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('Icon:') ++ 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('Icon mode:') ++ 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 . +- * +- * Copyright 2008 Christopher Williams +- */ +- +-#include +-#include +-#include +-#include +- +-#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 . ++ * ++ * Copyright 2008 Christopher Williams ++ */ ++ ++ /* ++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 ++#include ++#include ++ ++#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("()", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL); ++ command = g_regex_new("", 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 + */ + +-#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);