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
8 from checking
import CheckingBox
15 class GUIPolicy(Policy
):
18 monitored_downloads
= None
19 checking
= None # GtkDialog ("Checking for updates...")
20 original_implementation
= None
24 dl_callbacks
= {} # Download -> [ callback ]
26 def __init__(self
, interface
, prog_args
, download_only
, refresh
, main
, src
= False, restrictions
= None):
28 Policy
.__init
__(self
, interface
, src
= src
)
30 Policy
.__init
__(self
, interface
) # For older versions
35 for r
in restrictions
:
36 self
.root_restrictions
.append(r
)
40 self
.prog_args
= prog_args
41 self
.download_only
= download_only
42 self
.monitored_downloads
= []
45 self
.window
= mainwindow
.MainWindow(prog_args
, download_only
)
46 root
= policy
.get_interface(policy
.root
)
47 self
.window
.browser
.set_root(root
)
50 # If we have feeds then treat this as a refresh,
51 # even if we've never seen the main interface before.
52 # Used the first time the GUI is used, for example.
53 if root
.name
is not None or root
.feeds
:
54 self
.checking
= CheckingBox(root
)
56 self
.refresh_all(force
= False)
58 def show_details(self
):
59 """The checking box has disappeared. Should we show the details window, or
60 just run the program right now?"""
61 if self
.checking
.show_details
:
62 return True # User clicked on the Details button
64 return True # Not ready to start (can't find an implementation)
65 if self
.versions_changed():
66 return True # Confirm that the new version should be used
67 if self
.get_uncached_implementations():
68 return True # Need to download something; check first
71 def get_download(self
, url
, force
= False):
72 # For injector >= 0.20
74 for dl
in self
.monitored_downloads
:
83 dl
= download
.Download(url
)
84 self
.monitor_download(dl
)
87 def add_dl_callback(self
, url
, callback
):
88 """If url is being downloaded now, call callback() when it completes.
89 Otherwise, call callback() now."""
90 for dl
in self
.monitored_downloads
:
92 callbacks
= self
.dl_callbacks
.get(dl
, [])
93 callbacks
.append(callback
)
94 self
.dl_callbacks
[dl
] = callbacks
98 def monitor_download(self
, dl
):
99 from zeroinstall
.injector
import download
100 if hasattr(dl
, 'interface'):
103 name
= os
.path
.basename(dl
.url
)
105 self
.monitored_downloads
.append(dl
)
107 error_stream
= dl
.start()
108 def error_ready(src
, cond
):
109 got
= os
.read(src
.fileno(), 100)
112 self
.monitored_downloads
.remove(dl
)
113 if len(self
.monitored_downloads
) == 0:
114 gobject
.source_remove(self
.pulse
)
115 self
.window
.progress
.hide()
118 data
= dl
.error_stream_closed()
120 if isinstance(dl
, download
.InterfaceDownload
):
121 self
.check_signed_data(dl
, data
)
122 elif isinstance(dl
, download
.ImplementationDownload
):
123 self
.add_to_cache(dl
.source
, data
)
124 if hasattr(download
, 'IconDownload') and \
125 isinstance(dl
, download
.IconDownload
):
127 self
.window
.browser
.build_tree()
128 except download
.DownloadError
, ex
:
129 dialog
.alert(self
.window
,
130 "Error downloading '%s':\n\n%s" %
132 except InvalidInterface
, ex
:
133 dialog
.alert(self
.window
,
134 "Syntax error in downloaded interface '%s':\n\n%s" %
136 except SafeException
, ex
:
137 dialog
.alert(self
.window
,
138 "Error fetching '%s':\n\n%s" %
141 for cb
in self
.dl_callbacks
.get(dl
, []):
144 if len(self
.monitored_downloads
) == 0 and self
.checking
:
145 self
.checking
.updates_done(self
.versions_changed())
147 dl
.error_stream_data(got
)
150 gobject
.io_add_watch(error_stream
,
151 gobject
.IO_IN | gobject
.IO_HUP
,
154 if self
.pulse
is None:
157 self
.checking
.progress
.pulse()
159 self
.window
.progress
.pulse()
161 self
.pulse
= gobject
.timeout_add(50, pulse
)
162 self
.window
.progress
.show()
164 def store_icon(self
, interface
, stream
):
165 Policy
.store_icon(self
, interface
, stream
)
167 self
.window
.browser
.build_tree()
169 def recalculate(self
):
170 Policy
.recalculate(self
)
175 print >>sys
.stderr
, "Your version of the injector is very old. " \
176 "Try upgrading (http://0install.net/injector.html)"
178 self
.window
.set_response_sensitive(gtk
.RESPONSE_OK
, self
.ready
)
180 def confirm_trust_keys(self
, interface
, sigs
, iface_xml
):
182 trust_box
.confirm_trust(interface
, sigs
, iface_xml
)
187 if not self
.monitored_downloads
:
188 self
.checking
.updates_done(self
.versions_changed())
189 dialog
.wait_for_no_windows()
190 show_details
= self
.show_details()
197 download_box
.download_with_gui(self
.window
,
198 self
.prog_args
, main
= self
.main_exec
,
199 run_afterwards
= not self
.download_only
)
204 def get_best_source(self
, impl
):
205 """Return the best download source for this implementation."""
206 if impl
.download_sources
:
207 return impl
.download_sources
[0]
210 def refresh_all(self
, force
= True):
211 if hasattr(Policy
, 'refresh_all'):
212 Policy
.refresh_all(self
, force
)
214 # XXX: Remove this. Moved to Policy.
215 for x
in self
.walk_interfaces():
216 self
.begin_iface_download(x
, force
)
218 def abort_all_downloads(self
):
219 for x
in self
.monitored_downloads
[:]:
222 def set_original_implementations(self
):
223 assert self
.original_implementation
is None
224 self
.original_implementation
= policy
.implementation
.copy()
226 def versions_changed(self
):
227 """Return whether we have now chosen any different implementations.
228 If so, we want to show the dialog to the user to confirm the new ones."""
231 if not self
.original_implementation
:
232 return True # Shouldn't happen?
233 if len(self
.original_implementation
) != len(self
.implementation
):
235 for iface
in self
.original_implementation
:
236 old
= self
.original_implementation
[iface
]
239 new
= self
.implementation
.get(iface
, None)
246 def pretty_size(size
):
250 return '%d bytes' % size
252 for unit
in ('Kb', 'Mb', 'Gb', 'Tb'):
256 return '%.1f %s' % (size
, unit
)