1 import gtk
, os
, gobject
, sys
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
, pretty_size
11 from checking
import CheckingBox
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
22 def __init__(self
, root
):
23 self
.widgets
= gtk
.glade
.XML(gladefile
, root
)
26 def get_widget(self
, name
= None):
29 widget
= self
.widgets
.get_widget(name
)
30 assert widget
, "Widget '%s' not found in glade file '%s'" % (name
, gladefile
)
33 class GUIHandler(handler
.Handler
):
34 dl_callbacks
= None # Download -> [ callback ]
38 def __init__(self
, policy
):
39 handler
.Handler
.__init
__(self
)
42 def downloads_changed(self
):
43 if self
.monitored_downloads
and self
.pulse
is None:
45 if self
.policy
.checking
:
46 progress
= self
.policy
.checking
.progress
48 progress
= self
.policy
.window
.progress
51 done
= total
= self
.total_bytes_downloaded
# Completed downloads
52 n_downloads
= self
.n_completed_downloads
53 # Now add downloads in progress...
54 for x
in self
.monitored_downloads
.values():
55 if x
.status
!= download
.download_fetching
: continue
59 so_far
= x
.get_bytes_downloaded_so_far()
60 total
+= x
.expected_size
or max(4096, so_far
) # Guess about 4K for feeds/icons
63 progress_text
= '%s / %s' % (pretty_size(done
), pretty_size(total
))
65 progress
.set_text('Downloading one file (%s)' % progress_text
)
67 progress
.set_text('Downloading %d files (%s)' % (n_downloads
, progress_text
))
69 if total
== 0 or (n_downloads
< 2 and not any_known
):
72 progress
.set_fraction(float(done
) / total
)
76 self
.pulse
= gobject
.timeout_add(50, pulse
)
77 self
.policy
.window
.progress
.show()
78 elif len(self
.monitored_downloads
) == 0:
80 self
.n_completed_downloads
= 0
81 self
.total_bytes_downloaded
= 0
85 gobject
.source_remove(self
.pulse
)
86 self
.policy
.window
.progress
.hide()
89 if self
.policy
.checking
:
90 self
.policy
.checking
.updates_done(self
.policy
.versions_changed())
92 def confirm_trust_keys(self
, interface
, sigs
, iface_xml
):
93 if self
.policy
.checking
:
94 # Switch to main view if there are keys to be confirmed
95 self
.policy
.checking
.updates_done(True)
96 self
.policy
.window
.show()
98 return trust_box
.confirm_trust(interface
, sigs
, iface_xml
, parent
= self
.policy
.window
.window
)
100 def report_error(self
, ex
):
101 dialog
.alert(None, str(ex
))
103 class GUIPolicy(Policy
):
105 checking
= None # GtkDialog ("Checking for updates...")
106 original_implementation
= None
108 widgets
= None # Glade
110 def __init__(self
, interface
, download_only
, src
= False, restrictions
= None):
111 Policy
.__init
__(self
, interface
, GUIHandler(self
), src
= src
)
112 self
.solver
.record_details
= True
114 assert policy
is None
117 self
.widgets
= Template('main')
120 for r
in restrictions
:
121 self
.root_restrictions
.append(r
)
123 self
.download_only
= download_only
126 self
.window
= mainwindow
.MainWindow(download_only
)
127 root
= iface_cache
.get_interface(self
.root
)
128 self
.window
.browser
.set_root(root
)
130 self
.watchers
.append(self
.update_display
)
132 def show_details(self
):
133 """The checking box has disappeared. Should we show the details window, or
134 just run the program right now?"""
135 if self
.checking
.show_details_clicked
.happened
:
136 return True # User clicked on the Details button
138 return True # Not ready to start (can't find an implementation)
139 if self
.versions_changed():
140 return True # Confirm that the new version should be used
141 if self
.get_uncached_implementations():
142 return True # Need to download something; check first
145 def update_display(self
):
146 self
.window
.set_response_sensitive(gtk
.RESPONSE_OK
, self
.ready
)
148 def main(self
, refresh
):
150 # If we have feeds then treat this as an update check,
151 # even if we've never seen the main interface before.
152 # Used the first time the GUI is used, for example.
153 root
= iface_cache
.get_interface(self
.root
)
154 if root
.name
is not None or root
.feeds
:
155 self
.checking
= CheckingBox(root
)
157 solved
= self
.solve_with_downloads(force
= refresh
)
163 blockers
= [solved
, self
.checking
.show_details_clicked
, self
.checking
.cancelled
]
166 tasks
.check(blockers
)
167 except Exception, ex
:
170 if not (self
.checking
.show_details_clicked
.happened
or self
.checking
.cancelled
.happened
):
171 self
.checking
.updates_done(self
.versions_changed())
172 blockers
= tasks
.TimeoutBlocker(0.5, "checking result timeout")
174 tasks
.check(blockers
)
175 self
.checking
.destroy()
177 show_details
= self
.show_details() or error
182 dialog
.alert(self
.window
.window
, "Failed to check for updates: %s" % ex
)
185 from zeroinstall
.injector
import selections
186 sels
= selections
.Selections(policy
)
188 reply
= doc
.toxml('utf-8')
189 sys
.stdout
.write(('Length:%8x\n' % len(reply
)) + reply
)
190 self
.window
.destroy()
191 sys
.exit(0) # Success
197 except Exception, ex
:
199 traceback
.print_exc()
200 dialog
.alert(self
.window
.window
, str(ex
))
203 def abort_all_downloads(self
):
204 for dl
in self
.handler
.monitored_downloads
.values():
207 def set_original_implementations(self
):
208 assert self
.original_implementation
is None
209 self
.original_implementation
= policy
.implementation
.copy()
211 def versions_changed(self
):
212 """Return whether we have now chosen any different implementations.
213 If so, we want to show the dialog to the user to confirm the new ones."""
216 if not self
.original_implementation
:
217 return True # Shouldn't happen?
218 if len(self
.original_implementation
) != len(self
.implementation
):
220 for iface
in self
.original_implementation
:
221 old
= self
.original_implementation
[iface
]
224 new
= self
.implementation
.get(iface
, None)