Added accelerator for Download button.
[zeroinstall.git] / properties.py
blob720efcda5a1cac6fc4b8b88cf422c3e411f6f8c4
1 from zeroinstall.injector.model import *
2 from zeroinstall.injector import writer
3 import gtk
5 import help_box
6 from dialog import Dialog
7 from gui import policy
8 from impl_list import ImplementationList
9 import time
10 import dialog
12 _dialogs = {} # Interface -> Properties
14 tips = gtk.Tooltips()
16 # Response codes
17 ADD_FEED = 1
19 def enumerate(items):
20 x = 0
21 for i in items:
22 yield x, i
23 x += 1
25 def format_para(para):
26 lines = [l.strip() for l in para.split('\n')]
27 return ' '.join(lines)
29 class Description(gtk.ScrolledWindow):
30 def __init__(self):
31 gtk.ScrolledWindow.__init__(self, None, None)
32 self.set_shadow_type(gtk.SHADOW_IN)
33 self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
34 description = gtk.TextView()
35 description.set_left_margin(4)
36 description.set_right_margin(4)
37 description.set_wrap_mode(gtk.WRAP_WORD)
38 description.set_editable(False)
39 description.set_cursor_visible(False)
40 self.add(description)
42 self.buffer = description.get_buffer()
43 self.heading_style = self.buffer.create_tag(underline = True, scale = 1.2)
44 description.set_size_request(-1, 100)
46 def set_details(self, interface):
47 buffer = self.buffer
48 heading_style = self.heading_style
50 buffer.delete(buffer.get_start_iter(), buffer.get_end_iter())
52 iter = buffer.get_start_iter()
54 buffer.insert_with_tags(iter,
55 '%s (%s)' % (interface.get_name(), interface.summary), heading_style)
57 buffer.insert(iter, '\n%s\n' % interface.uri)
59 # (converts to local time)
60 if interface.last_modified:
61 buffer.insert(iter, '\nLast upstream change: %s' % time.ctime(interface.last_modified))
63 if interface.last_checked:
64 buffer.insert(iter, '\nLast checked: %s' % time.ctime(interface.last_checked))
66 if hasattr(interface, 'feeds') and interface.feeds:
67 for feed in interface.feeds:
68 if hasattr(feed, 'uri'):
69 feed = feed.uri
70 buffer.insert(iter, '\nFeed: %s' % feed)
72 buffer.insert_with_tags(iter, '\n\nDescription\n', heading_style)
74 paragraphs = [format_para(p) for p in (interface.description or "-").split('\n\n')]
76 buffer.insert(iter, '\n\n'.join(paragraphs))
79 class Properties(Dialog):
80 interface = None
81 use_list = None
83 def __init__(self, interface):
84 Dialog.__init__(self)
85 self.interface = interface
86 self.set_title('Interface ' + interface.get_name())
87 self.set_default_size(gtk.gdk.screen_width() / 2,
88 gtk.gdk.screen_height() / 3)
90 vbox = gtk.VBox(False, 4)
91 vbox.set_border_width(4)
92 self.vbox.pack_start(vbox, True, True, 0)
94 self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP)
95 add_feed_button = self.add_mixed_button(_('Add Local Feed...'),
96 gtk.STOCK_ADD, ADD_FEED)
97 self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CANCEL)
98 self.set_default_response(gtk.RESPONSE_CANCEL)
100 tips.set_tip(add_feed_button,
101 _('If you have another implementation of this interface (e.g., a '
102 'CVS checkout), you can add it to the list by registering the XML '
103 'feed file that came with it.'))
105 def response(dialog, resp):
106 if resp == gtk.RESPONSE_CANCEL:
107 self.destroy()
108 #elif resp == 1:
109 # policy.begin_iface_download(interface, True)
110 elif resp == gtk.RESPONSE_HELP:
111 properties_help.display()
112 elif resp == ADD_FEED:
113 add_feed(interface)
114 self.connect('response', response)
116 main_hbox = gtk.HBox(False, 5)
117 vbox.pack_start(main_hbox, True, True, 0)
119 description = Description()
120 description.set_details(interface)
121 main_hbox.pack_start(description, True, True, 0)
123 main_hbox.pack_start(self.build_versions_column(interface), False, True, 0)
125 self.update_list()
126 vbox.show_all()
128 def updated():
129 self.update_list()
130 description.set_details(interface)
131 self.connect('destroy', lambda s: policy.watchers.remove(updated))
132 policy.watchers.append(updated)
134 def update_list(self):
135 impls = policy.get_ranked_implementations(self.interface)
136 self.use_list.set_items(impls)
138 def build_versions_column(self, interface):
139 assert self.use_list is None
141 vbox = gtk.VBox(False, 2)
143 hbox = gtk.HBox(False, 2)
144 vbox.pack_start(hbox, False, True, 2)
146 eb = gtk.EventBox()
147 stability = gtk.combo_box_new_text()
148 eb.add(stability)
149 stability.append_text('Use default setting')
150 stability.set_active(0)
151 for i, x in enumerate((stable, testing, developer)):
152 stability.append_text(str(x).capitalize())
153 if x is interface.stability_policy:
154 stability.set_active(i + 1)
155 hbox.pack_start(gtk.Label('Preferred stability:'), False, True, 2)
156 hbox.pack_start(eb, True, True, 0)
157 def set_stability_policy(combo):
158 i = stability.get_active()
159 if i == 0:
160 new_stability = None
161 else:
162 name = stability.get_model()[i][0].lower()
163 new_stability = stability_levels[name]
164 interface.set_stability_policy(new_stability)
165 writer.save_interface(interface)
166 policy.recalculate()
167 stability.connect('changed', set_stability_policy)
168 tips.set_tip(eb, _('Implementations at this stability level or higher '
169 'will be used in preference to others. You can use this '
170 'to override the global "Help test new versions" setting '
171 'just for this interface.'))
173 self.use_list = ImplementationList(interface)
174 self.use_list.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
175 vbox.pack_start(self.use_list, True, True, 2)
177 return vbox
179 def add_feed(interface):
180 sel = gtk.FileSelection(_('Select XML feed file'))
181 def ok(b):
182 from xml.dom import minidom
183 from zeroinstall.injector import reader
184 feed = sel.get_filename()
185 try:
186 if hasattr(policy, 'get_feed_targets'):
187 feed_targets = policy.get_feed_targets(feed)
188 if interface not in feed_targets:
189 raise Exception("Not a valid feed for '%s'; this is a feed for:\n%s" %
190 (interface.uri,
191 '\n'.join([f.uri for f in feed_targets])))
192 if interface.get_feed(feed):
193 dialog.alert(None, 'This feed is already registered.')
194 else:
195 interface.feeds.append(Feed(feed, user_override = True, arch = None))
196 else:
197 doc = minidom.parse(feed)
198 uri = doc.documentElement.getAttribute('uri')
199 if not uri:
200 raise Exception("Missing uri attribute in interface file '%s'" % feed)
201 if uri != interface.uri:
202 raise Exception("Feed is for interface '%s', not '%s'" %
203 (uri, interface.uri))
204 if feed in interface.feeds:
205 raise Exception("Feed is already registered")
206 interface.feeds.append(feed)
207 writer.save_interface(interface)
208 sel.destroy()
209 reader.update_from_cache(interface)
210 policy.recalculate()
211 except Exception, ex:
212 dialog.alert(None, "Error in feed file '%s':\n\n%s" % (feed, str(ex)))
214 sel.ok_button.connect('clicked', ok)
215 sel.cancel_button.connect('clicked', lambda b: sel.destroy())
216 sel.show()
218 def edit(interface):
219 assert isinstance(interface, Interface)
220 if interface in _dialogs:
221 _dialogs[interface].destroy()
222 _dialogs[interface] = Properties(interface)
223 _dialogs[interface].show()
225 properties_help = help_box.HelpBox("Injector Properties Help",
226 ('Interface properties', """
227 This window displays information about an interface. On the left is some information \
228 about the interface:
230 - At the top is its short name.
231 - Below that is the full name; this is also location which is used to update \
232 the information.
233 - 'Last upstream change' shows the version of the cached copy of the interface file.
234 - 'Last checked' is the last time a fresh copy of the upstream interface file was \
235 downloaded.
236 - 'Local feed' is shown if you have other sources of versions of this program (for \
237 example, a CVS checkout).
238 - Then there is a longer description of the interface."""),
240 ('Implementations', """
241 The right side of the window is a list of all known implementations of the interface. \
242 The columns have the following meanings:
244 Version gives the version number. High-numbered versions are considered to be \
245 better than low-numbered ones.
247 Stability is 'stable' if the implementation is believed to be stable, 'buggy' if \
248 it is known to contain serious bugs, and 'testing' if its stability is not yet \
249 known. This information is normally supplied and updated by the author of the \
250 software, but you can override their rating (overridden values are shown in upper-case). \
251 You can also use the special level 'preferred'.
253 C(ached) indicates whether the implementation is already stored on your computer. \
254 In off-line mode, only cached implementations are considered for use.
256 Arch indicates what kind of computer system the implementation is for, or 'any' \
257 if it works with all types of system.
258 """),
259 ('Sort order', """
260 The implementations are listed in the injector's currently preferred order (the one \
261 at the top will actually be used). Usable implementations all come before unusable \
262 ones.
264 Unusable ones are those for incompatible \
265 architectures, those marked as 'buggy', versions explicitly marked as incompatible with \
266 another interface you are using and, in off-line mode, uncached implementations. Unusable \
267 implementations are shown crossed out.
269 For the usable implementations, the order is as follows:
271 - Preferred implementations come first.
273 - Then, if network use is set to 'Minimal', cached implementations come before \
274 non-cached.
276 - Then, implementations at or above the selected stability level come before all others.
278 - Then, higher-numbered versions come before low-numbered ones.
280 - Then cached come before non-cached (for 'Full' network use mode).
281 """))