Handy program for adding Zero Install applications to the GNOME menus.
[Zero2Desktop.git] / zero2desktop
blob45ed5681f2a948782b6220270356c4d558753aca
1 #!/usr/bin/env python
2 import os, sys, popen2, tempfile, shutil
3 import pygtk; pygtk.require('2.0')
4 import gtk, gobject
5 from StringIO import StringIO
6 import gtk.glade
8 from zeroinstall.injector.policy import Policy
9 from zeroinstall.injector.iface_cache import iface_cache
11 zero2desktop_uri = "http://0install.net/2007/interfaces/Zero2Desktop.xml"
13 gladefile = os.path.join(os.path.dirname(__file__), 'zero2desktop.glade')
15 def try_interface(uri):
16 errors = StringIO()
17 launcher = processes.PipeThroughCommand(
18 ["0launch", "-d", "-g", "--", uri],
19 None, errors)
20 try:
21 launcher.wait()
22 except processes.ChildError, ex:
23 if '(256)' not in str(ex):
24 rox.alert(str(ex))
25 except:
26 rox.report_exception()
27 else:
28 return True
29 return False
31 template = """[Desktop Entry]
32 # This file was generated by zero2desktop.
33 # See the Zero Install project for details: http://0install.net
34 Type=Application
35 Version=1.0
36 Name=%s
37 Comment=%s
38 Icon=%s
39 Exec=0launch -- %s
40 Categories=Application;%s
41 """
43 class MainWindow:
44 def __init__(self):
45 tree = gtk.glade.XML(gladefile, 'main')
46 self.window = tree.get_widget('main')
48 def set_uri_ok(uri):
49 text = uri.get_text()
50 self.window.set_response_sensitive(gtk.RESPONSE_OK, bool(text))
51 uri = tree.get_widget('interface_uri')
52 about = tree.get_widget('about')
53 icon = tree.get_widget('icon')
54 category = tree.get_widget('category')
56 uri.set_text('http://rox.sourceforge.net/2005/interfaces/ROX-Filer')
57 uri.connect('changed', set_uri_ok)
58 set_uri_ok(uri)
60 nb = tree.get_widget('notebook1')
62 def update_details_page():
63 iface = iface_cache.get_interface(uri.get_text())
64 about.set_text('%s - %s' % (iface.get_name(), iface.summary))
65 icon_path = iface_cache.get_icon_path(iface)
66 if icon_path:
67 try:
68 # Icon format must be PNG (to avoid attacks)
69 loader = gtk.gdk.PixbufLoader('png')
70 try:
71 loader.write(file(icon_path).read())
72 finally:
73 loader.close()
74 icon_pixbuf = loader.get_pixbuf()
75 except Exception, ex:
76 print >>sys.stderr, "Failed to load cached PNG icon: %s" % ex
77 else:
78 icon.set_from_pixbuf(icon_pixbuf)
80 #update_details_page()
81 #nb.next_page()
83 def finish():
84 iface = iface_cache.get_interface(uri.get_text())
85 tmpdir = tempfile.mkdtemp(prefix = 'zero2desktop-')
86 try:
87 desktop_name = os.path.join(tmpdir, 'zeroinstall-%s.desktop' % iface.get_name().lower())
88 desktop = file(desktop_name, 'w')
89 desktop.write(template % (iface.get_name(), iface.summary,
90 iface_cache.get_icon_path(iface),
91 iface.uri,
92 category.get_active_text()))
93 desktop.close()
94 status = os.spawnlp(os.P_WAIT, 'xdg-desktop-menu', 'xdg-desktop-menu', 'install', desktop_name)
95 finally:
96 shutil.rmtree(tmpdir)
97 if status:
98 box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
99 'Failed to run xdg-desktop-menu (error code %d)' % status)
100 box.run()
101 box.destroy()
102 else:
103 self.window.destroy()
105 def response(box, resp):
106 if resp == gtk.RESPONSE_OK:
107 page = nb.get_current_page()
108 if page == 0:
109 iface = uri.get_text()
110 self.window.set_sensitive(False)
111 child = popen2.Popen3(['0launch',
112 '--gui', '--download-only',
113 '--', iface])
114 child.tochild.close()
115 def output_ready(src, cond):
116 got = os.read(src.fileno(), 100)
117 if not got:
118 status = child.wait()
119 self.window.set_sensitive(True)
120 if status == 0:
121 update_details_page()
122 nb.next_page()
123 return False
124 return True
125 gobject.io_add_watch(child.fromchild,
126 gobject.IO_IN | gobject.IO_HUP,
127 output_ready)
128 else:
129 finish()
130 elif resp == 0:
131 nb.previous_page()
132 else:
133 box.destroy()
134 self.window.connect('response', response)
136 main = MainWindow()
137 main.window.connect('destroy', lambda box: gtk.main_quit())
138 gtk.main()