From 607eb4be21eaabe01871b702ccb45aae1c309b10 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 17 Jun 2008 11:42:57 +0100 Subject: [PATCH] The buttons in the 0desktop management UI now work. - Run launches the program. - Help displays doc-dir using xdg-open, or the whole package if no doc dir is set. - Properties runs "0launch --gui". - Delete removes the launcher file. --- zeroinstall/gtkui/applistbox.py | 127 +++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 28 deletions(-) diff --git a/zeroinstall/gtkui/applistbox.py b/zeroinstall/gtkui/applistbox.py index ce8241d..8325b59 100644 --- a/zeroinstall/gtkui/applistbox.py +++ b/zeroinstall/gtkui/applistbox.py @@ -5,40 +5,29 @@ import os import gtk, gobject import gtk.glade +import subprocess from zeroinstall.gtkui import icon, xdgutils +def _pango_escape(s): + return s.replace('&', '&').replace('<', '<') + class AppListBox: """A dialog box which lists applications already added to the menus.""" ICON, URI, NAME, MARKUP = range(4) def __init__(self, iface_cache): gladefile = os.path.join(os.path.dirname(__file__), 'desktop.glade') + self.iface_cache = iface_cache widgets = gtk.glade.XML(gladefile, 'applist') self.window = widgets.get_widget('applist') tv = widgets.get_widget('treeview') - model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str) - apps = xdgutils.discover_existing_apps() - - for uri in apps: - itr = model.append() - model[itr][AppListBox.URI] = uri - - iface = iface_cache.get_interface(uri) - name = iface.get_name() - summary = iface.summary or 'No information available' - summary = summary[:1].capitalize() + summary[1:] + self.model = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str) - model[itr][AppListBox.NAME] = name - pixbuf = icon.load_icon(iface_cache.get_icon_path(iface)) - if pixbuf: - model[itr][AppListBox.ICON] = pixbuf - - model[itr][AppListBox.MARKUP] = '%s\n%s' % (name.replace('<', '<'), summary.replace('<', '<')) - - tv.set_model(model) + self.populate_model() + tv.set_model(self.model) tv.get_selection().set_mode(gtk.SELECTION_NONE) cell_icon = gtk.CellRendererPixbuf() @@ -51,7 +40,7 @@ class AppListBox: column = gtk.TreeViewColumn('Name', cell_text, markup = AppListBox.MARKUP) tv.append_column(column) - cell_actions = ActionsRenderer(tv) + cell_actions = ActionsRenderer(self, tv) actions_column = gtk.TreeViewColumn('Actions', cell_actions, uri = AppListBox.URI) tv.append_column(actions_column) @@ -62,13 +51,13 @@ class AppListBox: def motion(widget, mev): if mev.window == tv.get_bin_window(): - new_hover = (None, None) - pos = tv.get_path_at_pos(mev.x, mev.y) + new_hover = (None, None, None) + pos = tv.get_path_at_pos(int(mev.x), int(mev.y)) if pos: path, col, x, y = pos if col == actions_column: area = tv.get_cell_area(path, col) - iface = model[path][AppListBox.URI] + iface = self.model[path][AppListBox.URI] action = cell_actions.get_action(area, x, y) if action is not None: new_hover = (path, iface, action) @@ -78,23 +67,98 @@ class AppListBox: redraw_actions(cell_actions.hover[0]) tv.connect('motion-notify-event', motion) - model.set_sort_column_id(AppListBox.NAME, gtk.SORT_ASCENDING) + def leave(widget, lev): + redraw_actions(cell_actions.hover[0]) + cell_actions.hover = (None, None, None) + + tv.connect('leave-notify-event', leave) + + self.model.set_sort_column_id(AppListBox.NAME, gtk.SORT_ASCENDING) def response(box, resp): box.destroy() self.window.connect('response', response) + def populate_model(self): + self.apps = xdgutils.discover_existing_apps() + model = self.model + model.clear() + + for uri in self.apps: + itr = model.append() + model[itr][AppListBox.URI] = uri + + iface = self.iface_cache.get_interface(uri) + name = iface.get_name() + summary = iface.summary or 'No information available' + summary = summary[:1].capitalize() + summary[1:] + + model[itr][AppListBox.NAME] = name + pixbuf = icon.load_icon(self.iface_cache.get_icon_path(iface)) + if pixbuf: + model[itr][AppListBox.ICON] = pixbuf + + model[itr][AppListBox.MARKUP] = '%s\n%s' % (_pango_escape(name), _pango_escape(summary)) + + def action_run(self, uri): + subprocess.Popen(['0launch', '--', uri]) + + def action_help(self, uri): + from zeroinstall.injector import policy, reader, model + p = policy.Policy(uri) + policy.network_use = model.network_offline + if p.need_download(): + child = subprocess.Popen(['0launch', '-d', '--', uri]) + child.wait() + if child.returncode: + return + iface = self.iface_cache.get_interface(uri) + reader.update_from_cache(iface) + p.solve_with_downloads() + impl = p.solver.selections[iface] + assert impl, "Failed to choose an implementation of " + uri + help_dir = impl.metadata.get('doc-dir') + path = p.get_implementation_path(impl) + assert path, "Chosen implementation is not cached!" + if help_dir: + path = os.path.join(path, help_dir) + subprocess.Popen(['xdg-open', path]) + + def action_properties(self, uri): + subprocess.Popen(['0launch', '--gui', '--', uri]) + + def action_remove(self, uri): + name = self.iface_cache.get_interface(uri).get_name() + + box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_CANCEL, "") + box.set_markup("Remove %s from the menu?" % _pango_escape(name)) + box.add_button(gtk.STOCK_DELETE, gtk.RESPONSE_OK) + box.set_default_response(gtk.RESPONSE_OK) + resp = box.run() + box.destroy() + if resp == gtk.RESPONSE_OK: + path = self.apps[uri] + try: + os.unlink(path) + except Exception, ex: + box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Failed to remove %s: %s" % (path, ex)) + box.run() + box.destroy() + self.populate_model() + class ActionsRenderer(gtk.GenericCellRenderer): __gproperties__ = { "uri": (gobject.TYPE_STRING, "Text", "Text", "-", gobject.PARAM_READWRITE), } - def __init__(self, widget): + def __init__(self, applist, widget): "@param widget: widget used for style information" gtk.GenericCellRenderer.__init__(self) self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE) self.padding = 4 + self.applist = applist + self.size = 10 def stock_lookup(name): pixbuf = widget.render_icon(name, gtk.ICON_SIZE_BUTTON) @@ -143,10 +207,17 @@ class ActionsRenderer(gtk.GenericCellRenderer): b += 1 def on_activate(self, event, widget, path, background_area, cell_area, flags): - if event.type != gtk.gdk.BUTTON_PRESS_EVENT: + if event.type != gtk.gdk.BUTTON_PRESS: return False - action = self.get_action(cell_area, event.x, event.y) - print action + action = self.get_action(cell_area, event.x - cell_area.x, event.y - cell_area.y) + if action == 0: + self.applist.action_run(self.uri) + elif action == 1: + self.applist.action_help(self.uri) + elif action == 2: + self.applist.action_properties(self.uri) + elif action == 3: + self.applist.action_remove(self.uri) def get_action(self, area, x, y): lower = int(y > (area.height / 2)) * 2 -- 2.11.4.GIT