1 """A GTK dialog which lets the user add a new application to their desktop."""
2 # Copyright (C) 2008, Thomas Leonard
3 # See the README file for details, or visit http://0install.net.
8 import tempfile
, shutil
10 from zeroinstall
import SafeException
11 from zeroinstall
.injector
.namespaces
import XMLNS_IFACE
12 from zeroinstall
.injector
.iface_cache
import iface_cache
21 """A dialog box which prompts the user to choose the program to be added."""
22 def __init__(self
, interface_uri
= None):
23 gladefile
= os
.path
.join(os
.path
.dirname(__file__
), 'desktop.glade')
25 widgets
= gtk
.glade
.XML(gladefile
, 'main')
26 self
.window
= widgets
.get_widget('main')
30 self
.window
.set_response_sensitive(_RESPONSE_NEXT
, bool(text
))
32 drop_uri
= widgets
.get_widget('drop_uri')
33 uri
= widgets
.get_widget('interface_uri')
34 about
= widgets
.get_widget('about')
35 icon
= widgets
.get_widget('icon')
36 category
= widgets
.get_widget('category')
37 dialog_next
= widgets
.get_widget('dialog_next')
38 dialog_ok
= widgets
.get_widget('dialog_ok')
41 uri
.set_text(interface_uri
)
43 uri
.connect('changed', set_uri_ok
)
46 category
.set_active(11)
48 def uri_dropped(eb
, drag_context
, x
, y
, selection_data
, info
, timestamp
):
51 data
= codecs
.getdecoder('utf16')(selection_data
.data
)[0]
52 data
= data
.split('\n', 1)[0].strip()
54 data
= selection_data
.data
.split('\n', 1)[0].strip()
56 drag_context
.finish(True, False, timestamp
)
57 self
.window
.response(_RESPONSE_NEXT
)
59 self
.window
.drag_dest_set(gtk
.DEST_DEFAULT_MOTION | gtk
.DEST_DEFAULT_DROP | gtk
.DEST_DEFAULT_HIGHLIGHT
,
60 [('text/uri-list', 0, _URI_LIST
),
61 ('text/x-moz-url', 0, _UTF_16
)],
63 self
.window
.connect('drag-data-received', uri_dropped
)
65 nb
= widgets
.get_widget('notebook1')
67 def update_details_page():
68 iface
= iface_cache
.get_interface(uri
.get_text())
69 about
.set_text('%s - %s' % (iface
.get_name(), iface
.summary
))
70 icon_path
= iface_cache
.get_icon_path(iface
)
73 # Icon format must be PNG (to avoid attacks)
74 loader
= gtk
.gdk
.PixbufLoader('png')
76 loader
.write(file(icon_path
).read())
79 icon_pixbuf
= loader
.get_pixbuf()
81 print >>sys
.stderr
, "Failed to load cached PNG icon: %s" % ex
83 icon
.set_from_pixbuf(icon_pixbuf
)
86 for meta
in iface
.get_metadata(XMLNS_IFACE
, 'category'):
87 feed_category
= meta
.content
91 for row
in category
.get_model():
92 if row
[0].lower() == feed_category
.lower():
93 category
.set_active(i
)
96 self
.window
.set_response_sensitive(_RESPONSE_PREV
, True)
100 iface
= iface_cache
.get_interface(uri
.get_text())
103 icon_path
= iface_cache
.get_icon_path(iface
)
104 xdgutils
.add_to_menu(iface
, icon_path
, category
.get_active_text())
105 except SafeException
, ex
:
106 box
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_MODAL
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
, str(ex
))
110 self
.window
.destroy()
112 def response(box
, resp
):
113 if resp
== _RESPONSE_NEXT
:
114 iface
= uri
.get_text()
115 self
.window
.set_sensitive(False)
116 self
.set_keep_above(False)
118 child
= popen2
.Popen4(['0launch',
119 '--gui', '--download-only',
121 child
.tochild
.close()
123 def output_ready(src
, cond
):
124 got
= os
.read(src
.fileno(), 100)
128 status
= child
.wait()
129 self
.window
.set_sensitive(True)
130 self
.set_keep_above(True)
132 update_details_page()
134 dialog_next
.set_property('visible', False)
135 dialog_ok
.set_property('visible', True)
136 dialog_ok
.grab_focus()
138 box
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_MODAL
, gtk
.MESSAGE_ERROR
, gtk
.BUTTONS_OK
,
139 'Failed to run 0launch.\n' + errors
[0])
144 gobject
.io_add_watch(child
.fromchild
,
145 gobject
.IO_IN | gobject
.IO_HUP
,
147 elif resp
== gtk
.RESPONSE_OK
:
149 elif resp
== _RESPONSE_PREV
:
150 dialog_next
.set_property('visible', True)
151 dialog_ok
.set_property('visible', False)
152 dialog_next
.grab_focus()
154 self
.window
.set_response_sensitive(_RESPONSE_PREV
, False)
157 self
.window
.connect('response', response
)
159 if len(sys
.argv
) > 1:
160 self
.window
.response(_RESPONSE_NEXT
)
162 def set_keep_above(self
, above
):
163 if hasattr(self
.window
, 'set_keep_above'):
164 # This isn't very nice, but GNOME defaults to
165 # click-to-raise and in that mode drag-and-drop
166 # is useless without this...
167 self
.window
.set_keep_above(above
)