2 import os
, sys
, popen2
, tempfile
, shutil
, optparse
3 import pygtk
; pygtk
.require('2.0')
5 from StringIO
import StringIO
8 from zeroinstall
.injector
.policy
import Policy
9 from zeroinstall
.injector
.iface_cache
import iface_cache
12 parser
= optparse
.OptionParser(usage
="usage: %prog [options] [interface]")
13 parser
.add_option("-V", "--version", help="display version information", action
='store_true')
14 (options
, args
) = parser
.parse_args()
17 print "0publish-gui (zero-install) " + version
18 print "Copyright (C) 2007 Thomas Leonard"
19 print "This program comes with ABSOLUTELY NO WARRANTY,"
20 print "to the extent permitted by law."
21 print "You may redistribute copies of this program"
22 print "under the terms of the GNU General Public License."
23 print "For more information about these matters, see the file named COPYING."
26 zero2desktop_uri
= "http://0install.net/2007/interfaces/Zero2Desktop.xml"
28 gladefile
= os
.path
.join(os
.path
.dirname(__file__
), 'zero2desktop.glade')
30 # XDG_UTILS should go at the end, so that the local copy is used first
31 os
.environ
['PATH'] += ':' + os
.environ
['XDG_UTILS']
33 template
= """[Desktop Entry]
34 # This file was generated by zero2desktop.
35 # See the Zero Install project for details: http://0install.net
41 Exec=0launch -- %s %%f
42 Categories=Application;%s
52 tree
= gtk
.glade
.XML(gladefile
, 'main')
53 self
.window
= tree
.get_widget('main')
57 self
.window
.set_response_sensitive(RESPONSE_NEXT
, bool(text
))
59 drop_uri
= tree
.get_widget('drop_uri')
60 uri
= tree
.get_widget('interface_uri')
61 about
= tree
.get_widget('about')
62 icon
= tree
.get_widget('icon')
63 category
= tree
.get_widget('category')
64 dialog_next
= tree
.get_widget('dialog_next')
65 dialog_ok
= tree
.get_widget('dialog_ok')
68 uri
.set_text(sys
.argv
[1])
70 uri
.connect('changed', set_uri_ok
)
73 category
.set_active(11)
75 def uri_dropped(eb
, drag_context
, x
, y
, selection_data
, info
, timestamp
):
78 data
= codecs
.getdecoder('utf16')(selection_data
.data
)[0]
79 data
= data
.split('\n', 1)[0].strip()
81 data
= selection_data
.data
.split('\n', 1)[0].strip()
83 drag_context
.finish(True, False, timestamp
)
84 self
.window
.response(RESPONSE_NEXT
)
86 drop_uri
.drag_dest_set(gtk
.DEST_DEFAULT_MOTION | gtk
.DEST_DEFAULT_DROP | gtk
.DEST_DEFAULT_HIGHLIGHT
,
87 [('text/uri-list', 0, URI_LIST
),
88 ('text/x-moz-url', 0, UTF_16
)],
90 drop_uri
.connect('drag-data-received', uri_dropped
)
92 nb
= tree
.get_widget('notebook1')
94 def update_details_page():
95 iface
= iface_cache
.get_interface(uri
.get_text())
96 about
.set_text('%s - %s' % (iface
.get_name(), iface
.summary
))
97 icon_path
= iface_cache
.get_icon_path(iface
)
100 # Icon format must be PNG (to avoid attacks)
101 loader
= gtk
.gdk
.PixbufLoader('png')
103 loader
.write(file(icon_path
).read())
106 icon_pixbuf
= loader
.get_pixbuf()
107 except Exception, ex
:
108 print >>sys
.stderr
, "Failed to load cached PNG icon: %s" % ex
110 icon
.set_from_pixbuf(icon_pixbuf
)
113 iface
= iface_cache
.get_interface(uri
.get_text())
114 tmpdir
= tempfile
.mkdtemp(prefix
= 'zero2desktop-')
116 desktop_name
= os
.path
.join(tmpdir
, 'zeroinstall-%s.desktop' % iface
.get_name().lower())
117 desktop
= file(desktop_name
, 'w')
118 desktop
.write(template
% (iface
.get_name(), iface
.summary
,
119 iface_cache
.get_icon_path(iface
),
121 category
.get_active_text()))
123 status
= os
.spawnlp(os
.P_WAIT
, 'xdg-desktop-menu', 'xdg-desktop-menu', 'install', desktop_name
)
125 shutil
.rmtree(tmpdir
)
127 box
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_MODAL
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
,
128 'Failed to run xdg-desktop-menu (error code %d)' % status
)
132 self
.window
.destroy()
134 def response(box
, resp
):
135 if resp
== RESPONSE_NEXT
:
136 iface
= uri
.get_text()
137 self
.window
.set_sensitive(False)
138 child
= popen2
.Popen4(['0launch',
139 '--gui', '--download-only',
141 child
.tochild
.close()
143 def output_ready(src
, cond
):
144 got
= os
.read(src
.fileno(), 100)
148 status
= child
.wait()
149 self
.window
.set_sensitive(True)
151 update_details_page()
153 dialog_next
.set_property('visible', False)
154 dialog_ok
.set_property('visible', True)
155 dialog_ok
.grab_focus()
157 box
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_MODAL
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
,
158 'Failed to run 0launch.\n' + errors
[0])
163 gobject
.io_add_watch(child
.fromchild
,
164 gobject
.IO_IN | gobject
.IO_HUP
,
166 elif resp
== gtk
.RESPONSE_OK
:
172 self
.window
.connect('response', response
)
174 if len(sys
.argv
) > 1:
175 self
.window
.response(RESPONSE_NEXT
)
178 main
.window
.connect('destroy', lambda box
: gtk
.main_quit())