Depend on binutils ('ar' is needed to extract from debs).
[zeroinstall.git] / zeroinstall / 0launch-gui / impl_list.py
blob469e6c2ba5966ab6d28497510c9b326c7da35c51
1 import gtk, gobject, os
2 from zeroinstall.injector import model, writer
3 from gui import policy, pretty_size
4 from treetips import TreeTips
6 def popup_menu(bev, values, fn):
7 menu = gtk.Menu()
8 for value in values:
9 if value is None:
10 item = gtk.SeparatorMenuItem()
11 else:
12 item = gtk.MenuItem(str(value).capitalize())
13 item.connect('activate', lambda item, v=value: fn(v))
14 item.show()
15 menu.append(item)
16 menu.popup(None, None, None, bev.button, bev.time)
18 rox_filer = 'http://rox.sourceforge.net/2005/interfaces/ROX-Filer'
20 # Columns
21 ITEM = 0
22 ARCH = 1
23 STABILITY = 2
24 VERSION = 3
25 CACHED = 4
26 UNUSABLE = 5
27 RELEASED = 6
29 def interface_for(interface, impl):
30 # Since injector 0.16, we can get the interface from the impl
31 # Before that, the impl always comes from the main interface (even for feeds)
32 if hasattr(impl, 'interface'):
33 return impl.interface
34 return interface
36 class ImplTips(TreeTips):
37 def __init__(self, interface):
38 self.interface = interface
40 def get_tooltip_text(self, impl):
41 if hasattr(policy, 'restrictions'):
42 restrictions = policy.restrictions.get(self.interface, [])
43 unusable = policy.get_unusable_reason(impl, restrictions)
44 else:
45 restrictions = None
46 unusable = policy.get_unusable_reason(impl)
47 if unusable:
48 return unusable
50 if impl.id.startswith('/'):
51 return _("Local: %s") % impl.id
52 if policy.get_cached(impl):
53 return _("Cached: %s") % policy.get_implementation_path(impl)
55 src = policy.get_best_source(impl)
56 if src:
57 size = pretty_size(src.size)
58 return _("Not yet downloaded (%s)") % size
59 else:
60 return _("No downloads available!")
62 class ImplementationList(gtk.ScrolledWindow):
63 tree_view = None
64 model = None
65 interface = None
67 def __init__(self, interface):
68 gtk.ScrolledWindow.__init__(self, None, None)
69 self.set_shadow_type(gtk.SHADOW_IN)
71 self.interface = interface
73 self.model = gtk.ListStore(object, str, str, str,
74 gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN,
75 str)
77 self.tree_view = gtk.TreeView(self.model)
79 text = gtk.CellRendererText()
80 text_strike = gtk.CellRendererText()
81 toggle = gtk.CellRendererToggle()
83 stability = gtk.TreeViewColumn('Stability', text, text = STABILITY)
85 for column in (gtk.TreeViewColumn('Version', text, text = VERSION, strikethrough = UNUSABLE),
86 gtk.TreeViewColumn('Released', text, text = RELEASED, strikethrough = UNUSABLE),
87 stability,
88 gtk.TreeViewColumn('C', toggle, active = CACHED),
89 gtk.TreeViewColumn('Arch', text, text = ARCH)):
90 self.tree_view.append_column(column)
92 self.add(self.tree_view)
94 tips = ImplTips(interface)
96 def motion(tree_view, ev):
97 if ev.window is not tree_view.get_bin_window():
98 return False
99 pos = tree_view.get_path_at_pos(int(ev.x), int(ev.y))
100 if pos:
101 path = pos[0]
102 row = self.model[path]
103 if row[ITEM] is not tips.item:
104 tips.prime(tree_view, row[ITEM])
105 else:
106 tips.hide()
108 self.tree_view.connect('motion-notify-event', motion)
109 self.tree_view.connect('leave-notify-event', lambda tv, ev: tips.hide())
110 self.tree_view.connect('destroy', lambda tv: tips.hide())
112 def button_press(tree_view, bev):
113 if bev.button not in (1, 3):
114 return False
115 pos = tree_view.get_path_at_pos(int(bev.x), int(bev.y))
116 if not pos:
117 return False
118 path, col, x, y = pos
119 impl = self.model[path][ITEM]
120 if col == stability:
121 upstream = impl.upstream_stability or model.testing
122 choices = model.stability_levels.values()
123 choices.sort()
124 choices.reverse()
125 def set(new):
126 if isinstance(new, model.Stability):
127 impl.user_stability = new
128 else:
129 impl.user_stability = None
130 writer.save_interface(interface_for(interface, impl))
131 policy.recalculate()
132 popup_menu(bev, ['Unset (%s)' % upstream, None] + choices,
133 set)
134 elif bev.button == 3 and policy.get_cached(impl):
135 def open(item):
136 os.spawnlp(os.P_WAIT, '0launch',
137 '0launch', rox_filer, '-d',
138 policy.get_implementation_path(impl))
139 popup_menu(bev, ['Open cached copy'], open)
140 self.tree_view.connect('button-press-event', button_press)
142 def get_selection(self):
143 return self.tree_view.get_selection()
145 def set_items(self, items):
146 self.model.clear()
147 if hasattr(policy, 'restrictions'):
148 restrictions = policy.restrictions.get(self.interface, None)
149 else:
150 restrictions = None
151 for item in items:
152 new = self.model.append()
153 self.model[new][ITEM] = item
154 self.model[new][VERSION] = item.get_version()
155 if hasattr(item, 'released') and item.released:
156 self.model[new][RELEASED] = item.released
157 else:
158 self.model[new][RELEASED] = "-"
159 self.model[new][CACHED] = policy.get_cached(item)
160 if item.user_stability:
161 self.model[new][STABILITY] = str(item.user_stability).upper()
162 else:
163 self.model[new][STABILITY] = item.upstream_stability or \
164 model.testing
165 self.model[new][ARCH] = item.arch or 'any'
166 if restrictions:
167 self.model[new][UNUSABLE] = policy.is_unusable(item, restrictions)
168 else:
169 self.model[new][UNUSABLE] = policy.is_unusable(item)
171 def clear(self):
172 self.model.clear()