summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/bb/ui/crumbs/tasklistmodel.py346
1 files changed, 346 insertions, 0 deletions
diff --git a/lib/bb/ui/crumbs/tasklistmodel.py b/lib/bb/ui/crumbs/tasklistmodel.py
new file mode 100644
index 000000000..a83a176dd
--- /dev/null
+++ b/lib/bb/ui/crumbs/tasklistmodel.py
@@ -0,0 +1,346 @@
+#
+# BitBake Graphical GTK User Interface
+#
+# Copyright (C) 2011 Intel Corporation
+#
+# Authored by Joshua Lock <josh@linux.intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import gtk
+import gobject
+
+class TaskListModel(gtk.ListStore):
+ """
+ This class defines an gtk.ListStore subclass which will convert the output
+ of the bb.event.TargetsTreeGenerated event into a gtk.ListStore whilst also
+ providing convenience functions to access gtk.TreeModel subclasses which
+ provide filtered views of the data.
+ """
+ (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8)
+
+ __gsignals__ = {
+ "tasklist-populated" : (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ ())
+ }
+
+ """
+ """
+ def __init__(self):
+ self.contents = None
+ self.tasks = None
+ self.packages = None
+ self.images = None
+
+ gtk.ListStore.__init__ (self,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_BOOLEAN)
+
+ """
+ Create, if required, and return a filtered gtk.TreeModel
+ containing only the items which are to be included in the
+ image
+ """
+ def contents_model(self):
+ if not self.contents:
+ self.contents = self.filter_new()
+ self.contents.set_visible_column(self.COL_INC)
+ return self.contents
+
+ """
+ Helper function to determine whether an item is a task
+ """
+ def task_model_filter(self, model, it):
+ if model.get_value(it, self.COL_TYPE) == 'task':
+ return True
+ else:
+ return False
+
+ """
+ Create, if required, and return a filtered gtk.TreeModel
+ containing only the items which are tasks
+ """
+ def tasks_model(self):
+ if not self.tasks:
+ self.tasks = self.filter_new()
+ self.tasks.set_visible_func(self.task_model_filter)
+ return self.tasks
+
+ """
+ Helper function to determine whether an item is an image
+ """
+ def image_model_filter(self, model, it):
+ if model.get_value(it, self.COL_TYPE) == 'image':
+ return True
+ else:
+ return False
+
+ """
+ Create, if required, and return a filtered gtk.TreeModel
+ containing only the items which are images
+ """
+ def images_model(self):
+ if not self.images:
+ self.images = self.filter_new()
+ self.images.set_visible_func(self.image_model_filter)
+ return self.images
+
+ """
+ Helper function to determine whether an item is a package
+ """
+ def package_model_filter(self, model, it):
+ if model.get_value(it, self.COL_TYPE) == 'package':
+ return True
+ else:
+ return False
+
+ """
+ Create, if required, and return a filtered gtk.TreeModel
+ containing only the items which are packages
+ """
+ def packages_model(self):
+ if not self.packages:
+ self.packages = self.filter_new()
+ self.packages.set_visible_func(self.package_model_filter)
+ return self.packages
+
+ """
+ The populate() function takes as input the data from a
+ bb.event.TargetsTreeGenerated event and populates the TaskList.
+ Once the population is done it emits gsignal tasklist-populated
+ to notify any listeners that the model is ready
+ """
+ def populate(self, event_model):
+ for item in event_model["pn"]:
+ atype = 'package'
+ name = item
+ summary = event_model["pn"][item]["summary"]
+ license = event_model["pn"][item]["license"]
+ group = event_model["pn"][item]["section"]
+
+ depends = event_model["depends"].get(item, "")
+ rdepends = event_model["rdepends-pn"].get(item, "")
+ depends = depends + rdepends
+ self.squish(depends)
+ deps = " ".join(depends)
+
+ if name.count('task-') > 0:
+ atype = 'task'
+ elif name.count('-image-') > 0:
+ atype = 'image'
+
+ self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary,
+ self.COL_LIC, license, self.COL_GROUP, group,
+ self.COL_DEPS, deps, self.COL_BINB, "",
+ self.COL_TYPE, atype, self.COL_INC, False)
+
+ self.emit("tasklist-populated")
+
+ """
+ squish lst so that it doesn't contain any duplicates
+ """
+ def squish(self, lst):
+ seen = {}
+ for l in lst:
+ seen[l] = 1
+ return seen.keys()
+
+ """
+ Mark the item at path as not included
+ NOTE:
+ path should be a gtk.TreeModelPath into self (not a filtered model)
+ """
+ def remove_item_path(self, path):
+ self[path][self.COL_BINB] = ""
+ self[path][self.COL_INC] = False
+
+ """
+ """
+ def mark(self, path):
+ name = self[path][self.COL_NAME]
+ it = self.get_iter_first()
+ removals = []
+ #print("Removing %s" % name)
+
+ self.remove_item_path(path)
+
+ # Remove all dependent packages, update binb
+ while it:
+ path = self.get_path(it)
+ # FIXME: need to ensure partial name matching doesn't happen, regexp?
+ if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name):
+ #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name))
+ # found a dependency, remove it
+ self.mark(path)
+ if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name):
+ binb = self.find_alt_dependency(self[path][self.COL_NAME])
+ #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb))
+ self[path][self.COL_BINB] = binb
+ it = self.iter_next(it)
+
+ """
+ """
+ def sweep_up(self):
+ removals = []
+ it = self.get_iter_first()
+
+ while it:
+ path = self.get_path(it)
+ binb = self[path][self.COL_BINB]
+ if binb == "" or binb is None:
+ #print("Sweeping up %s" % self[path][self.COL_NAME])
+ if not path in removals:
+ removals.extend(path)
+ it = self.iter_next(it)
+
+ while removals:
+ path = removals.pop()
+ self.mark(path)
+
+ """
+ Remove an item from the contents
+ """
+ def remove_item(self, path):
+ self.mark(path)
+ self.sweep_up()
+
+ """
+ Find the name of an item in the image contents which depends on the item
+ at contents_path returns either an item name (str) or None
+ NOTE:
+ contents_path must be a path in the self.contents gtk.TreeModel
+ """
+ def find_alt_dependency(self, name):
+ it = self.get_iter_first()
+ while it:
+ # iterate all items in the model
+ path = self.get_path(it)
+ deps = self[path][self.COL_DEPS]
+ itname = self[path][self.COL_NAME]
+ inc = self[path][self.COL_INC]
+ if itname != name and inc and deps.count(name) > 0:
+ # if this item depends on the item, return this items name
+ #print("%s depends on %s" % (itname, name))
+ return itname
+ it = self.iter_next(it)
+ return ""
+
+ """
+ Convert a path in self to a path in the filtered contents model
+ """
+ def contents_path_for_path(self, path):
+ return self.contents.convert_child_path_to_path(path)
+
+ """
+ Check the self.contents gtk.TreeModel for an item
+ where COL_NAME matches item_name
+ Returns True if a match is found, False otherwise
+ """
+ def contents_includes_name(self, item_name):
+ it = self.contents.get_iter_first()
+ while it:
+ path = self.contents.get_path(it)
+ if self.contents[path][self.COL_NAME] == item_name:
+ return True
+ it = self.contents.iter_next(it)
+ return False
+
+ """
+ Add this item, and any of its dependencies, to the image contents
+ """
+ def include_item(self, item_path, binb=""):
+ name = self[item_path][self.COL_NAME]
+ deps = self[item_path][self.COL_DEPS]
+ cur_inc = self[item_path][self.COL_INC]
+ #print("Adding %s for %s dependency" % (name, binb))
+ if not cur_inc:
+ self[item_path][self.COL_INC] = True
+ self[item_path][self.COL_BINB] = binb
+ if deps:
+ #print("Dependencies of %s are %s" % (name, deps))
+ # add all of the deps and set their binb to this item
+ for dep in deps.split(" "):
+ # FIXME: this skipping virtuals can't be right? Unless we choose only to show target
+ # packages? In which case we should handle this server side...
+ # If the contents model doesn't already contain dep, add it
+ if not dep.startswith("virtual") and not self.contents_includes_name(dep):
+ path = self.find_path_for_item(dep)
+ if path:
+ self.include_item(path, name)
+ else:
+ pass
+
+ """
+ Find the model path for the item_name
+ Returns the path in the model or None
+ """
+ def find_path_for_item(self, item_name):
+ it = self.get_iter_first()
+ path = None
+ while it:
+ path = self.get_path(it)
+ if (self[path][self.COL_NAME] == item_name):
+ return path
+ else:
+ it = self.iter_next(it)
+ return None
+
+ """
+ Empty self.contents by setting the include of each entry to None
+ """
+ def reset(self):
+ it = self.contents.get_iter_first()
+ while it:
+ path = self.contents.get_path(it)
+ opath = self.contents.convert_path_to_child_path(path)
+ self[opath][self.COL_INC] = False
+ self[opath][self.COL_BINB] = ""
+ # As we've just removed the first item...
+ it = self.contents.get_iter_first()
+
+ """
+ Returns True if one of the selected tasks is an image, False otherwise
+ """
+ def targets_contains_image(self):
+ it = self.images.get_iter_first()
+ while it:
+ path = self.images.get_path(it)
+ inc = self.images[path][self.COL_INC]
+ if inc:
+ return True
+ it = self.images.iter_next(it)
+ return False
+
+ """
+ Return a list of all selected items which are not -native or -cross
+ """
+ def get_targets(self):
+ tasks = []
+
+ it = self.contents.get_iter_first()
+ while it:
+ path = self.contents.get_path(it)
+ name = self.contents[path][self.COL_NAME]
+ stype = self.contents[path][self.COL_TYPE]
+ if not name.count('-native') and not name.count('-cross'):
+ tasks.append(name)
+ it = self.contents.iter_next(it)
+ return tasks