GUI's refresh feature works again. Passes test-gui suite.
[zeroinstall/zeroinstall-mseaborn.git] / zeroinstall / 0launch-gui / gui.py
blob09438d7070a409c7020647478ac7091417228022
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 import trust_box
63 return trust_box.confirm_trust(interface, sigs, iface_xml, parent = self.policy.checking or self.policy.window.window)
65 def report_error(self, ex):
66 dialog.alert(None, str(ex))
68 class GUIPolicy(Policy):
69 window = None
70 checking = None # GtkDialog ("Checking for updates...")
71 original_implementation = None
72 download_only = None
73 widgets = None # Glade
75 def __init__(self, interface, download_only, src = False, restrictions = None):
76 Policy.__init__(self, interface, GUIHandler(self), src = src)
77 self.solver.record_details = True
78 global policy
79 assert policy is None
80 policy = self
82 self.widgets = Template('main')
84 if restrictions:
85 for r in restrictions:
86 self.root_restrictions.append(r)
88 self.download_only = download_only
90 import mainwindow
91 self.window = mainwindow.MainWindow(download_only)
92 root = iface_cache.get_interface(self.root)
93 self.window.browser.set_root(root)
95 self.watchers.append(self.update_display)
97 def show_details(self):
98 """The checking box has disappeared. Should we show the details window, or
99 just run the program right now?"""
100 if self.checking.show_details_clicked.happened:
101 return True # User clicked on the Details button
102 if not self.ready:
103 return True # Not ready to start (can't find an implementation)
104 if self.versions_changed():
105 return True # Confirm that the new version should be used
106 if self.get_uncached_implementations():
107 return True # Need to download something; check first
108 return False
110 def store_icon(self, interface, stream):
111 Policy.store_icon(self, interface, stream)
112 if self.window:
113 self.window.browser.build_tree()
115 def update_display(self):
116 self.window.set_response_sensitive(gtk.RESPONSE_OK, self.ready)
118 def main(self, refresh):
119 if refresh:
120 # If we have feeds then treat this as an update check,
121 # even if we've never seen the main interface before.
122 # Used the first time the GUI is used, for example.
123 root = self.get_interface(self.root)
124 if root.name is not None or root.feeds:
125 self.checking = CheckingBox(root)
127 solved = tasks.Task(self.solve_with_downloads(force = refresh), "solve_with_downloads")
129 if self.checking:
130 self.checking.show()
132 error = None
133 blockers = [solved.finished, self.checking.show_details_clicked, self.checking.cancelled]
134 yield blockers
135 try:
136 tasks.check(blockers)
137 except Exception, ex:
138 error = ex
140 self.checking.updates_done(self.versions_changed())
142 show_details = self.show_details() or error
143 self.checking = None
144 if show_details:
145 self.window.show()
146 if error:
147 dialog.alert(self.window.window, "Failed to check for updates: %s" % ex)
148 yield []
149 else:
150 from zeroinstall.injector import selections
151 sels = selections.Selections(policy)
152 doc = sels.toDOM()
153 reply = doc.toxml('utf-8')
154 sys.stdout.write(('Length:%8x\n' % len(reply)) + reply)
155 self.window.destroy()
156 sys.exit(0) # Success
157 else:
158 self.window.show()
159 yield solved.finished
160 try:
161 tasks.check(solved.finished)
162 except Exception, ex:
163 import traceback
164 traceback.print_exc()
165 dialog.alert(self.window.window, str(ex))
166 yield []
168 def abort_all_downloads(self):
169 for dl in self.handler.monitored_downloads.values():
170 dl.abort()
172 def set_original_implementations(self):
173 assert self.original_implementation is None
174 self.original_implementation = policy.implementation.copy()
176 def versions_changed(self):
177 """Return whether we have now chosen any different implementations.
178 If so, we want to show the dialog to the user to confirm the new ones."""
179 if not self.ready:
180 return True
181 if not self.original_implementation:
182 return True # Shouldn't happen?
183 if len(self.original_implementation) != len(self.implementation):
184 return True
185 for iface in self.original_implementation:
186 old = self.original_implementation[iface]
187 if old is None:
188 return True
189 new = self.implementation.get(iface, None)
190 if new is None:
191 return True
192 if old.id != new.id:
193 return True
194 return False