Import _ into each module rather than using a builtin
[zeroinstall.git] / zeroinstall / gtkui / addbox.py
blob68d14523641d90719b86a97764faf54f4d84bf05
1 """A GTK dialog which lets the user add a new application to their desktop."""
3 # Copyright (C) 2009, Thomas Leonard
4 # See the README file for details, or visit http://0install.net.
6 from zeroinstall import _
7 import os, sys
8 import gtk, gobject
9 import gtk.glade
11 from zeroinstall import SafeException
12 from zeroinstall.injector import model
13 from zeroinstall.injector.namespaces import XMLNS_IFACE
14 from zeroinstall.injector.iface_cache import iface_cache
16 _URI_LIST = 0
17 _UTF_16 = 1
19 _RESPONSE_PREV = 0
20 _RESPONSE_NEXT = 1
22 class AddBox:
23 """A dialog box which prompts the user to choose the program to be added."""
24 def __init__(self, interface_uri = None):
25 gladefile = os.path.join(os.path.dirname(__file__), 'desktop.glade')
27 widgets = gtk.glade.XML(gladefile, 'main')
28 self.window = widgets.get_widget('main')
29 self.set_keep_above(True)
31 def set_uri_ok(uri):
32 text = uri.get_text()
33 self.window.set_response_sensitive(_RESPONSE_NEXT, bool(text))
35 uri = widgets.get_widget('interface_uri')
36 about = widgets.get_widget('about')
37 icon_widget = widgets.get_widget('icon')
38 category = widgets.get_widget('category')
39 dialog_next = widgets.get_widget('dialog_next')
40 dialog_ok = widgets.get_widget('dialog_ok')
42 if interface_uri:
43 uri.set_text(interface_uri)
45 uri.connect('changed', set_uri_ok)
46 set_uri_ok(uri)
48 category.set_active(11)
50 def uri_dropped(eb, drag_context, x, y, selection_data, info, timestamp):
51 if info == _UTF_16:
52 import codecs
53 data = codecs.getdecoder('utf16')(selection_data.data)[0]
54 data = data.split('\n', 1)[0].strip()
55 else:
56 data = selection_data.data.split('\n', 1)[0].strip()
57 if self._sanity_check(data):
58 uri.set_text(data)
59 drag_context.finish(True, False, timestamp)
60 self.window.response(_RESPONSE_NEXT)
61 return True
62 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_HIGHLIGHT,
63 [('text/uri-list', 0, _URI_LIST),
64 ('text/x-moz-url', 0, _UTF_16)],
65 gtk.gdk.ACTION_COPY)
66 self.window.connect('drag-data-received', uri_dropped)
68 nb = widgets.get_widget('notebook1')
70 def update_details_page():
71 iface = iface_cache.get_interface(model.canonical_iface_uri(uri.get_text()))
72 about.set_text('%s - %s' % (iface.get_name(), iface.summary))
73 icon_path = iface_cache.get_icon_path(iface)
74 from zeroinstall.gtkui import icon
75 icon_pixbuf = icon.load_icon(icon_path)
76 if icon_pixbuf:
77 icon_widget.set_from_pixbuf(icon_pixbuf)
79 feed_category = None
80 for meta in iface.get_metadata(XMLNS_IFACE, 'category'):
81 feed_category = meta.content
82 break
83 if feed_category:
84 i = 0
85 for row in category.get_model():
86 if row[0].lower() == feed_category.lower():
87 category.set_active(i)
88 break
89 i += 1
90 self.window.set_response_sensitive(_RESPONSE_PREV, True)
92 def finish():
93 import xdgutils
94 iface = iface_cache.get_interface(model.canonical_iface_uri(uri.get_text()))
96 try:
97 icon_path = iface_cache.get_icon_path(iface)
98 xdgutils.add_to_menu(iface, icon_path, category.get_active_text())
99 except SafeException, ex:
100 box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, str(ex))
101 box.run()
102 box.destroy()
103 else:
104 self.window.destroy()
106 def response(box, resp):
107 if resp == _RESPONSE_NEXT:
108 iface = uri.get_text()
109 if not self._sanity_check(iface):
110 return
111 self.window.set_sensitive(False)
112 self.set_keep_above(False)
113 import subprocess
114 child = subprocess.Popen(['0launch',
115 '--gui', '--download-only',
116 '--', iface],
117 stdout = subprocess.PIPE,
118 stderr = subprocess.STDOUT)
119 errors = ['']
120 def output_ready(src, cond):
121 got = os.read(src.fileno(), 100)
122 if got:
123 errors[0] += got
124 else:
125 status = child.wait()
126 self.window.set_sensitive(True)
127 self.set_keep_above(True)
128 if status == 0:
129 update_details_page()
130 nb.next_page()
131 dialog_next.set_property('visible', False)
132 dialog_ok.set_property('visible', True)
133 dialog_ok.grab_focus()
134 else:
135 box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
136 _('Failed to run 0launch.\n') + errors[0])
137 box.run()
138 box.destroy()
139 return False
140 return True
141 gobject.io_add_watch(child.stdout,
142 gobject.IO_IN | gobject.IO_HUP,
143 output_ready)
144 elif resp == gtk.RESPONSE_OK:
145 finish()
146 elif resp == _RESPONSE_PREV:
147 dialog_next.set_property('visible', True)
148 dialog_ok.set_property('visible', False)
149 dialog_next.grab_focus()
150 nb.prev_page()
151 self.window.set_response_sensitive(_RESPONSE_PREV, False)
152 else:
153 box.destroy()
154 self.window.connect('response', response)
156 if len(sys.argv) > 1:
157 self.window.response(_RESPONSE_NEXT)
159 def set_keep_above(self, above):
160 if hasattr(self.window, 'set_keep_above'):
161 # This isn't very nice, but GNOME defaults to
162 # click-to-raise and in that mode drag-and-drop
163 # is useless without this...
164 self.window.set_keep_above(above)
166 def _sanity_check(self, uri):
167 if uri.endswith('.tar.bz2') or \
168 uri.endswith('.tar.gz') or \
169 uri.endswith('.exe') or \
170 uri.endswith('.rpm') or \
171 uri.endswith('.deb') or \
172 uri.endswith('.tgz'):
173 box = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
174 _("This URI (%s) looks like an archive, not a Zero Install feed. Make sure you're using the feed link!") % uri)
175 box.run()
176 box.destroy()
177 return False
178 return True