Viewing file: GroupSelector.py (23.18 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Copyright 2005-2007 Red Hat, Inc. # # Jeremy Katz <katzj@redhat.com> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 only # # 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import os import logging # # Python gettext: # import gettext
import gtk import gtk.glade import gtk.gdk as gdk import gobject
import yum import yum.Errors try: import repomd.mdErrors as mdErrors except ImportError: # yum 2.9.x mdErrors = yum.Errors from yum.constants import *
from rhpl.translate import _, N_, getDefaultLangs
from PackageList import listEntryString from constants import *
GLADE_FILE = "GroupSelector.glade"
def _getgladefile(fn): if os.path.exists(fn): return fn elif os.path.exists("data/%s" %(fn,)): return "data/%s" %(fn,) else: return "/usr/share/pirut/ui/%s" %(fn,)
# # Python gettext: # t = gettext.translation(I18N_DOMAIN, "/usr/share/locale", fallback = True) # _ = t.lgettext
# # Python gettext: # def getDefaultLangs(): # languages = [] # for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): # val = os.environ.get(envar) # if val: # languages = val.split(':') # break # if 'C' not in languages: # languages.append('C') # # # now normalize and expand the languages # nelangs = [] # for lang in languages: # for nelang in gettext._expand_lang(lang): # if nelang not in nelangs: # nelangs.append(nelang) # return nelangs
# kind of lame caching of translations so we don't always have # to do all the looping strs = {} def _xmltrans(base, thedict): if strs.has_key(base): return strs[base] langs = getDefaultLangs() for l in langs: if thedict.has_key(l): strs[base] = thedict[l] return strs[base] strs[base] = base return base
def _ui_comps_sort(one, two): if one.display_order > two.display_order: return 1 elif one.display_order < two.display_order: return -1 elif _xmltrans(one.name, one.translated_name) > \ _xmltrans(two.name, two.translated_name): return 1 elif _xmltrans(one.name, one.translated_name) < \ _xmltrans(two.name, two.translated_name): return -1 return 0
def _deselectPackage(ayum, group, pkg): grpid = group.groupid try: pkgs = ayum.pkgSack.returnNewestByName(pkg) except mdErrors.PackageSackError: log = logging.getLogger("yum.verbose") log.debug("no such package %s from group %s" %(pkg, self.group.groupid)) if pkgs: pkgs = ayum.bestPackagesFromList(pkgs) for po in pkgs: txmbrs = ayum.tsInfo.getMembers(pkgtup = po.pkgtup) for txmbr in txmbrs: try: txmbr.groups.remove(grpid) except ValueError: log = logging.getLogger("yum.verbose") log.debug("package %s was not marked in group %s" %(po, grpid)) if len(txmbr.groups) == 0: ayum.tsInfo.remove(po.pkgtup)
def _selectPackage(ayum, group, pkg): grpid = group.groupid try: txmbrs = ayum.install(name = pkg) except yum.Errors.InstallError, e: log = logging.getLogger("yum.verbose") log.info("No package named %s available to be installed: %s" %(pkg, e)) else: map(lambda x: x.groups.append(grpid), txmbrs)
def _groupHasPackages(grp, ayum): # this checks to see if the given group has any packages available # (ie, already installed or in the sack of available packages) # so that we don't show empty groups for p in grp.packages: try: pkgs = ayum.pkgSack.returnNewestByName(p) return True except yum.Errors.PackageSackError: pass try: pkgs = ayum.rpmdb.returnNewestByName(p) return True except (IndexError, yum.Errors.PackageSackError): pass return False
def _catHasGroupWithPackages(cat, ayum): grps = map(lambda x: ayum.comps.return_group(x), filter(lambda x: ayum.comps.has_group(x), cat.groups)) for g in grps: if _groupHasPackages(g, ayum): return True return False
class OptionalPackageSelector: def __init__(self, yumobj, group, parent = None, getgladefunc = None): self.ayum = yumobj self.group = group
if getgladefunc: xmlfn = getgladefunc(GLADE_FILE) else: xmlfn = _getgladefile(GLADE_FILE)
self.xml = gtk.glade.XML(xmlfn, "groupDetailsDialog", domain=I18N_DOMAIN)
self.window = self.xml.get_widget("groupDetailsDialog") if parent: self.window.set_transient_for(parent) self.window.set_title(_("Packages in %s") % _xmltrans(group.name, group.translated_name)) self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) self.window.set_size_request(600, 400) self._createStore() self._populate()
def __search_pkgs(self, model, col, key, i): val = model.get_value(i, 2).returnSimple('name') if val.lower().startswith(key.lower()): return False return True
def _createStore(self): self.pkgstore = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) tree = self.xml.get_widget("packageList") tree.set_model(self.pkgstore)
column = gtk.TreeViewColumn(None, None) cbr = gtk.CellRendererToggle() cbr.connect ("toggled", self._pkgToggled) column.pack_start(cbr, False) column.add_attribute(cbr, 'active', 0) tree.append_column(column) column = gtk.TreeViewColumn(None, None) renderer = gtk.CellRendererText() column.pack_start(renderer, True) column.add_attribute(renderer, 'markup', 1) tree.append_column(column) tree.set_search_equal_func(self.__search_pkgs) tree.connect("row-activated", self._rowToggle)
# Sort in _populate() # self.pkgstore.set_sort_column_id(1, gtk.SORT_ASCENDING)
def _rowToggle(self, tree, path, col): self._pkgToggled(None, path)
def _pkgToggled(self, widget, path): if type(path) == type(str): i = self.pkgstore.get_iter_from_string(path) else: i = self.pkgstore.get_iter(path) sel = self.pkgstore.get_value(i, 0) pkg = self.pkgstore.get_value(i, 2).returnSimple('name') if sel and not self.ayum.simpleDBInstalled(name = pkg): _deselectPackage(self.ayum, self.group, pkg) elif sel: self.ayum.remove(name = pkg) elif self.ayum.simpleDBInstalled(name = pkg): txmbrs = self.ayum.tsInfo.matchNaevr(name = pkg) for tx in txmbrs: if tx.output_state == TS_ERASE: self.ayum.tsInfo.remove(tx.pkgtup) else: _selectPackage(self.ayum, self.group, pkg) self.pkgstore.set_value(i, 0, not sel)
def __getPackageObject(self, pkgname): pos = self.ayum.pkgSack.searchNevra(name=pkgname) if len(pos) > 0: return pos[0] return None
def _populate(self): pkgs = self.group.default_packages.keys() + \ self.group.optional_packages.keys() tmp = set() for pkg in pkgs: po = self.__getPackageObject(pkg) if not po: continue tmp.add((po, pkg)) pkgs = tmp for po, pkg in sorted(pkgs): self.pkgstore.append([self.ayum.isPackageInstalled(pkg), listEntryString(po), po])
def run(self): self.window.show_all() return self.window.run()
def destroy(self): return self.window.destroy()
# the GroupSelector requires a YumBase object which also implements the # following additional methods: # * isPackageInstalled(p): is there a package named p installed or selected # * isGroupInstalled(grp): is there a group grp installed or selected class GroupSelector: def __init__(self, yumobj, getgladefunc = None, framefunc = None): self.ayum = yumobj
self.getgladefunc = getgladefunc self.framefunc = framefunc if getgladefunc: xmlfn = getgladefunc(GLADE_FILE) else: xmlfn = _getgladefile(GLADE_FILE)
self.xml = gtk.glade.XML(xmlfn, "groupSelectionBox", domain=I18N_DOMAIN) self.vbox = self.xml.get_widget("groupSelectionBox") self.xml.get_widget("detailsButton").set_sensitive(False)
self.menuxml = gtk.glade.XML(xmlfn, "groupPopupMenu", domain=I18N_DOMAIN) self.groupMenu = self.menuxml.get_widget("groupPopupMenu")
self._connectSignals() self._createStores() self.vbox.show()
def _connectSignals(self): sigs = { "on_detailsButton_clicked": self._optionalPackagesDialog, "on_groupList_button_press": self._groupListButtonPress, "on_groupList_popup_menu": self._groupListPopup, } self.xml.signal_autoconnect(sigs)
menusigs = { "on_select_activate": self._selectAllPackages, "on_selectgrp_activate": self._groupSelect, "on_deselectgrp_activate": self._groupDeselect, "on_deselect_activate": self._deselectAllPackages } self.menuxml.signal_autoconnect(menusigs)
def _createStores(self): self._createCategoryStore() self._createGroupStore()
b = gtk.TextBuffer() self.xml.get_widget("groupDescriptionTextView").set_buffer(b)
def _createCategoryStore(self): # display string, category object self.catstore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) tree = self.xml.get_widget("categoryList") tree.set_model(self.catstore)
renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('Text', renderer, markup=0) column.set_clickable(False) tree.append_column(column) tree.columns_autosize() tree.set_enable_search(False)
selection = tree.get_selection() selection.connect("changed", self._categorySelected)
def _createGroupStore(self): # checkbox, display string, object self.groupstore = gtk.TreeStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_OBJECT) tree = self.xml.get_widget("groupList") tree.set_model(self.groupstore)
column = gtk.TreeViewColumn(None, None) column.set_clickable(True) pixr = gtk.CellRendererPixbuf() pixr.set_property('stock-size', 1) column.pack_start(pixr, False) column.add_attribute(pixr, 'pixbuf', 3) cbr = gtk.CellRendererToggle() column.pack_start(cbr, False) column.add_attribute(cbr, 'active', 0) cbr.connect ("toggled", self._groupToggled) tree.append_column(column) renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('Text', renderer, markup=1) column.set_clickable(False) tree.append_column(column) tree.columns_autosize() tree.set_enable_search(False) tree.grab_focus()
selection = tree.get_selection() selection.connect("changed", self._groupSelected) selection.set_mode(gtk.SELECTION_MULTIPLE)
def _get_pix(self, fn): imgsize = 24 pix = gtk.gdk.pixbuf_new_from_file(fn) if pix.get_height() != imgsize or pix.get_width() != imgsize: pix = pix.scale_simple(imgsize, imgsize, gtk.gdk.INTERP_BILINEAR) return pix
def _categorySelected(self, selection): self.groupstore.clear() (model, i) = selection.get_selected() if not i: return cat = model.get_value(i, 1)
# fall back to the category pixbuf fbpix = None fn = "/usr/share/pixmaps/comps/%s.png" %(cat.categoryid,) if os.access(fn, os.R_OK): fbpix = self._get_pix(fn) self._populateGroups(cat.groups, fbpix)
def _populateGroups(self, groups, defaultpix = None): grps = map(lambda x: self.ayum.comps.return_group(x), filter(lambda x: self.ayum.comps.has_group(x), groups)) grps.sort(_ui_comps_sort) for grp in grps: if not _groupHasPackages(grp, self.ayum): continue s = "<span size=\"large\" weight=\"bold\">%s</span>" % _xmltrans(grp.name, grp.translated_name)
fn = "/usr/share/pixmaps/comps/%s.png" % grp.groupid if os.access(fn, os.R_OK): pix = self._get_pix(fn) elif defaultpix: pix = defaultpix else: pix = None self.groupstore.append(None, [self.ayum.isGroupInstalled(grp),s,grp,pix])
tree = self.xml.get_widget("groupList") gobject.idle_add(lambda x: x.scroll_to_point(0, 0), tree) self.xml.get_widget("optionalLabel").set_text("") self.xml.get_widget("detailsButton").set_sensitive(False)
# select the first group i = self.groupstore.get_iter_first() if i is not None: sel = self.xml.get_widget("groupList").get_selection() sel.select_iter(i)
def _groupSelected(self, selection): if selection.count_selected_rows() != 1: # if we have more groups (or no group) selected, then # we can't show a description or allow selecting optional self.__setGroupDescription(None) return (model, paths) = selection.get_selected_rows() grp = model.get_value(model.get_iter(paths[0]), 2) self.__setGroupDescription(grp)
def __setGroupDescription(self, grp): b = self.xml.get_widget("groupDescriptionTextView").get_buffer() b.set_text("") if grp is None: return if grp.description: txt = _xmltrans(grp.description, grp.translated_description) else: txt = _xmltrans(grp.name, grp.translated_name)
inst = 0 cnt = 0 pkgs = grp.default_packages.keys() + grp.optional_packages.keys() for p in pkgs: if self.ayum.isPackageInstalled(p): cnt += 1 inst += 1 elif self.ayum.pkgSack.searchNevra(name=p): cnt += 1 else: log = logging.getLogger("yum.verbose") log.debug("no such package %s for %s" %(p, grp.groupid))
b.set_text(txt) if cnt == 0 or not self.ayum.isGroupInstalled(grp): self.xml.get_widget("detailsButton").set_sensitive(False) self.xml.get_widget("optionalLabel").set_text("") else: self.xml.get_widget("detailsButton").set_sensitive(True) # # Python gettext: # txt = t.ngettext("%d of %d optional package selected", # "%d of %d optional packages selected", # cnt) %(inst, cnt)
# Gah, rhpl fails: if cnt != 1: txt = _("%d of %d optional packages selected") % (inst, cnt) else: txt = _("%d of %d optional package selected") % (inst, cnt) self.xml.get_widget("optionalLabel").set_markup(_("<i>%s</i>") %(txt,))
def _groupToggled(self, widget, path, sel = None, updateText = True): if type(path) == type(str): i = self.groupstore.get_iter_from_string(path) else: i = self.groupstore.get_iter(path) if sel is None: sel = not self.groupstore.get_value(i, 0) self.groupstore.set_value(i, 0, sel) grp = self.groupstore.get_value(i, 2)
self.vbox.window.set_cursor(gdk.Cursor(gdk.WATCH)) if sel: self.ayum.selectGroup(grp.groupid) else: self.ayum.deselectGroup(grp.groupid) # FIXME: this doesn't mark installed packages for removal. # we probably want that behavior with s-c-p, but not anaconda
if updateText: self.__setGroupDescription(grp)
self.vbox.window.set_cursor(None)
def populateCategories(self): self.catstore.clear() cats = self.ayum.comps.categories cats.sort(_ui_comps_sort) for cat in cats: if not _catHasGroupWithPackages(cat, self.ayum): continue s = "<span size=\"large\" weight=\"bold\">%s</span>" % _xmltrans(cat.name, cat.translated_name) self.catstore.append(None, [s, cat])
# select the first category i = self.catstore.get_iter_first() if i is not None: sel = self.xml.get_widget("categoryList").get_selection() sel.select_iter(i)
def _setupCatchallCategory(self): # FIXME: this is a bad hack, but catch groups which aren't in # a category yet are supposed to be user-visible somehow. # conceivably should be handled by yum grps = {} for g in self.ayum.comps.groups: if g.user_visible and _groupHasPackages(g, self.ayum): grps[g.groupid] = g
for cat in self.ayum.comps.categories: for g in cat.groups: if grps.has_key(g): del grps[g]
if len(grps.keys()) == 0: return c = yum.comps.Category() c.name = _("Uncategorized") c._groups = grps c.categoryid = "uncategorized"
self.ayum.comps._categories[c.categoryid] = c
def doRefresh(self): try: ncats = len(self.ayum.comps.categories) except yum.Errors.GroupsError: ncats = 0 if ncats == 0: self.xml.get_widget("categorySW").hide() try: grps = map(lambda x: x.groupid, self.ayum.comps.groups) except yum.Errors.GroupsError: grps = [] self._populateGroups(grps) else: self._setupCatchallCategory() self.populateCategories()
def _getSelectedGroup(self): """Return the selected group. NOTE: this only ever returns one group.""" selection = self.xml.get_widget("groupList").get_selection() (model, paths) = selection.get_selected_rows() for p in paths: return model.get_value(model.get_iter(p), 2) return None def _optionalPackagesDialog(self, *args): group = self._getSelectedGroup() if group is None: return
pwin = self.vbox.get_parent() # hack to find the parent window... while not isinstance(pwin, gtk.Window): pwin = pwin.get_parent() d = OptionalPackageSelector(self.ayum, group, pwin, self.getgladefunc) if self.framefunc: self.framefunc(d.window) rc = d.run() d.destroy() self.__setGroupDescription(group)
def _groupSelect(self, *args): selection = self.xml.get_widget("groupList").get_selection() if selection.count_selected_rows() == 0: return
(model, paths) = selection.get_selected_rows() for p in paths: self._groupToggled(model, p, True, False)
def _groupDeselect(self, *args): selection = self.xml.get_widget("groupList").get_selection() if selection.count_selected_rows() == 0: return
(model, paths) = selection.get_selected_rows() for p in paths: self._groupToggled(model, p, False, False)
def _selectAllPackages(self, *args): selection = self.xml.get_widget("groupList").get_selection() if selection.count_selected_rows() == 0: return (model, paths) = selection.get_selected_rows()
self.vbox.window.set_cursor(gdk.Cursor(gdk.WATCH))
for p in paths: i = model.get_iter(p) grp = model.get_value(i, 2)
# ensure the group is selected self.ayum.selectGroup(grp.groupid) model.set_value(i, 0, True) for pkg in grp.default_packages.keys() + \ grp.optional_packages.keys(): if self.ayum.isPackageInstalled(pkg): continue elif self.ayum.simpleDBInstalled(name = pkg): txmbrs = self.ayum.tsInfo.matchNaevr(name = pkg) for tx in txmbrs: if tx.output_state == TS_ERASE: self.ayum.tsInfo.remove(tx.pkgtup) else: _selectPackage(self.ayum, grp, pkg)
if len(paths) == 1: self.__setGroupDescription(grp) self.vbox.window.set_cursor(None)
def _deselectAllPackages(self, *args): selection = self.xml.get_widget("groupList").get_selection() if selection.count_selected_rows() == 0: return (model, paths) = selection.get_selected_rows() for p in paths: i = model.get_iter(p) grp = model.get_value(i, 2)
for pkg in grp.default_packages.keys() + \ grp.optional_packages.keys(): if not self.ayum.isPackageInstalled(pkg): continue elif self.ayum.simpleDBInstalled(name=pkg): self.ayum.remove(name=pkg) else: _deselectPackage(self.ayum, grp, pkg) if len(paths) == 1: self.__setGroupDescription(grp)
def __doGroupPopup(self, button, time): menu = self.groupMenu menu.popup(None, None, None, button, time) menu.show_all()
def _groupListButtonPress(self, widget, event): if event.button == 3: x = int(event.x) y = int(event.y) pthinfo = widget.get_path_at_pos(x, y) if pthinfo is not None: sel = widget.get_selection() if sel.count_selected_rows() == 1: path, col, cellx, celly = pthinfo widget.grab_focus() widget.set_cursor(path, col, 0) self.__doGroupPopup(event.button, event.time) return 1
def _groupListPopup(self, widget): sel = widget.get_selection() if sel.count_selected_rows() > 0: self.__doGroupPopup(0, 0)
|