diff options
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py new file mode 100644 index 0000000000..a83a176ddc --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py | |||
@@ -0,0 +1,346 @@ | |||
1 | # | ||
2 | # BitBake Graphical GTK User Interface | ||
3 | # | ||
4 | # Copyright (C) 2011 Intel Corporation | ||
5 | # | ||
6 | # Authored by Joshua Lock <josh@linux.intel.com> | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify | ||
9 | # it under the terms of the GNU General Public License version 2 as | ||
10 | # published by the Free Software Foundation. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | |||
21 | import gtk | ||
22 | import gobject | ||
23 | |||
24 | class TaskListModel(gtk.ListStore): | ||
25 | """ | ||
26 | This class defines an gtk.ListStore subclass which will convert the output | ||
27 | of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also | ||
28 | providing convenience functions to access gtk.TreeModel subclasses which | ||
29 | provide filtered views of the data. | ||
30 | """ | ||
31 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) | ||
32 | |||
33 | __gsignals__ = { | ||
34 | "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, | ||
35 | gobject.TYPE_NONE, | ||
36 | ()) | ||
37 | } | ||
38 | |||
39 | """ | ||
40 | """ | ||
41 | def __init__(self): | ||
42 | self.contents = None | ||
43 | self.tasks = None | ||
44 | self.packages = None | ||
45 | self.images = None | ||
46 | |||
47 | gtk.ListStore.__init__ (self, | ||
48 | gobject.TYPE_STRING, | ||
49 | gobject.TYPE_STRING, | ||
50 | gobject.TYPE_STRING, | ||
51 | gobject.TYPE_STRING, | ||
52 | gobject.TYPE_STRING, | ||
53 | gobject.TYPE_STRING, | ||
54 | gobject.TYPE_STRING, | ||
55 | gobject.TYPE_BOOLEAN) | ||
56 | |||
57 | """ | ||
58 | Create, if required, and return a filtered gtk.TreeModel | ||
59 | containing only the items which are to be included in the | ||
60 | image | ||
61 | """ | ||
62 | def contents_model(self): | ||
63 | if not self.contents: | ||
64 | self.contents = self.filter_new() | ||
65 | self.contents.set_visible_column(self.COL_INC) | ||
66 | return self.contents | ||
67 | |||
68 | """ | ||
69 | Helper function to determine whether an item is a task | ||
70 | """ | ||
71 | def task_model_filter(self, model, it): | ||
72 | if model.get_value(it, self.COL_TYPE) == 'task': | ||
73 | return True | ||
74 | else: | ||
75 | return False | ||
76 | |||
77 | """ | ||
78 | Create, if required, and return a filtered gtk.TreeModel | ||
79 | containing only the items which are tasks | ||
80 | """ | ||
81 | def tasks_model(self): | ||
82 | if not self.tasks: | ||
83 | self.tasks = self.filter_new() | ||
84 | self.tasks.set_visible_func(self.task_model_filter) | ||
85 | return self.tasks | ||
86 | |||
87 | """ | ||
88 | Helper function to determine whether an item is an image | ||
89 | """ | ||
90 | def image_model_filter(self, model, it): | ||
91 | if model.get_value(it, self.COL_TYPE) == 'image': | ||
92 | return True | ||
93 | else: | ||
94 | return False | ||
95 | |||
96 | """ | ||
97 | Create, if required, and return a filtered gtk.TreeModel | ||
98 | containing only the items which are images | ||
99 | """ | ||
100 | def images_model(self): | ||
101 | if not self.images: | ||
102 | self.images = self.filter_new() | ||
103 | self.images.set_visible_func(self.image_model_filter) | ||
104 | return self.images | ||
105 | |||
106 | """ | ||
107 | Helper function to determine whether an item is a package | ||
108 | """ | ||
109 | def package_model_filter(self, model, it): | ||
110 | if model.get_value(it, self.COL_TYPE) == 'package': | ||
111 | return True | ||
112 | else: | ||
113 | return False | ||
114 | |||
115 | """ | ||
116 | Create, if required, and return a filtered gtk.TreeModel | ||
117 | containing only the items which are packages | ||
118 | """ | ||
119 | def packages_model(self): | ||
120 | if not self.packages: | ||
121 | self.packages = self.filter_new() | ||
122 | self.packages.set_visible_func(self.package_model_filter) | ||
123 | return self.packages | ||
124 | |||
125 | """ | ||
126 | The populate() function takes as input the data from a | ||
127 | bb.event.TargetsTreeGenerated event and populates the TaskList. | ||
128 | Once the population is done it emits gsignal tasklist-populated | ||
129 | to notify any listeners that the model is ready | ||
130 | """ | ||
131 | def populate(self, event_model): | ||
132 | for item in event_model["pn"]: | ||
133 | atype = 'package' | ||
134 | name = item | ||
135 | summary = event_model["pn"][item]["summary"] | ||
136 | license = event_model["pn"][item]["license"] | ||
137 | group = event_model["pn"][item]["section"] | ||
138 | |||
139 | depends = event_model["depends"].get(item, "") | ||
140 | rdepends = event_model["rdepends-pn"].get(item, "") | ||
141 | depends = depends + rdepends | ||
142 | self.squish(depends) | ||
143 | deps = " ".join(depends) | ||
144 | |||
145 | if name.count('task-') > 0: | ||
146 | atype = 'task' | ||
147 | elif name.count('-image-') > 0: | ||
148 | atype = 'image' | ||
149 | |||
150 | self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, | ||
151 | self.COL_LIC, license, self.COL_GROUP, group, | ||
152 | self.COL_DEPS, deps, self.COL_BINB, "", | ||
153 | self.COL_TYPE, atype, self.COL_INC, False) | ||
154 | |||
155 | self.emit("tasklist-populated") | ||
156 | |||
157 | """ | ||
158 | squish lst so that it doesn't contain any duplicates | ||
159 | """ | ||
160 | def squish(self, lst): | ||
161 | seen = {} | ||
162 | for l in lst: | ||
163 | seen[l] = 1 | ||
164 | return seen.keys() | ||
165 | |||
166 | """ | ||
167 | Mark the item at path as not included | ||
168 | NOTE: | ||
169 | path should be a gtk.TreeModelPath into self (not a filtered model) | ||
170 | """ | ||
171 | def remove_item_path(self, path): | ||
172 | self[path][self.COL_BINB] = "" | ||
173 | self[path][self.COL_INC] = False | ||
174 | |||
175 | """ | ||
176 | """ | ||
177 | def mark(self, path): | ||
178 | name = self[path][self.COL_NAME] | ||
179 | it = self.get_iter_first() | ||
180 | removals = [] | ||
181 | #print("Removing %s" % name) | ||
182 | |||
183 | self.remove_item_path(path) | ||
184 | |||
185 | # Remove all dependent packages, update binb | ||
186 | while it: | ||
187 | path = self.get_path(it) | ||
188 | # FIXME: need to ensure partial name matching doesn't happen, regexp? | ||
189 | if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): | ||
190 | #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) | ||
191 | # found a dependency, remove it | ||
192 | self.mark(path) | ||
193 | if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): | ||
194 | binb = self.find_alt_dependency(self[path][self.COL_NAME]) | ||
195 | #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) | ||
196 | self[path][self.COL_BINB] = binb | ||
197 | it = self.iter_next(it) | ||
198 | |||
199 | """ | ||
200 | """ | ||
201 | def sweep_up(self): | ||
202 | removals = [] | ||
203 | it = self.get_iter_first() | ||
204 | |||
205 | while it: | ||
206 | path = self.get_path(it) | ||
207 | binb = self[path][self.COL_BINB] | ||
208 | if binb == "" or binb is None: | ||
209 | #print("Sweeping up %s" % self[path][self.COL_NAME]) | ||
210 | if not path in removals: | ||
211 | removals.extend(path) | ||
212 | it = self.iter_next(it) | ||
213 | |||
214 | while removals: | ||
215 | path = removals.pop() | ||
216 | self.mark(path) | ||
217 | |||
218 | """ | ||
219 | Remove an item from the contents | ||
220 | """ | ||
221 | def remove_item(self, path): | ||
222 | self.mark(path) | ||
223 | self.sweep_up() | ||
224 | |||
225 | """ | ||
226 | Find the name of an item in the image contents which depends on the item | ||
227 | at contents_path returns either an item name (str) or None | ||
228 | NOTE: | ||
229 | contents_path must be a path in the self.contents gtk.TreeModel | ||
230 | """ | ||
231 | def find_alt_dependency(self, name): | ||
232 | it = self.get_iter_first() | ||
233 | while it: | ||
234 | # iterate all items in the model | ||
235 | path = self.get_path(it) | ||
236 | deps = self[path][self.COL_DEPS] | ||
237 | itname = self[path][self.COL_NAME] | ||
238 | inc = self[path][self.COL_INC] | ||
239 | if itname != name and inc and deps.count(name) > 0: | ||
240 | # if this item depends on the item, return this items name | ||
241 | #print("%s depends on %s" % (itname, name)) | ||
242 | return itname | ||
243 | it = self.iter_next(it) | ||
244 | return "" | ||
245 | |||
246 | """ | ||
247 | Convert a path in self to a path in the filtered contents model | ||
248 | """ | ||
249 | def contents_path_for_path(self, path): | ||
250 | return self.contents.convert_child_path_to_path(path) | ||
251 | |||
252 | """ | ||
253 | Check the self.contents gtk.TreeModel for an item | ||
254 | where COL_NAME matches item_name | ||
255 | Returns True if a match is found, False otherwise | ||
256 | """ | ||
257 | def contents_includes_name(self, item_name): | ||
258 | it = self.contents.get_iter_first() | ||
259 | while it: | ||
260 | path = self.contents.get_path(it) | ||
261 | if self.contents[path][self.COL_NAME] == item_name: | ||
262 | return True | ||
263 | it = self.contents.iter_next(it) | ||
264 | return False | ||
265 | |||
266 | """ | ||
267 | Add this item, and any of its dependencies, to the image contents | ||
268 | """ | ||
269 | def include_item(self, item_path, binb=""): | ||
270 | name = self[item_path][self.COL_NAME] | ||
271 | deps = self[item_path][self.COL_DEPS] | ||
272 | cur_inc = self[item_path][self.COL_INC] | ||
273 | #print("Adding %s for %s dependency" % (name, binb)) | ||
274 | if not cur_inc: | ||
275 | self[item_path][self.COL_INC] = True | ||
276 | self[item_path][self.COL_BINB] = binb | ||
277 | if deps: | ||
278 | #print("Dependencies of %s are %s" % (name, deps)) | ||
279 | # add all of the deps and set their binb to this item | ||
280 | for dep in deps.split(" "): | ||
281 | # FIXME: this skipping virtuals can't be right? Unless we choose only to show target | ||
282 | # packages? In which case we should handle this server side... | ||
283 | # If the contents model doesn't already contain dep, add it | ||
284 | if not dep.startswith("virtual") and not self.contents_includes_name(dep): | ||
285 | path = self.find_path_for_item(dep) | ||
286 | if path: | ||
287 | self.include_item(path, name) | ||
288 | else: | ||
289 | pass | ||
290 | |||
291 | """ | ||
292 | Find the model path for the item_name | ||
293 | Returns the path in the model or None | ||
294 | """ | ||
295 | def find_path_for_item(self, item_name): | ||
296 | it = self.get_iter_first() | ||
297 | path = None | ||
298 | while it: | ||
299 | path = self.get_path(it) | ||
300 | if (self[path][self.COL_NAME] == item_name): | ||
301 | return path | ||
302 | else: | ||
303 | it = self.iter_next(it) | ||
304 | return None | ||
305 | |||
306 | """ | ||
307 | Empty self.contents by setting the include of each entry to None | ||
308 | """ | ||
309 | def reset(self): | ||
310 | it = self.contents.get_iter_first() | ||
311 | while it: | ||
312 | path = self.contents.get_path(it) | ||
313 | opath = self.contents.convert_path_to_child_path(path) | ||
314 | self[opath][self.COL_INC] = False | ||
315 | self[opath][self.COL_BINB] = "" | ||
316 | # As we've just removed the first item... | ||
317 | it = self.contents.get_iter_first() | ||
318 | |||
319 | """ | ||
320 | Returns True if one of the selected tasks is an image, False otherwise | ||
321 | """ | ||
322 | def targets_contains_image(self): | ||
323 | it = self.images.get_iter_first() | ||
324 | while it: | ||
325 | path = self.images.get_path(it) | ||
326 | inc = self.images[path][self.COL_INC] | ||
327 | if inc: | ||
328 | return True | ||
329 | it = self.images.iter_next(it) | ||
330 | return False | ||
331 | |||
332 | """ | ||
333 | Return a list of all selected items which are not -native or -cross | ||
334 | """ | ||
335 | def get_targets(self): | ||
336 | tasks = [] | ||
337 | |||
338 | it = self.contents.get_iter_first() | ||
339 | while it: | ||
340 | path = self.contents.get_path(it) | ||
341 | name = self.contents[path][self.COL_NAME] | ||
342 | stype = self.contents[path][self.COL_TYPE] | ||
343 | if not name.count('-native') and not name.count('-cross'): | ||
344 | tasks.append(name) | ||
345 | it = self.contents.iter_next(it) | ||
346 | return tasks | ||