When checking for updates, switch to full mode if we need to confirm a key.
[zeroinstall.git] / zeroinstall / 0launch-gui / gui.py
blobe98240d9bdd3b406a646864828c7050b3b58896d
1 import gtk, os, gobject, sys
2 import gtk.glade
4 from zeroinstall.injector.iface_cache import iface_cache
5 from zeroinstall.injector.policy import Policy
6 from zeroinstall.injector import download, handler
7 from zeroinstall.injector.model import SafeException
8 from zeroinstall.injector.reader import InvalidInterface
9 from zeroinstall.support import tasks
10 import dialog
11 from checking import CheckingBox
13 version = '0.31'
15 # Singleton Policy
16 policy = None
18 gladefile = os.path.join(os.path.dirname(__file__), 'zero-install.glade')
20 # Wrapped for glade widget tree that throws a sensible exception if the widget isn't found
21 class Template:
22 def __init__(self, root):
23 self.widgets = gtk.glade.XML(gladefile, root)
24 self.root = root
26 def get_widget(self, name = None):
27 if not name:
28 name = self.root
29 widget = self.widgets.get_widget(name)
30 assert widget, "Widget '%s' not found in glade file '%s'" % (name, gladefile)
31 return widget
33 class GUIHandler(handler.Handler):
34 dl_callbacks = None # Download -> [ callback ]
35 pulse = None
36 policy = None
38 def __init__(self, policy):
39 handler.Handler.__init__(self)
40 self.policy = policy
42 def downloads_changed(self):
43 if self.monitored_downloads and self.pulse is None:
44 def pulse():
45 if self.policy.checking:
46 self.policy.checking.progress.pulse()
47 else:
48 self.policy.window.progress.pulse()
49 return True
50 self.pulse = gobject.timeout_add(50, pulse)
51 self.policy.window.progress.show()
52 elif len(self.monitored_downloads) == 0:
53 if self.pulse:
54 gobject.source_remove(self.pulse)
55 self.policy.window.progress.hide()
56 self.pulse = None
58 if self.policy.checking:
59 self.policy.checking.updates_done(self.policy.versions_changed())
61 def confirm_trust_keys(self, interface, sigs, iface_xml):
62 if self.policy.checking:
63 # Switch to main view if there are keys to be confirmed
64 self.policy.checking.updates_done(True)
65 self.policy.window.show()
66 import trust_box
67 return trust_box.confirm_trust(interface, sigs, iface_xml, parent = self.policy.window.window)
69 def report_error(self, ex):
70 dialog.alert(None, str(ex))
72 class GUIPolicy(Policy):
73 window = None
74 checking = None # GtkDialog ("Checking for updates...")
75 original_implementation = None
76 download_only = None
77 widgets = None # Glade
79 def __init__(self, interface, download_only, src = False, restrictions = None):
80 Policy.__init__(self, interface, GUIHandler(self), src = src)
81 self.solver.record_details = True
82 global policy
83 assert policy is None
84 policy = self
86 self.widgets = Template('main')
88 if restrictions:
89 for r in restrictions:
90 self.root_restrictions.append(r)
92 self.download_only = download_only
94 import mainwindow
95 self.window = mainwindow.MainWindow(download_only)
96 root = iface_cache.get_interface(self.root)
97 self.window.browser.set_root(root)
99 self.watchers.append(self.update_display)
101 def show_details(self):
102 """The checking box has disappeared. Should we show the details window, or
103 just run the program right now?"""
104 if self.checking.show_details_clicked.happened:
105 return True # User clicked on the Details button
106 if not self.ready:
107 return True # Not ready to start (can't find an implementation)
108 if self.versions_changed():
109 return True # Confirm that the new version should be used
110 if self.get_uncached_implementations():
111 return True # Need to download something; check first
112 return False
114 def update_display(self):
115 self.window.set_response_sensitive(gtk.RESPONSE_OK, self.ready)
117 def main(self, refresh):
118 if refresh:
119 # If we have feeds then treat this as an update check,
120 # even if we've never seen the main interface before.
121 # Used the first time the GUI is used, for example.
122 root = iface_cache.get_interface(self.root)
123 if root.name is not None or root.feeds:
124 self.checking = CheckingBox(root)
126 solved = self.solve_with_downloads(force = refresh)
128 if self.checking:
129 self.checking.show()
131 error = None
132 blockers = [solved, self.checking.show_details_clicked, self.checking.cancelled]
133 yield blockers
134 try:
135 tasks.check(blockers)
136 except Exception, ex:
137 error = ex
139 if not (self.checking.show_details_clicked.happened or self.checking.cancelled.happened):
140 self.checking.updates_done(self.versions_changed())
141 blockers = tasks.TimeoutBlocker(0.5, "checking result timeout")
142 yield blockers
143 tasks.check(blockers)
144 self.checking.destroy()
146 show_details = self.show_details() or error
147 self.checking = None
148 if show_details:
149 self.window.show()
150 if error:
151 dialog.alert(self.window.window, "Failed to check for updates: %s" % ex)
152 yield []
153 else:
154 from zeroinstall.injector import selections
155 sels = selections.Selections(policy)
156 doc = sels.toDOM()
157 reply = doc.toxml('utf-8')
158 sys.stdout.write(('Length:%8x\n' % len(reply)) + reply)
159 self.window.destroy()
160 sys.exit(0) # Success
161 else:
162 self.window.show()
163 yield solved
164 try:
165 tasks.check(solved)
166 except Exception, ex:
167 import traceback
168 traceback.print_exc()
169 dialog.alert(self.window.window, str(ex))
170 yield []
172 def abort_all_downloads(self):
173 for dl in self.handler.monitored_downloads.values():
174 dl.abort()
176 def set_original_implementations(self):
177 assert self.original_implementation is None
178 self.original_implementation = policy.implementation.copy()
180 def versions_changed(self):
181 """Return whether we have now chosen any different implementations.
182 If so, we want to show the dialog to the user to confirm the new ones."""
183 if not self.ready:
184 return True
185 if not self.original_implementation:
186 return True # Shouldn't happen?
187 if len(self.original_implementation) != len(self.implementation):
188 return True
189 for iface in self.original_implementation:
190 old = self.original_implementation[iface]
191 if old is None:
192 return True
193 new = self.implementation.get(iface, None)
194 if new is None:
195 return True
196 if old.id != new.id:
197 return True
198 return False