summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--geneticone/__init__.py20
-rw-r--r--geneticone/__init__.pycbin0 -> 454 bytes
-rw-r--r--geneticone/helper.py190
-rw-r--r--geneticone/helper.pycbin0 -> 9391 bytes
-rw-r--r--geneticone/package.py202
-rw-r--r--geneticone/package.pycbin0 -> 6488 bytes
-rwxr-xr-xgui.py538
-rw-r--r--setup.py8
8 files changed, 958 insertions, 0 deletions
diff --git a/geneticone/__init__.py b/geneticone/__init__.py
new file mode 100644
index 0000000..b1ec682
--- /dev/null
+++ b/geneticone/__init__.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+import sys
+
+# insert the gentoolkit-location into syspath
+sys.path.insert(0, "/usr/lib/gentoolkit/pym")
+
+# import gentoolkit and portage
+import gentoolkit
+import portage
+
+# this is set to "var/lib/portage/world" by default - so we add the leading /
+portage.WORLD_FILE = "/"+portage.WORLD_FILE
+
+# portage tree vars
+porttree = gentoolkit.porttree
+vartree = gentoolkit.vartree
+
+# import our packages
+from helper import *
+from package import *
diff --git a/geneticone/__init__.pyc b/geneticone/__init__.pyc
new file mode 100644
index 0000000..b0b671e
--- /dev/null
+++ b/geneticone/__init__.pyc
Binary files differ
diff --git a/geneticone/helper.py b/geneticone/helper.py
new file mode 100644
index 0000000..8f801ce
--- /dev/null
+++ b/geneticone/helper.py
@@ -0,0 +1,190 @@
+#!/usr/bin/python
+
+from geneticone import *
+import geneticone
+
+import re
+import os
+
+import gentoolkit
+import portage
+from portage_util import unique_array
+
+class BlockedException (Exception):
+ pass
+
+class PackageNotFoundException (Exception):
+ pass
+
+def find_lambda (name):
+ """Returns the function needed by all the find_all_*-functions. Returns None if no name is given."""
+ if name != None:
+ return lambda x: re.match(".*"+name+".*",x)
+ else:
+ return lambda x: True
+
+def geneticize_list (list_of_packages):
+ '''to convert the output of each gentoolkit helper function is a list of *geneticone* Package objects
+ '''
+ cpvs=[x.get_cpv() for x in list_of_packages]
+ return [geneticone.Package(x) for x in cpvs]
+
+def find_best_match (search_key, only_installed = False):
+ """Finds the best match in the portage tree."""
+ t = None
+ if not only_installed:
+ t = porttree.dep_bestmatch(search_key)
+ else:
+ t = vartree.dep_bestmatch(search_key)
+ if t:
+ return geneticone.Package(t)
+ return None
+
+def find_packages (search_key, masked=False):
+ """This returns a list of packages which have to fit exactly. Additionally ranges like '<,>,=,~,!' et. al. are possible."""
+ return geneticize_list(gentoolkit.find_packages(search_key, masked))
+
+def find_installed_packages (search_key, masked=False):
+ """This returns a list of installed packages which have to fit exactly. Additionally ranges like '<,>,=,~,!' et. al. are possible."""
+ return geneticize_list(gentoolkit.find_installed_packages(search_key, masked))
+
+def find_system_packages (name=None):
+ """Returns a list-tuple (resolved_packages, unresolved_packages) for all system packages."""
+ list = gentoolkit.find_system_packages()
+ return (geneticize_list(list[0]), geneticize_list(list[1]))
+
+def find_world_packages ():
+ """Returns a list-tuple (resolved_packages, unresolved_packages) for all packages in the world-file."""
+ list = gentoolkit.find_world_packages()
+ return geneticize_list(list[0]),geneticize_list(list[1])
+
+def find_all_installed_packages (name=None):
+ """Returns a list of all installed packages matching ".*name.*".
+ Returns ALL installed packages if name is None."""
+ return geneticize_list(gentoolkit.find_all_installed_packages(find_lambda(name)))
+
+def find_all_uninstalled_packages (name=None):
+ """Returns a list of all uninstalled packages matching ".*name.*".
+ Returns ALL uninstalled packages if name is None."""
+ return geneticize_list(gentoolkit.find_all_uninstalled_packages(find_lambda(name)))
+
+def find_all_packages (name=None):
+ """Returns a list of all packages matching ".*name.*".
+ Returns ALL packages if name is None."""
+ return geneticize_list(gentoolkit.find_all_packages(find_lambda(name)))
+
+def find_all_world_files (name=None):
+ """Returns a list of all world packages matching ".*name.*".
+ Returns ALL world packages if name is None."""
+ world = filter(find_lambda(name), [x.get_cpv() for x in find_world_packages()[0]])
+ world = unique_array(world)
+ return [geneticone.Package(x) for x in world]
+
+def find_all_system_files (name=None):
+ """Returns a list of all system packages matching ".*name.*".
+ Returns ALL system packages if name is None."""
+ sys = filter(find_lambda(name), [x.get_cpv() for x in find_system_packages()[0]])
+ sys = unique_array(sys)
+ return [geneticone.Package(x) for x in sys]
+
+def list_categories (name=None):
+ """Returns a list of categories matching ".*name.*" or all categories."""
+ categories = gentoolkit.settings.categories
+ return filter(find_lambda(name), categories)
+
+def split_package_name (name):
+ """Returns a list in the form [category, name, version, revision]. Revision will
+ be 'r0' if none can be inferred. Category and version will be empty, if none can
+ be inferred."""
+ return gentoolkit.split_package_name(name)
+
+def sort_package_list(pkglist):
+ """Sorts a package list in the same manner portage does."""
+ return gentoolkit.sort_package_list(pkglist)
+
+def am_i_root ():
+ """Returns True if the current user is root, False otherwise."""
+ if os.getuid() == 0:
+ return True
+ else:
+ return False
+
+def old_export_to_dictionaries (list_of_packages):
+ '''DEPRECATED:
+ Exports some of the intrinsic data of a list of Package objects to a list of dictionaries.
+ This is meant to transmit data back to the genetic-client, just by eval()-uating the output.'''
+ dictionaries=[]
+ keys=['name','version','category','cpv','runtime_deps','compiletime_deps','postmerge_deps','is_installed', 'is_overlay', 'is_masked','mask_status', 'package_path', 'size','use_flags_when_installed','all_useflags','set_useflags']
+ package_methods=['get_name','get_version','get_category','get_cpv','get_runtime_deps', 'get_compiletime_deps','get_postmerge_deps','is_installed','is_overlay','is_masked','get_mask_status','get_package_path','size','get_use_flags','get_all_useflags','get_set_useflags']
+
+ for item in list_of_packages:
+ dictionaries.append({})
+ for key,method in zip(keys,package_methods):
+ try:
+ dictionaries[-1][key]=eval('item.'+method+'()')
+ except AttributeError: #this may happen if, for example, package is not installed and I look for the path...
+ dictionaries[-1][key]=None
+ return dictionaries
+
+def export_to_dictionaries (packages):
+ '''Exports some of the intrinsic data of a list of Package objects to a list of dictionaries.
+ This is meant to transmit data back to the genetic-client, just by eval()-uating the output.'''
+ dictionaries=[]
+
+ for item in packages:
+ dictionaries.append({})
+ for method in dir(item):
+ if (method.startswith('get_') or method.startswith('is_'))\
+ and method != 'get_dependants': # bug in gentoolkit.Package.get_dependants --> see bug #137783
+ key = method[method.index('_')+1:] # the key is everything after the first underscore
+ try:
+ dictionaries[-1][key] = eval("item."+method+"()")
+ except AttributeError: # this may happen if, for example, package is not installed and I look for the path...
+ dictionaries[-1][key] = None
+ except TypeError:
+ pass # this method takes an argument - ignore it
+ except NotImplementedError:
+ pass # this method is not implemented - ignore
+ except "Not implemented yet!":
+ pass
+ return dictionaries
+
+use_descs = {}
+local_use_descs = {}
+def get_use_desc (flag, package = None):
+ """Returns the description of a specific useflag or None if no desc was found.
+ If a package is given (in the <cat>/<name> format) the local use descriptions are searched too.
+ In the first run the dictionaries 'use_descs' and 'local_use_descs' are filled."""
+ # fill cache if needed
+ if use_descs == {} or local_use_descs == {}:
+ # read use.desc
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.desc")
+ for line in fd.readlines():
+ line = line.strip()
+ if line != "" and line[0] != '#':
+ fields = [x.strip() for x in line.split(" - ",1)]
+ if len(fields) == 2:
+ use_descs[fields[0]] = fields[1]
+
+ # read use.local.desc
+ fd = open(gentoolkit.settings["PORTDIR"]+"/profiles/use.local.desc")
+ for line in fd.readlines():
+ line = line.strip()
+ if line != "" and line[0] != '#':
+ fields = [x.strip() for x in line.split(":",1)]
+ if len(fields) == 2:
+ if not fields[0] in local_use_descs: # create
+ local_use_descs[fields[0]] = {}
+ subfields = [x.strip() for x in fields[1].split(" - ",1)]
+ if len(subfields) == 2:
+ local_use_descs[fields[0]][subfields[0]] = subfields[1]
+
+ # start
+ desc = None
+ if flag in use_descs:
+ desc = use_descs[flag]
+ if package != None:
+ if package in local_use_descs:
+ if flag in local_use_descs[package]:
+ desc = local_use_descs[package][flag]
+ return desc
diff --git a/geneticone/helper.pyc b/geneticone/helper.pyc
new file mode 100644
index 0000000..50fb7e8
--- /dev/null
+++ b/geneticone/helper.pyc
Binary files differ
diff --git a/geneticone/package.py b/geneticone/package.py
new file mode 100644
index 0000000..6ec2299
--- /dev/null
+++ b/geneticone/package.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+
+from geneticone import *
+
+import gentoolkit
+import portage
+from portage_util import unique_array
+
+class Package (gentoolkit.Package):
+ """This is just a subclass of the Package-class of gentoolkit."""
+
+ def __init__ (self, cpv):
+ gentoolkit.Package.__init__(self, cpv)
+
+ def get_mask_status(self):
+ '''gets the numeric mask status of a package
+ can be translated as string:
+ maskmodes = [ " ", " ~", " -", "M ", "M~", "M-" ]
+ (0=unmasked 1=~arch 2=-arch etc.)
+
+ This method adapted from equery 0.1.4
+ Original author: Karl Trygve Kalleberg <karltk@gentoo.org>
+ '''
+
+ pkgmask = 0
+ if self.is_masked():
+ pkgmask = pkgmask + 3
+ keywords = self.get_env_var("KEYWORDS").split()
+ if "~" + gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 1
+ elif "-*" in keywords or "-" + gentoolkit.settings["ARCH"] in keywords:
+ pkgmask = pkgmask + 2
+ return pkgmask
+
+ def get_size (self):
+ return self.size()
+
+ def get_all_useflags (self):
+ """Returns a list of _all_ useflags for this package."""
+ return self.get_env_var("IUSE").split()
+
+ def get_all_deps (self):
+ """Returns a linearised list of all first-level dependencies for this package, on
+ the form [(comparator, [use flags], cpv), ...]"""
+ return unique_array(self.get_compiletime_deps()+self.get_runtime_deps()+self.get_postmerge_deps())
+
+ def get_dep_packages (self):
+ """Returns a cpv-list of packages on which this package depends and which have not been installed yet.
+ raises: BlockedException, PackageNotFoundException."""
+ dep_pkgs = [] # the package list
+
+ # let portage do the main stuff ;)
+ # pay attention to any changes here
+ deps = portage.dep_check (self.get_env_var("RDEPEND")+" "+self.get_env_var("DEPEND"), vartree.dbapi, self._settings)
+
+ if not deps: # what is the difference to [1, []] ?
+ return []
+
+ deps = deps[1]
+
+ for dep in deps:
+ if dep[0] == '!':
+ blocked = find_installed_packages(dep[1:])
+ if blocked != []:
+ raise BlockedException, blocked[0].get_cpv()
+ else: # next flag
+ continue
+
+ pkg = find_best_match(dep)
+ if not dep:
+ raise PackageNotFoundException, dep
+ else:
+ dep_pkgs.append(pkg.get_cpv())
+
+ return dep_pkgs
+
+ def own_get_dep_packages (self, old_cpv_dict = {}):
+ # XXX: after having finished this, i realized, that there is already a portage function -.- ;
+ # will keep this in case portage changes anything
+ """Returns a list of all packages (i.e. package-cpvs) which this package depends on and which not have been installed yet.
+ Param old_cpv_dict is a {cp: version}-dictionary holding already found deps.
+ Raises a BlockedException if the package is being blocked by another installed package."""
+ # XXX: This won't find blocking dependencies
+ # XXX: Has some problems with modular X (this has a very strange ebuild) ... we should enhance _parse_deps
+ print "Actual: "+self._cpv # debug output
+
+ uses = [] # list of actual useflags / useflags the package has been installed with
+ dep_packages = [] # list of packages returned
+ dep_cpv_dict = {} # all dependencies are inserted here
+
+ # get useflags
+ if self.is_installed():
+ uses = self.get_set_useflags()
+ else:
+ uses = self.get_settings("USE")
+
+ # cycle through dependencies
+ for (comp, flags, dep_cpv) in self.get_all_deps():
+
+ # find blocking packages
+ if comp and comp[0] == '!':
+ blocked = find_installed_packages(comp[1:]+dep_cpv)
+ if blocked != []:
+ raise BlockedException, blocked[0].get_cpv()
+ else: # next flag
+ continue
+
+ # look whether this package is really required
+ needDep = True
+ for flag in flags:
+ if (flag[0] == '!' and flag[1:] in uses) or (flag[0] != '!' and flag not in uses):
+ needDep = False
+ break
+
+ if needDep: # it is ...
+ if find_installed_packages(comp+dep_cpv) == []: # ... and not installed yet
+ d = find_best_match(comp+dep_cpv)
+ if not d: # no package found
+ raise PackageNotFoundException, dep_cpv
+ if d.get_cp() not in old_cpv_dict: # ... and not found already by an other package
+ dep_cpv_dict[d.get_cp()] = d.get_version()
+ print "Dep: "+d.get_cpv() # debug
+ dep_packages.append(d.get_cpv())
+
+ for dep in dep_packages: # find dependencies for each package
+ old_cpv_dict.update(dep_cpv_dict)
+ old_cpv_dict.update({self.get_cp() : self.get_version()})
+ dep_packages += find_packages("="+dep)[0].own_get_dep_packages(old_cpv_dict)
+
+ return unique_array(dep_packages)
+
+ def get_set_useflags (self):
+ """Returns a list of the useflags enabled at installation time. If package is not installed, it returns an empty list."""
+ if self.is_installed():
+ uses = self.get_use_flags().split()
+ iuses = self.get_all_useflags()
+ set = []
+ for u in iuses:
+ if u in uses:
+ set.append(u)
+ return set
+ else:
+ return []
+
+ def get_cp (self):
+ """Returns category/package."""
+ return self.get_category()+"/"+self.get_name()
+
+ def is_masked (self):
+ """Returns True if either masked by package.mask or by profile."""
+ # XXX: Better solution than string comparison?
+ status = portage.getmaskingstatus(self._cpv)
+ if "profile" in status or "package.mask" in status:
+ return True
+ return False
+
+ def _parse_deps(self,deps,curuse=[],level=0):
+ """Modified method "_parse_deps" of gentoolkit.Package.
+ Do NOT ignore blocks."""
+ # store (comparator, [use predicates], cpv)
+ r = []
+ comparators = ["~","<",">","=","<=",">="]
+ end = len(deps)
+ i = 0
+ while i < end:
+ blocked = False
+ tok = deps[i]
+ if tok == ')':
+ return r,i
+ if tok[-1] == "?":
+ tok = tok.replace("?","")
+ sr,l = self._parse_deps(deps[i+2:],curuse=curuse+[tok],level=level+1)
+ r += sr
+ i += l + 3
+ continue
+ if tok == "||":
+ sr,l = self._parse_deps(deps[i+2:],curuse,level=level+1)
+ r += sr
+ i += l + 3
+ continue
+ # conjonction, like in "|| ( ( foo bar ) baz )" => recurse
+ if tok == "(":
+ sr,l = self._parse_deps(deps[i+1:],curuse,level=level+1)
+ r += sr
+ i += l + 2
+ continue
+ # pkg block "!foo/bar" => ignore it
+ if tok[0] == "!":
+ #i += 1
+ #continue
+ blocked = True # added
+ tok = tok[1:] # added
+ # pick out comparator, if any
+ cmp = ""
+ for c in comparators:
+ if tok.find(c) == 0:
+ cmp = c
+ if blocked: cmp = "!"+cmp # added
+ tok = tok[len(cmp):]
+ r.append((cmp,curuse,tok))
+ i += 1
+ return r,i
diff --git a/geneticone/package.pyc b/geneticone/package.pyc
new file mode 100644
index 0000000..977ef4e
--- /dev/null
+++ b/geneticone/package.pyc
Binary files differ
diff --git a/gui.py b/gui.py
new file mode 100755
index 0000000..c9e9196
--- /dev/null
+++ b/gui.py
@@ -0,0 +1,538 @@
+#!/usr/bin/python
+
+import geneticone
+
+import pygtk
+pygtk.require("2.0")
+import gtk
+
+# for doing emerge
+from subprocess import *
+
+# for the terminal
+import pty
+import vte
+
+from portage_util import unique_array
+
+class EmergeQueue:
+ """This class manages the emerge queue."""
+
+ def __init__ (self, tree = None, console = None):
+ """"tree" is a gtk.TreeStore to show the queue in; "console" is a vte.Terminal to print the output to."""
+ self.mergequeue = {}
+ self.unmergequeue = []
+ self.tree = tree
+ self.console = console
+
+ if self.tree:
+ self.emergeIt = self.tree.append(None, ["Emerge"])
+ self.unmergeIt = self.tree.append(None, ["Unmerge"])
+ else:
+ self.emergeIt = self.unmergeIt = None
+
+ def append (self, sth, unmerge = False):
+ """Appends a cpv either to the merge queue or to the unmerge-queue.
+ Also update the tree-view."""
+ if not unmerge:
+ # insert dependencies
+ pkg = geneticone.find_packages("="+sth)[0]
+ try:
+ self.mergequeue.update({sth : pkg.get_dep_packages()})
+ except geneticone.BlockedException, e :
+ blocks = e[0]
+ blockedDialog(sth, blocks)
+ return
+ else:
+ # update tree
+ if self.emergeIt:
+ pkgIt = self.tree.append(self.emergeIt, [sth])
+ for p in self.mergequeue[sth]:
+ self.tree.append(pkgIt, [p])
+ else:
+ self.unmergequeue.append(sth)
+ if self.unmergeIt: # update tree
+ self.tree.append(self.unmergeIt, [sth])
+
+ def _emerge (self, options):
+ """Calls emerge and updates the terminal."""
+ # open pty
+ (master, slave) = pty.openpty()
+ Popen("emerge "+options, stdout = slave, stderr = STDOUT, shell = True)
+ self.removeAll(self.emergeIt)
+ self.console.set_pty(master)
+
+ def emerge (self, force = False):
+ """Emerges everything in the merge-queue. If force is 'False' (default) only 'emerge -pv' is called."""
+ if len(self.mergequeue) == 0: return
+
+ list = ""
+ for k in self.mergequeue.keys():
+ list += " '='"+k+"'"
+
+ s = ""
+ if not force: s = "-pv "
+ self._emerge(s+list)
+
+ def unmerge (self, force = False):
+ """Unmerges everything in the umerge-queue. If force is 'False' (default) only "emerge -pv -C" is called."""
+ if len(self.unmergequeue) == 0: return
+
+ list = " ".join(self.unmergequeue)
+ s = ""
+ if not force: s = "-pv "
+ self._emerge("-C "+s+list)
+
+ def removeAll (self, parentIt):
+ """Removes all children of a given parent TreeIter."""
+ childIt = self.tree.iter_children(parentIt)
+
+ while childIt:
+ temp = childIt
+ childIt = self.tree.iter_next(childIt)
+ self.remove(temp)
+
+ def remove (self, it):
+ if self.tree.iter_parent(it): # NEVER remove our top stuff
+ if self.tree.get_string_from_iter(it).split(":")[0] == self.tree.get_string_from_iter(self.emergeIt):
+ del self.mergequeue[self.tree.get_value(it,0)]
+ else:
+ self.unmergequeue.remove(self.tree.get_value(it,0))
+
+ self.tree.remove(it)
+
+class PackageWindow:
+ """A window with data about a specfic package."""
+
+ def cbChanged (self, combo, data = None):
+ """Callback for the changed ComboBox.
+ It then rebuilds the useList and the checkboxes."""
+ # remove old useList
+ self.useListScroll.remove(self.useList)
+
+ # build new
+ self.useList = self.buildUseList()
+ self.useListScroll.add(self.useList)
+ self.updateCheckBoxes()
+
+ self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
+
+ # set emerge-button-label
+ if not self.actualPackage().is_installed():
+ self.emergeBtn.set_label("_Emerge")
+ else:
+ self.emergeBtn.set_label("_Unmerge")
+
+ # refresh - make window as small as possible
+ self.table.show_all()
+ self.window.resize(1,1)
+ return True
+
+ def buildVersCombo (self):
+ """Creates the combo box with the different versions."""
+ combo = gtk.combo_box_new_text()
+
+ # append versions
+ for s in [x.get_version() for x in self.packages]:
+ combo.append_text(s)
+
+ # activate the first one
+ try:
+ best_version = ""
+ if self.version:
+ best_version = self.version
+ else:
+ best_version = geneticone.find_best_match(self.packages[0].get_cp(), (self.instPackages != [])).get_version()
+ for i in range(len(self.packages)):
+ if self.packages[i].get_version() == best_version:
+ combo.set_active(i)
+ break
+ except AttributeError: # no package found
+ combo.set_active(0)
+
+ combo.connect("changed", self.cbChanged)
+
+ return combo
+
+ def actualPackage (self):
+ """Returns the actual package (a geneticone.Package-object)."""
+ return self.packages[self.vCombo.get_active()]
+
+ def cbButtonPressed (self, b, event, data = None):
+ """Callback for pressed checkboxes. Just quits the event-loop - no redrawing."""
+ b.emit_stop_by_name("button-press-event")
+ return True
+
+ def cbEmergeClicked (self, button, data = None):
+ """Adds the package to the EmergeQueue."""
+ if not geneticone.am_i_root():
+ errorMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "You cannot (un)merge without being root.")
+ errorMB.run()
+ errorMB.destroy()
+ else:
+ unmerge = self.actualPackage().is_installed()
+ self.queue.append(self.actualPackage().get_cpv(), unmerge)
+ self.window.destroy()
+ return True
+
+ def updateCheckBoxes (self):
+ """Updates the checkboxes."""
+ self.installedCheck.set_active(self.actualPackage().is_installed())
+ self.maskedCheck.set_active(self.actualPackage().is_masked())
+ self.testingCheck.set_active((self.actualPackage().get_mask_status() % 3) == 1)
+
+ def buildUseList (self):
+ """Builds the useList."""
+ store = gtk.ListStore(bool, str, str)
+
+ pkg = self.actualPackage()
+ for use in pkg.get_all_useflags():
+ if pkg.is_installed() and use in pkg.get_set_useflags(): # flags set during install
+ enabled = True
+ elif (not pkg.is_installed()) and use in pkg.get_settings("USE").split(): # flags that would be set
+ enabled = True
+ else:
+ enabled = False
+ store.append([enabled, use, geneticone.get_use_desc(use, self.cp)])
+
+ # build view
+ view = gtk.TreeView(store)
+ cell = gtk.CellRendererText()
+ tCell = gtk.CellRendererToggle()
+ view.append_column(gtk.TreeViewColumn("Enabled", tCell, active = 0))
+ view.append_column(gtk.TreeViewColumn("Flags", cell, text = 1))
+ view.append_column(gtk.TreeViewColumn("Description", cell, text = 2))
+
+ if store.iter_n_children(None) == 0:
+ view.set_child_visible(False)
+ else:
+ view.set_child_visible(True)
+ return view
+
+ def cbSizeCheck (self, event, data = None):
+ if self.useListScroll:
+ width, height = self.window.get_size()
+ if height > gtk.gdk.screen_height():
+ self.window.set_default_size(width, gtk.gdk.screen_height())
+ self.window.resize(width, gtk.gdk.screen_height())
+ self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+
+ return False
+
+ def __init__ (self, parent, cp, queue = None, version = None):
+ """Build up window contents."""
+ self.parent = parent # parent window
+ self.cp = cp # category/package
+ self.version = version # version - if not None this is used
+ self.queue = queue
+
+ # window
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.set_title(cp)
+ self.window.set_modal(True)
+ self.window.set_transient_for(parent)
+ self.window.set_destroy_with_parent(True)
+ self.window.set_resizable(False)
+ self.window.set_default_size(1,1) # as small as possible
+ self.window.connect("delete-event", lambda a,b: False)
+ #self.window.connect("configure-event", self.cbSizeCheck)
+
+ # packages and installed packages
+ self.packages = geneticone.sort_package_list(geneticone.find_packages(cp, masked=True))
+ self.instPackages = geneticone.sort_package_list(geneticone.find_installed_packages(cp, masked=True))
+
+ # main structure - the table
+ self.table = gtk.Table(rows=4,columns=2)
+ self.window.add(self.table)
+
+ # version-combo-box
+ self.vCombo = self.buildVersCombo()
+ self.table.attach(self.vCombo, 0, 1, 1, 2, yoptions = gtk.FILL)
+
+ # the label (must be here, because it depends on the combo box)
+ desc = self.actualPackage().get_env_var("DESCRIPTION")
+ use_markup = True
+ if not desc:
+ desc = "<no description>"
+ use_markup = False
+ else:
+ desc = "<b>"+desc+"</b>"
+ self.descLabel = gtk.Label(desc)
+ self.descLabel.set_line_wrap(True)
+ self.descLabel.set_justify(gtk.JUSTIFY_CENTER)
+ self.descLabel.set_use_markup(use_markup)
+ self.table.attach(self.descLabel, 0, 2, 0, 1, xoptions = gtk.FILL, ypadding = 10)
+
+ # the check boxes
+ checkHB = gtk.HBox (True, 1)
+ self.table.attach(checkHB, 1, 2, 1, 2, yoptions = gtk.FILL)
+
+ self.installedCheck = gtk.CheckButton()
+ self.installedCheck.connect("button-press-event", self.cbButtonPressed)
+ self.installedCheck.set_label("Installed")
+ checkHB.pack_start(self.installedCheck, True, False)
+
+ self.maskedCheck = gtk.CheckButton()
+ self.maskedCheck.connect("button-press-event", self.cbButtonPressed)
+ self.maskedCheck.set_label("Masked")
+ checkHB.pack_start(self.maskedCheck, True, False)
+
+ self.testingCheck = gtk.CheckButton()
+ self.testingCheck.connect("button-press-event", self.cbButtonPressed)
+ self.testingCheck.set_label("Testing")
+ checkHB.pack_start(self.testingCheck, True, False)
+
+ # use list
+ self.useList = self.buildUseList()
+ self.useListScroll = gtk.ScrolledWindow()
+ self.useListScroll.add(self.useList)
+ self.useListScroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) # XXX: make this work correctly
+ self.table.attach(self.useListScroll, 0, 2, 2, 3, ypadding = 10)
+
+ # buttons
+ buttonHB = gtk.HButtonBox()
+ buttonHB.set_layout(gtk.BUTTONBOX_SPREAD)
+ self.table.attach(buttonHB, 0, 2, 3, 4)
+
+ self.emergeBtn = gtk.Button("_Emerge")
+ if not self.queue: self.emergeBtn.set_sensitive(False)
+ self.cancelBtn = gtk.Button("_Cancel")
+ self.cancelBtn.connect("clicked", lambda x: self.window.destroy())
+ self.emergeBtn.connect("clicked", self.cbEmergeClicked)
+ buttonHB.pack_start(self.emergeBtn)
+ buttonHB.pack_start(self.cancelBtn)
+
+ # current status
+ self.cbChanged(self.vCombo)
+
+ # show
+ self.window.show_all()
+
+class MainWindow:
+ """Application main window."""
+
+ def cbDelete (self, widget, data = None):
+ """Returns false -> window is deleted."""
+ return False
+
+ def cbDestroy (self, widget, data = None):
+ """Calls main_quit()."""
+ gtk.main_quit()
+
+ def createMainMenu (self):
+ """Creates the main menu."""
+ # the menu-list
+ mainMenuDesc = [
+ ( "/_File", None, None, 0, "<Branch>"),
+ ( "/File/_Close", None, self.cbDestroy, 0, "")
+ ]
+ self.itemFactory = gtk.ItemFactory(gtk.MenuBar, "<main>", None)
+ self.itemFactory.create_items(mainMenuDesc)
+ return self.itemFactory.get_widget("<main>")
+
+ def cbCatListSelection (self, view, data = None):
+ """Callback for a category-list selection. Updates the package list with these packages in the category."""
+ if view == self.catList: # be sure it is the catList
+ # get the selected category
+ sel = view.get_selection()
+ store, it = sel.get_selected()
+
+ if it:
+ # remove old one
+ self.scroll_2.remove(self.pkgList)
+
+ # create new package list
+ self.pkgList = self.createPkgList(store.get_value(it,0))
+ self.scroll_2.add(self.pkgList)
+ self.scroll_2.show_all()
+ return False
+
+ def cbRowActivated (self, view, path, col, store = None):
+ """Callback for an activated row in the pkgList. Opens a package window."""
+ if view == self.pkgList:
+ package = store.get_value(store.get_iter(path), 0)
+ if package[-1] == '*': package = package[:-1]
+ PackageWindow(self.window, self.selCatName+"/"+package, self.queue)
+ elif view == self.emergeView:
+ if len(path) > 1:
+ package = store.get_value(store.get_iter(path), 0)
+ cat, name, vers, rev = geneticone.split_package_name(package)
+ PackageWindow(self.window, cat+"/"+name, queue = None, version = vers+"-"+rev)
+ return True
+
+ def createCatList (self):
+ """Creates the category list."""
+ store = gtk.ListStore(str)
+
+ # build categories
+ for p in geneticone.list_categories():
+ store.append([p])
+ # sort them alphabetically
+ store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+
+ view = gtk.TreeView(store)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn("Categories", cell, text = 0)
+ view.append_column(col)
+ view.connect("cursor-changed", self.cbCatListSelection)
+ view.connect("row-activated", lambda v,p,c : self.cbCatListSelection(v))
+ view.set_search_column(0)
+
+ return view
+
+ packages = {} # directory category -> [packages]
+ def createPkgList (self, name = None):
+ """Creates the package list. Gets the name of the category."""
+ self.selCatName = name # actual category
+
+ store = gtk.ListStore(str)
+
+ # calculate packages
+ if name:
+ if name not in self.packages: # only calc packages if not already done
+ self.packages[name] = []
+ for p in unique_array([x.get_name() for x in geneticone.find_all_packages(name+"/")]):
+ if geneticone.find_installed_packages(name+"/"+p, masked=True) != []:
+ p += "*" # append a '*' if the package is installed
+ self.packages[name].append(p)
+
+ for p in self.packages[name]:
+ store.append([p])
+
+ # sort alphabetically
+ store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+
+ # build view
+ pkgList = gtk.TreeView(store)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn("Packages", cell, text = 0)
+ pkgList.append_column(col)
+ pkgList.connect("row-activated", self.cbRowActivated, store)
+
+ return pkgList
+
+ def cbRemoveClicked (self, button, data = None):
+ selected = self.emergeView.get_selection()
+
+ if selected:
+ model, iter = selected.get_selected()
+
+ if not model.iter_parent(iter): # top-level
+ if model.iter_n_children(iter) > 0: # and has children which can be removed :)
+ askMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, "Do you really want to clear the whole queue?")
+ if askMB.run() == gtk.RESPONSE_YES :
+ self.queue.removeAll(iter)
+ askMB.destroy()
+ elif model.iter_parent(model.iter_parent(iter)): # this is in the 3rd level => dependency
+ infoMB = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "You cannot remove dependencies. :)")
+ infoMB.run()
+ infoMB.destroy()
+ else:
+ self.queue.remove(iter)
+
+ return True
+
+ def __init__ (self):
+ """Build up window"""
+
+ # window
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.set_title("Genetic")
+ self.window.connect("delete_event", self.cbDelete)
+ self.window.connect("destroy", self.cbDestroy)
+ self.window.set_border_width(2)
+ self.window.set_geometry_hints (self.window, min_width = 600, min_height = 700, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width())
+ self.window.set_resizable(True)
+
+ # main vb
+ vb = gtk.VBox(False, 1)
+ self.window.add(vb)
+
+ # menubar
+ menubar = self.createMainMenu()
+ vb.pack_start(menubar, False)
+
+ # VPaned holding the lists and the Terminal
+ vpaned = gtk.VPaned()
+ vpaned.set_position(400)
+ vb.pack_start(vpaned, True, True)
+
+ # a HB holding the lists
+ hb = gtk.HBox(True, 5)
+ hbFrame = gtk.Frame()
+ hbFrame.add(hb)
+ hbFrame.set_shadow_type(gtk.SHADOW_IN)
+ vpaned.pack1(hbFrame, shrink = True, resize = True)
+
+ self.scroll_1 = gtk.ScrolledWindow()
+ self.scroll_2 = gtk.ScrolledWindow()
+ self.scroll_1.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self.scroll_2.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ hb.pack_start(self.scroll_1, True, True)
+ hb.pack_start(self.scroll_2, True, True)
+
+ # create cat List
+ self.catList = self.createCatList()
+ self.scroll_1.add(self.catList)
+
+ # create pkg list
+ self.pkgList = self.createPkgList()
+ self.scroll_2.add(self.pkgList)
+
+ # queue list
+ queueVB = gtk.VBox(False, 0)
+ hb.pack_start(queueVB, True, True)
+
+ emergeStore = gtk.TreeStore(str)
+ self.emergeView = gtk.TreeView(emergeStore)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn("Queue", cell, text = 0)
+ self.emergeView.append_column(col)
+ self.emergeView.connect("row-activated", self.cbRowActivated, emergeStore)
+ queueVB.pack_start(self.emergeView, True, True)
+
+ # buttons right unter the queue list
+ buttonBox = gtk.HButtonBox()
+ queueVB.pack_start(buttonBox, False)
+ emergeBtn = gtk.Button("_Emerge")
+ emergeBtn.connect("clicked", lambda a: self.queue.emerge(force=True))
+ unmergeBtn = gtk.Button("_Unmerge")
+ unmergeBtn.connect("clicked", lambda a: self.queue.unmerge(force=True))
+ removeBtn = gtk.Button("_Remove")
+ removeBtn.connect("clicked", self.cbRemoveClicked)
+ buttonBox.pack_start(emergeBtn)
+ buttonBox.pack_start(removeBtn)
+ buttonBox.pack_start(unmergeBtn)
+
+ # the terminal
+ term = vte.Terminal()
+ term.set_scrollback_lines(1024)
+ term.set_scroll_on_output(True)
+ termBox = gtk.HBox(False, 0)
+ termScroll = gtk.VScrollbar(term.get_adjustment())
+ termBox.pack_start(term, True, True)
+ termBox.pack_start(termScroll, False)
+
+ termFrame = gtk.Frame("Console")
+ termFrame.set_shadow_type(gtk.SHADOW_IN)
+ termFrame.add(termBox)
+ vpaned.pack2(termFrame, shrink = True, resize = True)
+
+ # show
+ self.window.show_all()
+
+ # set emerge queue
+ self.queue = EmergeQueue(console=term, tree = emergeStore)
+
+ def main (self):
+ """Main."""
+ gtk.main()
+
+def blockedDialog (blocked, blocks):
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, blocked+" is blocked by "+blocks+".\nPlease unmerge the blocking package.")
+ dialog.run()
+ dialog.destroy()
+
+if __name__ == "__main__":
+ m = MainWindow()
+ m.main()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..d21165c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,8 @@
+#!/usr/bin/python
+
+from distutils.core import setup
+
+setup(name="Genetic/One",
+ version="svn",
+ packages=["geneticone"]
+ )