Support feeds with multiple targets.
[zeroinstall.git] / gui.py
blobc405a58d7fd3bae963f3c6ae65a5f95cfd2bbae2
1 import gtk, os, gobject, sys
3 from zeroinstall.injector.policy import Policy
4 from zeroinstall.injector import download
5 from zeroinstall.injector.model import SafeException
6 from zeroinstall.injector.reader import InvalidInterface
7 import dialog
8 from checking import CheckingBox
10 version = '0.15'
12 # Singleton Policy
13 policy = None
15 class GUIPolicy(Policy):
16 window = None
17 pulse = None
18 monitored_downloads = None
19 checking = None # GtkDialog ("Checking for updates...")
20 original_implementation = None
21 download_only = None
22 prog_args = None
23 main_exec = None
25 def __init__(self, interface, prog_args, download_only, refresh, main):
26 Policy.__init__(self, interface)
27 global policy
28 assert policy is None
29 policy = self
30 self.main_exec = main
31 self.prog_args = prog_args
32 self.download_only = download_only
33 self.monitored_downloads = []
35 import mainwindow
36 self.window = mainwindow.MainWindow(prog_args, download_only)
37 root = policy.get_interface(policy.root)
38 self.window.browser.set_root(root)
40 if refresh:
41 if root.name is not None:
42 self.checking = CheckingBox(root)
44 self.refresh_all(force = False)
46 def show_details(self):
47 """The checking box has disappeared. Should we show the details window, or
48 just run the program right now?"""
49 if self.checking.show_details:
50 return True # User clicked on the Details button
51 if not self.ready:
52 return True # Not ready to start (can't find an implementation)
53 if self.versions_changed():
54 return True # Confirm that the new version should be used
55 if self.get_uncached_implementations():
56 return True # Need to download something; check first
57 return False
59 def monitor_download(self, dl):
60 self.monitored_downloads.append(dl)
62 error_stream = dl.start()
63 def error_ready(src, cond):
64 got = os.read(src.fileno(), 100)
65 if not got:
66 error_stream.close()
67 self.monitored_downloads.remove(dl)
68 if len(self.monitored_downloads) == 0:
69 gobject.source_remove(self.pulse)
70 self.window.progress.hide()
71 self.pulse = None
72 try:
73 data = dl.error_stream_closed()
74 self.check_signed_data(dl, data)
75 except download.DownloadError, ex:
76 dialog.alert(self.window,
77 "Error downloading interface '%s':\n\n%s" %
78 (dl.interface.uri, ex))
79 except InvalidInterface, ex:
80 dialog.alert(self.window,
81 "Syntax error in downloaded interface '%s':\n\n%s" %
82 (dl.interface.uri, ex))
83 except SafeException, ex:
84 dialog.alert(self.window,
85 "Error updating interface '%s':\n\n%s" %
86 (dl.interface.uri, ex))
88 if len(self.monitored_downloads) == 0 and self.checking:
89 self.checking.updates_done(self.versions_changed())
90 return False
91 dl.error_stream_data(got)
92 return True
94 gobject.io_add_watch(error_stream,
95 gobject.IO_IN | gobject.IO_HUP,
96 error_ready)
98 if self.pulse is None:
99 def pulse():
100 if self.checking:
101 self.checking.progress.pulse()
102 else:
103 self.window.progress.pulse()
104 return True
105 self.pulse = gobject.timeout_add(50, pulse)
106 self.window.progress.show()
108 def recalculate(self):
109 Policy.recalculate(self)
110 try:
111 self.ready
112 except:
113 self.ready = True
114 print >>sys.stderr, "Your version of the injector is very old. " \
115 "Try upgrading (http://0install.net/injector.html)"
116 else:
117 self.window.set_response_sensitive(gtk.RESPONSE_OK, self.ready)
119 def confirm_trust_keys(self, interface, sigs, iface_xml):
120 import trust_box
121 trust_box.confirm_trust(interface, sigs, iface_xml)
123 def main(self):
124 if self.checking:
125 self.checking.show()
126 if not self.monitored_downloads:
127 self.checking.updates_done(self.versions_changed())
128 dialog.wait_for_no_windows()
129 show_details = self.show_details()
130 self.checking = None
131 if show_details:
132 self.window.show()
133 gtk.main()
134 else:
135 import download_box
136 download_box.download_with_gui(self.window, self.prog_args, main = self.main_exec,
137 run_afterwards = not self.download_only)
138 else:
139 self.window.show()
140 gtk.main()
142 def get_best_source(self, impl):
143 """Return the best download source for this implementation."""
144 if impl.download_sources:
145 return impl.download_sources[0]
146 return None
148 def refresh_all(self, force = True):
149 if hasattr(Policy, 'refresh_all'):
150 Policy.refresh_all(self, force)
151 else:
152 # XXX: Remove this. Moved to Policy.
153 for x in self.walk_interfaces():
154 self.begin_iface_download(x, force)
156 def abort_all_downloads(self):
157 for x in self.monitored_downloads[:]:
158 x.abort()
160 def set_original_implementations(self):
161 assert self.original_implementation is None
162 self.original_implementation = policy.implementation.copy()
164 def versions_changed(self):
165 """Return whether we have now chosen any different implementations.
166 If so, we want to show the dialog to the user to confirm the new ones."""
167 if not self.ready:
168 return True
169 if not self.original_implementation:
170 return True # Shouldn't happen?
171 if len(self.original_implementation) != len(self.implementation):
172 return True
173 for iface in self.original_implementation:
174 old = self.original_implementation[iface]
175 if old is None:
176 return True
177 new = self.implementation.get(iface, None)
178 if new is None:
179 return True
180 if old.id != new.id:
181 return True
182 return False
184 def pretty_size(size):
185 if size is None:
186 return '?'
187 if size < 2048:
188 return '%d bytes' % size
189 size = float(size)
190 for unit in ('Kb', 'Mb', 'Gb', 'Tb'):
191 size /= 1024
192 if size < 2048:
193 break
194 return '%.1f %s' % (size, unit)