diff options
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/hoblistmodel.py')
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hoblistmodel.py | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/hoblistmodel.py b/bitbake/lib/bb/ui/crumbs/hoblistmodel.py new file mode 100644 index 0000000000..b4d2a621b7 --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/hoblistmodel.py | |||
@@ -0,0 +1,900 @@ | |||
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 | # Authored by Dongxiao Xu <dongxiao.xu@intel.com> | ||
8 | # Authored by Shane Wang <shane.wang@intel.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License version 2 as | ||
12 | # published by the Free Software Foundation. | ||
13 | # | ||
14 | # This program is distributed in the hope that it will be useful, | ||
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | # GNU General Public License for more details. | ||
18 | # | ||
19 | # You should have received a copy of the GNU General Public License along | ||
20 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | |||
23 | import gtk | ||
24 | import gobject | ||
25 | from bb.ui.crumbs.hobpages import HobPage | ||
26 | |||
27 | # | ||
28 | # PackageListModel | ||
29 | # | ||
30 | class PackageListModel(gtk.ListStore): | ||
31 | """ | ||
32 | This class defines an gtk.ListStore subclass which will convert the output | ||
33 | of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also | ||
34 | providing convenience functions to access gtk.TreeModel subclasses which | ||
35 | provide filtered views of the data. | ||
36 | """ | ||
37 | |||
38 | (COL_NAME, COL_VER, COL_REV, COL_RNM, COL_SEC, COL_SUM, COL_RDEP, COL_RPROV, COL_SIZE, COL_RCP, COL_BINB, COL_INC, COL_FADE_INC, COL_FONT, COL_FLIST) = range(15) | ||
39 | |||
40 | __gsignals__ = { | ||
41 | "package-selection-changed" : (gobject.SIGNAL_RUN_LAST, | ||
42 | gobject.TYPE_NONE, | ||
43 | ()), | ||
44 | } | ||
45 | |||
46 | __toolchain_required_packages__ = ["packagegroup-core-standalone-sdk-target", "packagegroup-core-standalone-sdk-target-dbg"] | ||
47 | |||
48 | def __init__(self): | ||
49 | self.rprov_pkg = {} | ||
50 | gtk.ListStore.__init__ (self, | ||
51 | gobject.TYPE_STRING, | ||
52 | gobject.TYPE_STRING, | ||
53 | gobject.TYPE_STRING, | ||
54 | gobject.TYPE_STRING, | ||
55 | gobject.TYPE_STRING, | ||
56 | gobject.TYPE_STRING, | ||
57 | gobject.TYPE_STRING, | ||
58 | gobject.TYPE_STRING, | ||
59 | gobject.TYPE_STRING, | ||
60 | gobject.TYPE_STRING, | ||
61 | gobject.TYPE_STRING, | ||
62 | gobject.TYPE_BOOLEAN, | ||
63 | gobject.TYPE_BOOLEAN, | ||
64 | gobject.TYPE_STRING, | ||
65 | gobject.TYPE_STRING) | ||
66 | self.sort_column_id, self.sort_order = PackageListModel.COL_NAME, gtk.SORT_ASCENDING | ||
67 | |||
68 | """ | ||
69 | Find the model path for the item_name | ||
70 | Returns the path in the model or None | ||
71 | """ | ||
72 | def find_path_for_item(self, item_name): | ||
73 | pkg = item_name | ||
74 | if item_name not in self.pn_path.keys(): | ||
75 | if item_name not in self.rprov_pkg.keys(): | ||
76 | return None | ||
77 | pkg = self.rprov_pkg[item_name] | ||
78 | if pkg not in self.pn_path.keys(): | ||
79 | return None | ||
80 | |||
81 | return self.pn_path[pkg] | ||
82 | |||
83 | def find_item_for_path(self, item_path): | ||
84 | return self[item_path][self.COL_NAME] | ||
85 | |||
86 | """ | ||
87 | Helper function to determine whether an item is an item specified by filter | ||
88 | """ | ||
89 | def tree_model_filter(self, model, it, filter): | ||
90 | name = model.get_value(it, self.COL_NAME) | ||
91 | |||
92 | for key in filter.keys(): | ||
93 | if key == self.COL_NAME: | ||
94 | if filter[key] != 'Search packages by name': | ||
95 | if name and filter[key] not in name: | ||
96 | return False | ||
97 | else: | ||
98 | if model.get_value(it, key) not in filter[key]: | ||
99 | return False | ||
100 | self.filtered_nb += 1 | ||
101 | return True | ||
102 | |||
103 | """ | ||
104 | Create, if required, and return a filtered gtk.TreeModelSort | ||
105 | containing only the items specified by filter | ||
106 | """ | ||
107 | def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False): | ||
108 | model = self.filter_new() | ||
109 | self.filtered_nb = 0 | ||
110 | model.set_visible_func(self.tree_model_filter, filter) | ||
111 | |||
112 | sort = gtk.TreeModelSort(model) | ||
113 | sort.connect ('sort-column-changed', self.sort_column_changed_cb) | ||
114 | if initial: | ||
115 | sort.set_sort_column_id(PackageListModel.COL_NAME, gtk.SORT_ASCENDING) | ||
116 | sort.set_default_sort_func(None) | ||
117 | elif excluded_items_ahead: | ||
118 | sort.set_default_sort_func(self.exclude_item_sort_func, search_data) | ||
119 | elif included_items_ahead: | ||
120 | sort.set_default_sort_func(self.include_item_sort_func, search_data) | ||
121 | else: | ||
122 | if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name': | ||
123 | sort.set_default_sort_func(self.sort_func, search_data) | ||
124 | else: | ||
125 | sort.set_sort_column_id(self.sort_column_id, self.sort_order) | ||
126 | sort.set_default_sort_func(None) | ||
127 | |||
128 | sort.set_sort_func(PackageListModel.COL_INC, self.sort_column, PackageListModel.COL_INC) | ||
129 | sort.set_sort_func(PackageListModel.COL_SIZE, self.sort_column, PackageListModel.COL_SIZE) | ||
130 | sort.set_sort_func(PackageListModel.COL_BINB, self.sort_binb_column) | ||
131 | sort.set_sort_func(PackageListModel.COL_RCP, self.sort_column, PackageListModel.COL_RCP) | ||
132 | return sort | ||
133 | |||
134 | def sort_column_changed_cb (self, data): | ||
135 | self.sort_column_id, self.sort_order = data.get_sort_column_id () | ||
136 | |||
137 | def sort_column(self, model, row1, row2, col): | ||
138 | value1 = model.get_value(row1, col) | ||
139 | value2 = model.get_value(row2, col) | ||
140 | if col==PackageListModel.COL_SIZE: | ||
141 | value1 = HobPage._string_to_size(value1) | ||
142 | value2 = HobPage._string_to_size(value2) | ||
143 | |||
144 | cmp_res = cmp(value1, value2) | ||
145 | if cmp_res!=0: | ||
146 | if col==PackageListModel.COL_INC: | ||
147 | return -cmp_res | ||
148 | else: | ||
149 | return cmp_res | ||
150 | else: | ||
151 | name1 = model.get_value(row1, PackageListModel.COL_NAME) | ||
152 | name2 = model.get_value(row2, PackageListModel.COL_NAME) | ||
153 | return cmp(name1,name2) | ||
154 | |||
155 | def sort_binb_column(self, model, row1, row2): | ||
156 | value1 = model.get_value(row1, PackageListModel.COL_BINB) | ||
157 | value2 = model.get_value(row2, PackageListModel.COL_BINB) | ||
158 | value1_list = value1.split(', ') | ||
159 | value2_list = value2.split(', ') | ||
160 | |||
161 | value1 = value1_list[0] | ||
162 | value2 = value2_list[0] | ||
163 | |||
164 | cmp_res = cmp(value1, value2) | ||
165 | if cmp_res==0: | ||
166 | cmp_size = cmp(len(value1_list), len(value2_list)) | ||
167 | if cmp_size==0: | ||
168 | name1 = model.get_value(row1, PackageListModel.COL_NAME) | ||
169 | name2 = model.get_value(row2, PackageListModel.COL_NAME) | ||
170 | return cmp(name1,name2) | ||
171 | else: | ||
172 | return cmp_size | ||
173 | else: | ||
174 | return cmp_res | ||
175 | |||
176 | def exclude_item_sort_func(self, model, iter1, iter2, user_data=None): | ||
177 | if user_data: | ||
178 | val1 = model.get_value(iter1, PackageListModel.COL_NAME) | ||
179 | val2 = model.get_value(iter2, PackageListModel.COL_NAME) | ||
180 | return self.cmp_vals(val1, val2, user_data) | ||
181 | else: | ||
182 | val1 = model.get_value(iter1, PackageListModel.COL_FADE_INC) | ||
183 | val2 = model.get_value(iter2, PackageListModel.COL_INC) | ||
184 | return ((val1 == True) and (val2 == False)) | ||
185 | |||
186 | def include_item_sort_func(self, model, iter1, iter2, user_data=None): | ||
187 | if user_data: | ||
188 | val1 = model.get_value(iter1, PackageListModel.COL_NAME) | ||
189 | val2 = model.get_value(iter2, PackageListModel.COL_NAME) | ||
190 | return self.cmp_vals(val1, val2, user_data) | ||
191 | else: | ||
192 | val1 = model.get_value(iter1, PackageListModel.COL_INC) | ||
193 | val2 = model.get_value(iter2, PackageListModel.COL_INC) | ||
194 | return ((val1 == False) and (val2 == True)) | ||
195 | |||
196 | def sort_func(self, model, iter1, iter2, user_data): | ||
197 | val1 = model.get_value(iter1, PackageListModel.COL_NAME) | ||
198 | val2 = model.get_value(iter2, PackageListModel.COL_NAME) | ||
199 | return self.cmp_vals(val1, val2, user_data) | ||
200 | |||
201 | def cmp_vals(self, val1, val2, user_data): | ||
202 | if val1.startswith(user_data) and not val2.startswith(user_data): | ||
203 | return -1 | ||
204 | elif not val1.startswith(user_data) and val2.startswith(user_data): | ||
205 | return 1 | ||
206 | else: | ||
207 | return cmp(val1, val2) | ||
208 | |||
209 | def convert_vpath_to_path(self, view_model, view_path): | ||
210 | # view_model is the model sorted | ||
211 | # get the path of the model filtered | ||
212 | filtered_model_path = view_model.convert_path_to_child_path(view_path) | ||
213 | # get the model filtered | ||
214 | filtered_model = view_model.get_model() | ||
215 | # get the path of the original model | ||
216 | path = filtered_model.convert_path_to_child_path(filtered_model_path) | ||
217 | return path | ||
218 | |||
219 | def convert_path_to_vpath(self, view_model, path): | ||
220 | it = view_model.get_iter_first() | ||
221 | while it: | ||
222 | name = self.find_item_for_path(path) | ||
223 | view_name = view_model.get_value(it, PackageListModel.COL_NAME) | ||
224 | if view_name == name: | ||
225 | view_path = view_model.get_path(it) | ||
226 | return view_path | ||
227 | it = view_model.iter_next(it) | ||
228 | return None | ||
229 | |||
230 | """ | ||
231 | The populate() function takes as input the data from a | ||
232 | bb.event.PackageInfo event and populates the package list. | ||
233 | """ | ||
234 | def populate(self, pkginfolist): | ||
235 | # First clear the model, in case repopulating | ||
236 | self.clear() | ||
237 | |||
238 | def getpkgvalue(pkgdict, key, pkgname, defaultval = None): | ||
239 | value = pkgdict.get('%s_%s' % (key, pkgname), None) | ||
240 | if not value: | ||
241 | value = pkgdict.get(key, defaultval) | ||
242 | return value | ||
243 | |||
244 | for pkginfo in pkginfolist: | ||
245 | pn = pkginfo['PN'] | ||
246 | pv = pkginfo['PV'] | ||
247 | pr = pkginfo['PR'] | ||
248 | pkg = pkginfo['PKG'] | ||
249 | pkgv = getpkgvalue(pkginfo, 'PKGV', pkg) | ||
250 | pkgr = getpkgvalue(pkginfo, 'PKGR', pkg) | ||
251 | # PKGSIZE is artificial, will always be overridden with the package name if present | ||
252 | pkgsize = pkginfo.get('PKGSIZE_%s' % pkg, "0") | ||
253 | # PKG_%s is the renamed version | ||
254 | pkg_rename = pkginfo.get('PKG_%s' % pkg, "") | ||
255 | # The rest may be overridden or not | ||
256 | section = getpkgvalue(pkginfo, 'SECTION', pkg, "") | ||
257 | summary = getpkgvalue(pkginfo, 'SUMMARY', pkg, "") | ||
258 | rdep = getpkgvalue(pkginfo, 'RDEPENDS', pkg, "") | ||
259 | rrec = getpkgvalue(pkginfo, 'RRECOMMENDS', pkg, "") | ||
260 | rprov = getpkgvalue(pkginfo, 'RPROVIDES', pkg, "") | ||
261 | files_list = getpkgvalue(pkginfo, 'FILES_INFO', pkg, "") | ||
262 | for i in rprov.split(): | ||
263 | self.rprov_pkg[i] = pkg | ||
264 | |||
265 | recipe = pn + '-' + pv + '-' + pr | ||
266 | |||
267 | allow_empty = getpkgvalue(pkginfo, 'ALLOW_EMPTY', pkg, "") | ||
268 | |||
269 | if pkgsize == "0" and not allow_empty: | ||
270 | continue | ||
271 | |||
272 | # pkgsize is in KB | ||
273 | size = HobPage._size_to_string(HobPage._string_to_size(pkgsize + ' KB')) | ||
274 | self.set(self.append(), self.COL_NAME, pkg, self.COL_VER, pkgv, | ||
275 | self.COL_REV, pkgr, self.COL_RNM, pkg_rename, | ||
276 | self.COL_SEC, section, self.COL_SUM, summary, | ||
277 | self.COL_RDEP, rdep + ' ' + rrec, | ||
278 | self.COL_RPROV, rprov, self.COL_SIZE, size, | ||
279 | self.COL_RCP, recipe, self.COL_BINB, "", | ||
280 | self.COL_INC, False, self.COL_FONT, '10', self.COL_FLIST, files_list) | ||
281 | |||
282 | self.pn_path = {} | ||
283 | it = self.get_iter_first() | ||
284 | while it: | ||
285 | pn = self.get_value(it, self.COL_NAME) | ||
286 | path = self.get_path(it) | ||
287 | self.pn_path[pn] = path | ||
288 | it = self.iter_next(it) | ||
289 | |||
290 | """ | ||
291 | Update the model, send out the notification. | ||
292 | """ | ||
293 | def selection_change_notification(self): | ||
294 | self.emit("package-selection-changed") | ||
295 | |||
296 | """ | ||
297 | Check whether the item at item_path is included or not | ||
298 | """ | ||
299 | def path_included(self, item_path): | ||
300 | return self[item_path][self.COL_INC] | ||
301 | |||
302 | """ | ||
303 | Add this item, and any of its dependencies, to the image contents | ||
304 | """ | ||
305 | def include_item(self, item_path, binb=""): | ||
306 | if self.path_included(item_path): | ||
307 | return | ||
308 | |||
309 | item_name = self[item_path][self.COL_NAME] | ||
310 | item_deps = self[item_path][self.COL_RDEP] | ||
311 | |||
312 | self[item_path][self.COL_INC] = True | ||
313 | |||
314 | item_bin = self[item_path][self.COL_BINB].split(', ') | ||
315 | if binb and not binb in item_bin: | ||
316 | item_bin.append(binb) | ||
317 | self[item_path][self.COL_BINB] = ', '.join(item_bin).lstrip(', ') | ||
318 | |||
319 | if item_deps: | ||
320 | # Ensure all of the items deps are included and, where appropriate, | ||
321 | # add this item to their COL_BINB | ||
322 | for dep in item_deps.split(" "): | ||
323 | if dep.startswith('('): | ||
324 | continue | ||
325 | # If the contents model doesn't already contain dep, add it | ||
326 | dep_path = self.find_path_for_item(dep) | ||
327 | if not dep_path: | ||
328 | continue | ||
329 | dep_included = self.path_included(dep_path) | ||
330 | |||
331 | if dep_included and not dep in item_bin: | ||
332 | # don't set the COL_BINB to this item if the target is an | ||
333 | # item in our own COL_BINB | ||
334 | dep_bin = self[dep_path][self.COL_BINB].split(', ') | ||
335 | if not item_name in dep_bin: | ||
336 | dep_bin.append(item_name) | ||
337 | self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ') | ||
338 | elif not dep_included: | ||
339 | self.include_item(dep_path, binb=item_name) | ||
340 | |||
341 | def exclude_item(self, item_path): | ||
342 | if not self.path_included(item_path): | ||
343 | return | ||
344 | |||
345 | self[item_path][self.COL_INC] = False | ||
346 | |||
347 | item_name = self[item_path][self.COL_NAME] | ||
348 | item_deps = self[item_path][self.COL_RDEP] | ||
349 | if item_deps: | ||
350 | for dep in item_deps.split(" "): | ||
351 | if dep.startswith('('): | ||
352 | continue | ||
353 | dep_path = self.find_path_for_item(dep) | ||
354 | if not dep_path: | ||
355 | continue | ||
356 | dep_bin = self[dep_path][self.COL_BINB].split(', ') | ||
357 | if item_name in dep_bin: | ||
358 | dep_bin.remove(item_name) | ||
359 | self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ') | ||
360 | |||
361 | item_bin = self[item_path][self.COL_BINB].split(', ') | ||
362 | if item_bin: | ||
363 | for binb in item_bin: | ||
364 | binb_path = self.find_path_for_item(binb) | ||
365 | if not binb_path: | ||
366 | continue | ||
367 | self.exclude_item(binb_path) | ||
368 | |||
369 | """ | ||
370 | Empty self.contents by setting the include of each entry to None | ||
371 | """ | ||
372 | def reset(self): | ||
373 | it = self.get_iter_first() | ||
374 | while it: | ||
375 | self.set(it, | ||
376 | self.COL_INC, False, | ||
377 | self.COL_BINB, "") | ||
378 | it = self.iter_next(it) | ||
379 | |||
380 | self.selection_change_notification() | ||
381 | |||
382 | def get_selected_packages(self): | ||
383 | packagelist = [] | ||
384 | |||
385 | it = self.get_iter_first() | ||
386 | while it: | ||
387 | if self.get_value(it, self.COL_INC): | ||
388 | name = self.get_value(it, self.COL_NAME) | ||
389 | packagelist.append(name) | ||
390 | it = self.iter_next(it) | ||
391 | |||
392 | return packagelist | ||
393 | |||
394 | def get_user_selected_packages(self): | ||
395 | packagelist = [] | ||
396 | |||
397 | it = self.get_iter_first() | ||
398 | while it: | ||
399 | if self.get_value(it, self.COL_INC): | ||
400 | binb = self.get_value(it, self.COL_BINB) | ||
401 | if binb == "User Selected": | ||
402 | name = self.get_value(it, self.COL_NAME) | ||
403 | packagelist.append(name) | ||
404 | it = self.iter_next(it) | ||
405 | |||
406 | return packagelist | ||
407 | |||
408 | def get_selected_packages_toolchain(self): | ||
409 | packagelist = [] | ||
410 | |||
411 | it = self.get_iter_first() | ||
412 | while it: | ||
413 | if self.get_value(it, self.COL_INC): | ||
414 | name = self.get_value(it, self.COL_NAME) | ||
415 | if name.endswith("-dev") or name.endswith("-dbg"): | ||
416 | packagelist.append(name) | ||
417 | it = self.iter_next(it) | ||
418 | |||
419 | return list(set(packagelist + self.__toolchain_required_packages__)); | ||
420 | |||
421 | """ | ||
422 | Package model may be incomplete, therefore when calling the | ||
423 | set_selected_packages(), some packages will not be set included. | ||
424 | Return the un-set packages list. | ||
425 | """ | ||
426 | def set_selected_packages(self, packagelist, user_selected=False): | ||
427 | left = [] | ||
428 | binb = 'User Selected' if user_selected else '' | ||
429 | for pn in packagelist: | ||
430 | if pn in self.pn_path.keys(): | ||
431 | path = self.pn_path[pn] | ||
432 | self.include_item(item_path=path, binb=binb) | ||
433 | else: | ||
434 | left.append(pn) | ||
435 | |||
436 | self.selection_change_notification() | ||
437 | return left | ||
438 | |||
439 | """ | ||
440 | Return the selected package size, unit is B. | ||
441 | """ | ||
442 | def get_packages_size(self): | ||
443 | packages_size = 0 | ||
444 | it = self.get_iter_first() | ||
445 | while it: | ||
446 | if self.get_value(it, self.COL_INC): | ||
447 | str_size = self.get_value(it, self.COL_SIZE) | ||
448 | if not str_size: | ||
449 | continue | ||
450 | |||
451 | packages_size += HobPage._string_to_size(str_size) | ||
452 | |||
453 | it = self.iter_next(it) | ||
454 | return packages_size | ||
455 | |||
456 | """ | ||
457 | Resync the state of included items to a backup column before performing the fadeout visible effect | ||
458 | """ | ||
459 | def resync_fadeout_column(self, model_first_iter=None): | ||
460 | it = model_first_iter | ||
461 | while it: | ||
462 | active = self.get_value(it, self.COL_INC) | ||
463 | self.set(it, self.COL_FADE_INC, active) | ||
464 | it = self.iter_next(it) | ||
465 | |||
466 | # | ||
467 | # RecipeListModel | ||
468 | # | ||
469 | class RecipeListModel(gtk.ListStore): | ||
470 | """ | ||
471 | This class defines an gtk.ListStore subclass which will convert the output | ||
472 | of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also | ||
473 | providing convenience functions to access gtk.TreeModel subclasses which | ||
474 | provide filtered views of the data. | ||
475 | """ | ||
476 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_INSTALL, COL_PN, COL_FADE_INC, COL_SUMMARY, COL_VERSION, | ||
477 | COL_REVISION, COL_HOMEPAGE, COL_BUGTRACKER, COL_FILE) = range(18) | ||
478 | |||
479 | __custom_image__ = "Start with an empty image recipe" | ||
480 | |||
481 | __gsignals__ = { | ||
482 | "recipe-selection-changed" : (gobject.SIGNAL_RUN_LAST, | ||
483 | gobject.TYPE_NONE, | ||
484 | ()), | ||
485 | } | ||
486 | |||
487 | """ | ||
488 | """ | ||
489 | def __init__(self): | ||
490 | gtk.ListStore.__init__ (self, | ||
491 | gobject.TYPE_STRING, | ||
492 | gobject.TYPE_STRING, | ||
493 | gobject.TYPE_STRING, | ||
494 | gobject.TYPE_STRING, | ||
495 | gobject.TYPE_STRING, | ||
496 | gobject.TYPE_STRING, | ||
497 | gobject.TYPE_STRING, | ||
498 | gobject.TYPE_BOOLEAN, | ||
499 | gobject.TYPE_BOOLEAN, | ||
500 | gobject.TYPE_STRING, | ||
501 | gobject.TYPE_STRING, | ||
502 | gobject.TYPE_BOOLEAN, | ||
503 | gobject.TYPE_STRING, | ||
504 | gobject.TYPE_STRING, | ||
505 | gobject.TYPE_STRING, | ||
506 | gobject.TYPE_STRING, | ||
507 | gobject.TYPE_STRING, | ||
508 | gobject.TYPE_STRING) | ||
509 | self.sort_column_id, self.sort_order = RecipeListModel.COL_NAME, gtk.SORT_ASCENDING | ||
510 | |||
511 | """ | ||
512 | Find the model path for the item_name | ||
513 | Returns the path in the model or None | ||
514 | """ | ||
515 | def find_path_for_item(self, item_name): | ||
516 | if self.non_target_name(item_name) or item_name not in self.pn_path.keys(): | ||
517 | return None | ||
518 | else: | ||
519 | return self.pn_path[item_name] | ||
520 | |||
521 | def find_item_for_path(self, item_path): | ||
522 | return self[item_path][self.COL_NAME] | ||
523 | |||
524 | """ | ||
525 | Helper method to determine whether name is a target pn | ||
526 | """ | ||
527 | def non_target_name(self, name): | ||
528 | if name and ('-native' in name): | ||
529 | return True | ||
530 | return False | ||
531 | |||
532 | """ | ||
533 | Helper function to determine whether an item is an item specified by filter | ||
534 | """ | ||
535 | def tree_model_filter(self, model, it, filter): | ||
536 | name = model.get_value(it, self.COL_NAME) | ||
537 | if self.non_target_name(name): | ||
538 | return False | ||
539 | |||
540 | for key in filter.keys(): | ||
541 | if key == self.COL_NAME: | ||
542 | if filter[key] != 'Search recipes by name' and filter[key] != 'Search package groups by name': | ||
543 | if filter[key] not in name: | ||
544 | return False | ||
545 | else: | ||
546 | if model.get_value(it, key) not in filter[key]: | ||
547 | return False | ||
548 | self.filtered_nb += 1 | ||
549 | |||
550 | return True | ||
551 | |||
552 | def exclude_item_sort_func(self, model, iter1, iter2, user_data=None): | ||
553 | if user_data: | ||
554 | val1 = model.get_value(iter1, RecipeListModel.COL_NAME) | ||
555 | val2 = model.get_value(iter2, RecipeListModel.COL_NAME) | ||
556 | return self.cmp_vals(val1, val2, user_data) | ||
557 | else: | ||
558 | val1 = model.get_value(iter1, RecipeListModel.COL_FADE_INC) | ||
559 | val2 = model.get_value(iter2, RecipeListModel.COL_INC) | ||
560 | return ((val1 == True) and (val2 == False)) | ||
561 | |||
562 | def include_item_sort_func(self, model, iter1, iter2, user_data=None): | ||
563 | if user_data: | ||
564 | val1 = model.get_value(iter1, RecipeListModel.COL_NAME) | ||
565 | val2 = model.get_value(iter2, RecipeListModel.COL_NAME) | ||
566 | return self.cmp_vals(val1, val2, user_data) | ||
567 | else: | ||
568 | val1 = model.get_value(iter1, RecipeListModel.COL_INC) | ||
569 | val2 = model.get_value(iter2, RecipeListModel.COL_INC) | ||
570 | return ((val1 == False) and (val2 == True)) | ||
571 | |||
572 | def sort_func(self, model, iter1, iter2, user_data): | ||
573 | val1 = model.get_value(iter1, RecipeListModel.COL_NAME) | ||
574 | val2 = model.get_value(iter2, RecipeListModel.COL_NAME) | ||
575 | return self.cmp_vals(val1, val2, user_data) | ||
576 | |||
577 | def cmp_vals(self, val1, val2, user_data): | ||
578 | if val1.startswith(user_data) and not val2.startswith(user_data): | ||
579 | return -1 | ||
580 | elif not val1.startswith(user_data) and val2.startswith(user_data): | ||
581 | return 1 | ||
582 | else: | ||
583 | return cmp(val1, val2) | ||
584 | |||
585 | """ | ||
586 | Create, if required, and return a filtered gtk.TreeModelSort | ||
587 | containing only the items specified by filter | ||
588 | """ | ||
589 | def tree_model(self, filter, excluded_items_ahead=False, included_items_ahead=False, search_data=None, initial=False): | ||
590 | model = self.filter_new() | ||
591 | self.filtered_nb = 0 | ||
592 | model.set_visible_func(self.tree_model_filter, filter) | ||
593 | |||
594 | sort = gtk.TreeModelSort(model) | ||
595 | sort.connect ('sort-column-changed', self.sort_column_changed_cb) | ||
596 | if initial: | ||
597 | sort.set_sort_column_id(RecipeListModel.COL_NAME, gtk.SORT_ASCENDING) | ||
598 | sort.set_default_sort_func(None) | ||
599 | elif excluded_items_ahead: | ||
600 | sort.set_default_sort_func(self.exclude_item_sort_func, search_data) | ||
601 | elif included_items_ahead: | ||
602 | sort.set_default_sort_func(self.include_item_sort_func, search_data) | ||
603 | else: | ||
604 | if search_data and search_data!='Search recipes by name' and search_data!='Search package groups by name': | ||
605 | sort.set_default_sort_func(self.sort_func, search_data) | ||
606 | else: | ||
607 | sort.set_sort_column_id(self.sort_column_id, self.sort_order) | ||
608 | sort.set_default_sort_func(None) | ||
609 | |||
610 | sort.set_sort_func(RecipeListModel.COL_INC, self.sort_column, RecipeListModel.COL_INC) | ||
611 | sort.set_sort_func(RecipeListModel.COL_GROUP, self.sort_column, RecipeListModel.COL_GROUP) | ||
612 | sort.set_sort_func(RecipeListModel.COL_BINB, self.sort_binb_column) | ||
613 | sort.set_sort_func(RecipeListModel.COL_LIC, self.sort_column, RecipeListModel.COL_LIC) | ||
614 | return sort | ||
615 | |||
616 | def sort_column_changed_cb (self, data): | ||
617 | self.sort_column_id, self.sort_order = data.get_sort_column_id () | ||
618 | |||
619 | def sort_column(self, model, row1, row2, col): | ||
620 | value1 = model.get_value(row1, col) | ||
621 | value2 = model.get_value(row2, col) | ||
622 | cmp_res = cmp(value1, value2) | ||
623 | if cmp_res!=0: | ||
624 | if col==RecipeListModel.COL_INC: | ||
625 | return -cmp_res | ||
626 | else: | ||
627 | return cmp_res | ||
628 | else: | ||
629 | name1 = model.get_value(row1, RecipeListModel.COL_NAME) | ||
630 | name2 = model.get_value(row2, RecipeListModel.COL_NAME) | ||
631 | return cmp(name1,name2) | ||
632 | |||
633 | def sort_binb_column(self, model, row1, row2): | ||
634 | value1 = model.get_value(row1, RecipeListModel.COL_BINB) | ||
635 | value2 = model.get_value(row2, RecipeListModel.COL_BINB) | ||
636 | value1_list = value1.split(', ') | ||
637 | value2_list = value2.split(', ') | ||
638 | |||
639 | value1 = value1_list[0] | ||
640 | value2 = value2_list[0] | ||
641 | |||
642 | cmp_res = cmp(value1, value2) | ||
643 | if cmp_res==0: | ||
644 | cmp_size = cmp(len(value1_list), len(value2_list)) | ||
645 | if cmp_size==0: | ||
646 | name1 = model.get_value(row1, RecipeListModel.COL_NAME) | ||
647 | name2 = model.get_value(row2, RecipeListModel.COL_NAME) | ||
648 | return cmp(name1,name2) | ||
649 | else: | ||
650 | return cmp_size | ||
651 | else: | ||
652 | return cmp_res | ||
653 | |||
654 | def convert_vpath_to_path(self, view_model, view_path): | ||
655 | filtered_model_path = view_model.convert_path_to_child_path(view_path) | ||
656 | filtered_model = view_model.get_model() | ||
657 | |||
658 | # get the path of the original model | ||
659 | path = filtered_model.convert_path_to_child_path(filtered_model_path) | ||
660 | return path | ||
661 | |||
662 | def convert_path_to_vpath(self, view_model, path): | ||
663 | it = view_model.get_iter_first() | ||
664 | while it: | ||
665 | name = self.find_item_for_path(path) | ||
666 | view_name = view_model.get_value(it, RecipeListModel.COL_NAME) | ||
667 | if view_name == name: | ||
668 | view_path = view_model.get_path(it) | ||
669 | return view_path | ||
670 | it = view_model.iter_next(it) | ||
671 | return None | ||
672 | |||
673 | """ | ||
674 | The populate() function takes as input the data from a | ||
675 | bb.event.TargetsTreeGenerated event and populates the RecipeList. | ||
676 | """ | ||
677 | def populate(self, event_model): | ||
678 | # First clear the model, in case repopulating | ||
679 | self.clear() | ||
680 | |||
681 | # dummy image for prompt | ||
682 | self.set_in_list(self.__custom_image__, "Use 'Edit image recipe' to customize recipes and packages " \ | ||
683 | "to be included in your image ") | ||
684 | |||
685 | for item in event_model["pn"]: | ||
686 | name = item | ||
687 | desc = event_model["pn"][item]["description"] | ||
688 | lic = event_model["pn"][item]["license"] | ||
689 | group = event_model["pn"][item]["section"] | ||
690 | inherits = event_model["pn"][item]["inherits"] | ||
691 | summary = event_model["pn"][item]["summary"] | ||
692 | version = event_model["pn"][item]["version"] | ||
693 | revision = event_model["pn"][item]["prevision"] | ||
694 | homepage = event_model["pn"][item]["homepage"] | ||
695 | bugtracker = event_model["pn"][item]["bugtracker"] | ||
696 | filename = event_model["pn"][item]["filename"] | ||
697 | install = [] | ||
698 | |||
699 | depends = event_model["depends"].get(item, []) + event_model["rdepends-pn"].get(item, []) | ||
700 | |||
701 | if ('packagegroup.bbclass' in " ".join(inherits)): | ||
702 | atype = 'packagegroup' | ||
703 | elif ('image.bbclass' in " ".join(inherits)): | ||
704 | if name != "hob-image": | ||
705 | atype = 'image' | ||
706 | install = event_model["rdepends-pkg"].get(item, []) + event_model["rrecs-pkg"].get(item, []) | ||
707 | elif ('meta-' in name): | ||
708 | atype = 'toolchain' | ||
709 | elif (name == 'dummy-image' or name == 'dummy-toolchain'): | ||
710 | atype = 'dummy' | ||
711 | else: | ||
712 | atype = 'recipe' | ||
713 | |||
714 | self.set(self.append(), self.COL_NAME, item, self.COL_DESC, desc, | ||
715 | self.COL_LIC, lic, self.COL_GROUP, group, | ||
716 | self.COL_DEPS, " ".join(depends), self.COL_BINB, "", | ||
717 | self.COL_TYPE, atype, self.COL_INC, False, | ||
718 | self.COL_IMG, False, self.COL_INSTALL, " ".join(install), self.COL_PN, item, | ||
719 | self.COL_SUMMARY, summary, self.COL_VERSION, version, self.COL_REVISION, revision, | ||
720 | self.COL_HOMEPAGE, homepage, self.COL_BUGTRACKER, bugtracker, | ||
721 | self.COL_FILE, filename) | ||
722 | |||
723 | self.pn_path = {} | ||
724 | it = self.get_iter_first() | ||
725 | while it: | ||
726 | pn = self.get_value(it, self.COL_NAME) | ||
727 | path = self.get_path(it) | ||
728 | self.pn_path[pn] = path | ||
729 | it = self.iter_next(it) | ||
730 | |||
731 | def set_in_list(self, item, desc): | ||
732 | self.set(self.append(), self.COL_NAME, item, | ||
733 | self.COL_DESC, desc, | ||
734 | self.COL_LIC, "", self.COL_GROUP, "", | ||
735 | self.COL_DEPS, "", self.COL_BINB, "", | ||
736 | self.COL_TYPE, "image", self.COL_INC, False, | ||
737 | self.COL_IMG, False, self.COL_INSTALL, "", self.COL_PN, item, | ||
738 | self.COL_SUMMARY, "", self.COL_VERSION, "", self.COL_REVISION, "", | ||
739 | self.COL_HOMEPAGE, "", self.COL_BUGTRACKER, "") | ||
740 | self.pn_path = {} | ||
741 | it = self.get_iter_first() | ||
742 | while it: | ||
743 | pn = self.get_value(it, self.COL_NAME) | ||
744 | path = self.get_path(it) | ||
745 | self.pn_path[pn] = path | ||
746 | it = self.iter_next(it) | ||
747 | |||
748 | """ | ||
749 | Update the model, send out the notification. | ||
750 | """ | ||
751 | def selection_change_notification(self): | ||
752 | self.emit("recipe-selection-changed") | ||
753 | |||
754 | def path_included(self, item_path): | ||
755 | return self[item_path][self.COL_INC] | ||
756 | |||
757 | """ | ||
758 | Add this item, and any of its dependencies, to the image contents | ||
759 | """ | ||
760 | def include_item(self, item_path, binb="", image_contents=False): | ||
761 | if self.path_included(item_path): | ||
762 | return | ||
763 | |||
764 | item_name = self[item_path][self.COL_NAME] | ||
765 | item_deps = self[item_path][self.COL_DEPS] | ||
766 | |||
767 | self[item_path][self.COL_INC] = True | ||
768 | |||
769 | item_bin = self[item_path][self.COL_BINB].split(', ') | ||
770 | if binb and not binb in item_bin: | ||
771 | item_bin.append(binb) | ||
772 | self[item_path][self.COL_BINB] = ', '.join(item_bin).lstrip(', ') | ||
773 | |||
774 | # We want to do some magic with things which are brought in by the | ||
775 | # base image so tag them as so | ||
776 | if image_contents: | ||
777 | self[item_path][self.COL_IMG] = True | ||
778 | |||
779 | if item_deps: | ||
780 | # Ensure all of the items deps are included and, where appropriate, | ||
781 | # add this item to their COL_BINB | ||
782 | for dep in item_deps.split(" "): | ||
783 | # If the contents model doesn't already contain dep, add it | ||
784 | dep_path = self.find_path_for_item(dep) | ||
785 | if not dep_path: | ||
786 | continue | ||
787 | dep_included = self.path_included(dep_path) | ||
788 | |||
789 | if dep_included and not dep in item_bin: | ||
790 | # don't set the COL_BINB to this item if the target is an | ||
791 | # item in our own COL_BINB | ||
792 | dep_bin = self[dep_path][self.COL_BINB].split(', ') | ||
793 | if not item_name in dep_bin: | ||
794 | dep_bin.append(item_name) | ||
795 | self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ') | ||
796 | elif not dep_included: | ||
797 | self.include_item(dep_path, binb=item_name, image_contents=image_contents) | ||
798 | dep_bin = self[item_path][self.COL_BINB].split(', ') | ||
799 | if self[item_path][self.COL_NAME] in dep_bin: | ||
800 | dep_bin.remove(self[item_path][self.COL_NAME]) | ||
801 | self[item_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ') | ||
802 | |||
803 | def exclude_item(self, item_path): | ||
804 | if not self.path_included(item_path): | ||
805 | return | ||
806 | |||
807 | self[item_path][self.COL_INC] = False | ||
808 | |||
809 | item_name = self[item_path][self.COL_NAME] | ||
810 | item_deps = self[item_path][self.COL_DEPS] | ||
811 | if item_deps: | ||
812 | for dep in item_deps.split(" "): | ||
813 | dep_path = self.find_path_for_item(dep) | ||
814 | if not dep_path: | ||
815 | continue | ||
816 | dep_bin = self[dep_path][self.COL_BINB].split(', ') | ||
817 | if item_name in dep_bin: | ||
818 | dep_bin.remove(item_name) | ||
819 | self[dep_path][self.COL_BINB] = ', '.join(dep_bin).lstrip(', ') | ||
820 | |||
821 | item_bin = self[item_path][self.COL_BINB].split(', ') | ||
822 | if item_bin: | ||
823 | for binb in item_bin: | ||
824 | binb_path = self.find_path_for_item(binb) | ||
825 | if not binb_path: | ||
826 | continue | ||
827 | self.exclude_item(binb_path) | ||
828 | |||
829 | def reset(self): | ||
830 | it = self.get_iter_first() | ||
831 | while it: | ||
832 | self.set(it, | ||
833 | self.COL_INC, False, | ||
834 | self.COL_BINB, "", | ||
835 | self.COL_IMG, False) | ||
836 | it = self.iter_next(it) | ||
837 | |||
838 | self.selection_change_notification() | ||
839 | |||
840 | """ | ||
841 | Returns two lists. One of user selected recipes and the other containing | ||
842 | all selected recipes | ||
843 | """ | ||
844 | def get_selected_recipes(self): | ||
845 | allrecipes = [] | ||
846 | userrecipes = [] | ||
847 | |||
848 | it = self.get_iter_first() | ||
849 | while it: | ||
850 | if self.get_value(it, self.COL_INC): | ||
851 | name = self.get_value(it, self.COL_PN) | ||
852 | type = self.get_value(it, self.COL_TYPE) | ||
853 | if type != "image": | ||
854 | allrecipes.append(name) | ||
855 | sel = "User Selected" in self.get_value(it, self.COL_BINB) | ||
856 | if sel: | ||
857 | userrecipes.append(name) | ||
858 | it = self.iter_next(it) | ||
859 | |||
860 | return list(set(userrecipes)), list(set(allrecipes)) | ||
861 | |||
862 | def set_selected_recipes(self, recipelist): | ||
863 | for pn in recipelist: | ||
864 | if pn in self.pn_path.keys(): | ||
865 | path = self.pn_path[pn] | ||
866 | self.include_item(item_path=path, | ||
867 | binb="User Selected") | ||
868 | self.selection_change_notification() | ||
869 | |||
870 | def get_selected_image(self): | ||
871 | it = self.get_iter_first() | ||
872 | while it: | ||
873 | if self.get_value(it, self.COL_INC): | ||
874 | name = self.get_value(it, self.COL_PN) | ||
875 | type = self.get_value(it, self.COL_TYPE) | ||
876 | if type == "image": | ||
877 | sel = "User Selected" in self.get_value(it, self.COL_BINB) | ||
878 | if sel: | ||
879 | return name | ||
880 | it = self.iter_next(it) | ||
881 | return None | ||
882 | |||
883 | def set_selected_image(self, img): | ||
884 | if not img: | ||
885 | return | ||
886 | self.reset() | ||
887 | path = self.find_path_for_item(img) | ||
888 | self.include_item(item_path=path, | ||
889 | binb="User Selected", | ||
890 | image_contents=True) | ||
891 | self.selection_change_notification() | ||
892 | |||
893 | def set_custom_image_version(self, version): | ||
894 | self.custom_image_version = version | ||
895 | |||
896 | def get_custom_image_version(self): | ||
897 | return self.custom_image_version | ||
898 | |||
899 | def is_custom_image(self): | ||
900 | return self.get_selected_image() == self.__custom_image__ | ||