Imported PyGObject's pygtkcompat.py for better compatibility
[zeroinstall.git] / zeroinstall / 0launch-gui / preferences.py
blob86c754945e55734f92947049ba3597872e344c4e
1 # Copyright (C) 2009, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 import sys
5 import gtk
6 from dialog import Template
7 from zeroinstall import _
8 from zeroinstall.gtkui import help_box
9 from zeroinstall.injector.model import network_levels
10 from zeroinstall.injector import trust, gpg
11 from freshness import freshness_levels, Freshness
13 SHOW_CACHE = 0
15 class Preferences:
16 def __init__(self, config, notify_cb = None):
17 if notify_cb is None:
18 notify_cb = lambda: None
20 def connect_toggle(widget_name, setting_name):
21 widget = widgets.get_widget(widget_name)
22 widget.set_active(getattr(config, setting_name))
23 def toggle(w, config = config, setting_name = setting_name):
24 setattr(config, setting_name, w.get_active())
25 config.save_globals()
26 notify_cb()
27 widget.connect('toggled', toggle)
29 widgets = Template('preferences_box')
31 self.window = widgets.get_widget('preferences_box')
32 self.window.connect('destroy', lambda w: self.destroyed())
34 # (attribute to avoid: free variable 'network' referenced before assignment in enclosing scope)
35 self.network = widgets.get_widget('network_use')
36 self.network.set_active(list(network_levels).index(config.network_use))
38 def set_network_use(combo):
39 config.network_use = network_levels[self.network.get_active()]
40 config.save_globals()
41 notify_cb()
42 self.network.connect('changed', set_network_use)
44 # Freshness
45 times = [x.time for x in freshness_levels]
46 if config.freshness not in times:
47 freshness_levels.append(Freshness(config.freshness,
48 '%d seconds' % config.freshness))
49 times.append(config.freshness)
50 freshness = widgets.get_widget('freshness')
51 freshness_model = freshness.get_model()
52 for level in freshness_levels:
53 i = freshness_model.append()
54 freshness_model.set_value(i, 0, str(level))
55 freshness.set_active(times.index(config.freshness))
56 def set_freshness(combo, freshness = freshness): # (pygtk bug?)
57 config.freshness = freshness_levels[freshness.get_active()].time
58 config.save_globals()
59 notify_cb()
60 freshness.connect('changed', set_freshness)
62 connect_toggle('help_test', 'help_with_testing')
64 # Keys
65 keys_view = widgets.get_widget('trusted_keys')
66 KeyList(keys_view)
67 connect_toggle('auto_approve', 'auto_approve_keys')
69 # Responses
70 self.window.set_default_response(gtk.RESPONSE_CLOSE)
71 self.window.get_default_widget().grab_focus()
73 def response(dialog, resp):
74 if resp in (gtk.RESPONSE_CLOSE, gtk.RESPONSE_DELETE_EVENT):
75 self.window.destroy()
76 elif resp == gtk.RESPONSE_HELP:
77 gui_help.display()
78 self.window.connect('response', response)
80 self.window.set_default_size(-1, gtk.gdk.screen_height() / 3)
82 def destroyed(self):
83 global preferences_box
84 preferences_box = None
86 class KeyList:
87 def __init__(self, tv):
88 self.trusted_keys = gtk.TreeStore(str, object)
89 tv.set_model(self.trusted_keys)
90 tc = gtk.TreeViewColumn(_('Trusted keys'), gtk.CellRendererText(), text = 0)
91 tv.append_column(tc)
92 trust.trust_db.ensure_uptodate()
94 def update_keys():
95 # Remember which ones are open
96 expanded_elements = set()
97 def add_row(tv, path, unused = None):
98 if len(path) == 1:
99 domain = self.trusted_keys[path][0]
100 expanded_elements.add(domain)
101 tv.map_expanded_rows(add_row, None)
103 self.trusted_keys.clear()
104 domains = {}
106 keys = gpg.load_keys(list(trust.trust_db.keys.keys()))
108 for fingerprint in keys:
109 for domain in trust.trust_db.keys[fingerprint]:
110 if domain not in domains:
111 domains[domain] = set()
112 domains[domain].add(keys[fingerprint])
113 for domain in sorted(domains):
114 iter = self.trusted_keys.append(None, [domain, None])
115 for key in domains[domain]:
116 self.trusted_keys.append(iter, [key.name, key])
118 def may_expand(model, path, iter, unused):
119 if len(path) == 1:
120 if model[iter][0] in expanded_elements:
121 tv.expand_row(path, False)
122 self.trusted_keys.foreach(may_expand, None)
124 trust.trust_db.watchers.append(update_keys)
125 tv.connect('destroy', lambda w: trust.trust_db.watchers.remove(update_keys))
127 update_keys()
129 def remove_key(fingerprint, domain):
130 trust.trust_db.untrust_key(fingerprint, domain)
131 trust.trust_db.notify()
133 def trusted_keys_button_press(tv, bev):
134 if bev.type == gtk.gdk.BUTTON_PRESS and bev.button == 3:
135 pos = tv.get_path_at_pos(int(bev.x), int(bev.y))
136 if not pos:
137 return False
138 path, col, x, y = pos
139 if len(path) != 2:
140 return False
142 key = self.trusted_keys[path][1]
143 if isinstance(path, tuple):
144 path = path[:-1] # PyGTK
145 else:
146 path.up() # PyGObject
147 domain = self.trusted_keys[path][0]
149 global menu # Needed to stop Python 3 GCing the menu and closing it instantly
150 menu = gtk.Menu()
152 item = gtk.MenuItem()
153 item.set_label(_('Remove key for "%s"') % key.get_short_name())
154 item.connect('activate',
155 lambda item, fp = key.fingerprint, d = domain: remove_key(fp, d))
156 item.show()
157 menu.append(item)
159 if sys.version_info[0] > 2:
160 menu.popup(None, None, None, None, bev.button, bev.time)
161 else:
162 menu.popup(None, None, None, bev.button, bev.time)
163 return True
164 return False
165 tv.connect('button-press-event', trusted_keys_button_press)
167 preferences_box = None
168 def show_preferences(config, notify_cb = None):
169 global preferences_box
170 if preferences_box:
171 preferences_box.window.destroy()
172 preferences_box = Preferences(config, notify_cb)
173 preferences_box.window.show()
174 return preferences_box.window
176 gui_help = help_box.HelpBox(_("Zero Install Preferences Help"),
177 (_('Overview'), '\n\n' +
178 _("""There are three ways to control which implementations are chosen. You can adjust the \
179 network policy and the overall stability policy, which affect all interfaces, or you \
180 can edit the policy of individual interfaces.""")),
182 (_('Network use'), '\n' +
183 _("""The 'Network use' option controls how the injector uses the network. If off-line, \
184 the network is not used at all. If 'Minimal' is selected then the injector will use \
185 the network if needed, but only if it has no choice. It will run an out-of-date \
186 version rather than download a newer one. If 'Full' is selected, the injector won't \
187 worry about how much it downloads, but will always pick the version it thinks is best.""")),
189 (_('Freshness'), '\n' +
190 _("""The feed files, which provide the information about which versions are \
191 available, are also cached. To update them, click on 'Refresh all now'. You can also \
192 get the injector to check for new versions automatically from time to time using \
193 the Freshness setting.""")),
195 (_('Help test new versions'), '\n' +
196 _("""The overall stability policy can either be to prefer stable versions, or to help test \
197 new versions. Choose whichever suits you. Since different programmers have different \
198 ideas of what 'stable' means, you may wish to override this on a per-interface basis.
200 To set the policy for an interface individually, select it in the main window and \
201 click on 'Interface Properties'. See that dialog's help text for more information.""")),
203 (_('Security'), '\n' +
204 _("""This section lists all keys which you currently trust. When fetching a new program or \
205 updates for an existing one, the feed must be signed by one of these keys. If not, \
206 you will be prompted to confirm that you trust the new key, and it will then be added \
207 to this list.
209 If "Automatic approval for new feeds" is on, new keys will be automatically approved if \
210 you haven't used the program before and the key is known to the key information server. \
211 When updating feeds, confirmation for new keys is always required.
213 To remove a key, right-click on it and choose 'Remove' from the menu.""")),