diff options
author | René 'Necoro' Neumann <necoro@necoro.net> | 2008-09-02 13:01:17 +0200 |
---|---|---|
committer | René 'Necoro' Neumann <necoro@necoro.net> | 2008-09-02 13:01:17 +0200 |
commit | afa1de13f0576ace6dcbb0176490fd20922950cd (patch) | |
tree | 056a5fd646f53dfa83f2fe231ec0943747b15ffc /portato/gui | |
parent | 02d96210d9102f0cdec95b4e0f595cbd8fdd1e10 (diff) | |
download | portato-afa1de13f0576ace6dcbb0176490fd20922950cd.tar.gz portato-afa1de13f0576ace6dcbb0176490fd20922950cd.tar.bz2 portato-afa1de13f0576ace6dcbb0176490fd20922950cd.zip |
Switch from tabs to 4 spaces
Diffstat (limited to '')
-rw-r--r-- | portato/gui/__init__.py | 18 | ||||
-rw-r--r-- | portato/gui/dialogs.py | 134 | ||||
-rw-r--r-- | portato/gui/exception_handling.py | 196 | ||||
-rw-r--r-- | portato/gui/queue.py | 1234 | ||||
-rw-r--r-- | portato/gui/session.py | 16 | ||||
-rw-r--r-- | portato/gui/updater.py | 200 | ||||
-rw-r--r-- | portato/gui/utils.py | 530 | ||||
-rw-r--r-- | portato/gui/views.py | 214 | ||||
-rw-r--r-- | portato/gui/windows/about.py | 16 | ||||
-rw-r--r-- | portato/gui/windows/basic.py | 180 | ||||
-rw-r--r-- | portato/gui/windows/mailinfo.py | 106 | ||||
-rw-r--r-- | portato/gui/windows/main.py | 3482 | ||||
-rw-r--r-- | portato/gui/windows/plugin.py | 316 | ||||
-rw-r--r-- | portato/gui/windows/preference.py | 384 | ||||
-rw-r--r-- | portato/gui/windows/search.py | 98 | ||||
-rw-r--r-- | portato/gui/windows/splash.py | 60 | ||||
-rw-r--r-- | portato/gui/windows/update.py | 188 | ||||
-rw-r--r-- | portato/gui/wrapper.py | 622 |
18 files changed, 3997 insertions, 3997 deletions
diff --git a/portato/gui/__init__.py b/portato/gui/__init__.py index 0df890c..5bbe4c8 100644 --- a/portato/gui/__init__.py +++ b/portato/gui/__init__.py @@ -16,13 +16,13 @@ from .. import get_listener from .exception_handling import register_ex_handler def run (): - from .windows.splash import SplashScreen - s = SplashScreen(_("Loading Backend")) + from .windows.splash import SplashScreen + s = SplashScreen(_("Loading Backend")) - register_ex_handler() - s.show() - - from .windows.main import MainWindow - m = MainWindow(s) - s.hide() - m.main() + register_ex_handler() + s.show() + + from .windows.main import MainWindow + m = MainWindow(s) + s.hide() + m.main() diff --git a/portato/gui/dialogs.py b/portato/gui/dialogs.py index bf7acc7..f178d2b 100644 --- a/portato/gui/dialogs.py +++ b/portato/gui/dialogs.py @@ -14,95 +14,95 @@ import gtk from ..helper import error def mail_failure_dialog(e): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Mail could not be sent")) - dialog.format_secondary_text(_("The error was: %s") % e) - ret = dialog.run() - dialog.destroy() - return ret + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Mail could not be sent")) + dialog.format_secondary_text(_("The error was: %s") % e) + ret = dialog.run() + dialog.destroy() + return ret def queue_not_empty_dialog(): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("Do you really want to quit?")) - dialog.format_secondary_text(_("There are some packages in the emerge queue and/or an emerge process is running.")) - dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) - ret = dialog.run() - dialog.destroy() - return ret + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("Do you really want to quit?")) + dialog.format_secondary_text(_("There are some packages in the emerge queue and/or an emerge process is running.")) + dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) + ret = dialog.run() + dialog.destroy() + return ret def io_ex_dialog (io_ex): - string = io_ex.strerror - if io_ex.filename: - string = string+": "+io_ex.filename - - error(string) - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, string) - ret = dialog.run() - dialog.destroy() - return ret + string = io_ex.strerror + if io_ex.filename: + string = string+": "+io_ex.filename + + error(string) + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, string) + ret = dialog.run() + dialog.destroy() + return ret def blocked_dialog (blocked, blocks): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.") % {"blocked":blocked, "blocks" : blocks}) - dialog.format_secondary_text(_("Please unmerge the blocking package.")) - ret = dialog.run() - dialog.destroy() - return ret + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.") % {"blocked":blocked, "blocks" : blocks}) + dialog.format_secondary_text(_("Please unmerge the blocking package.")) + ret = dialog.run() + dialog.destroy() + return ret def not_root_dialog (): - errorMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You are not root.")) - ret = errorMB.run() - errorMB.destroy() - return ret + errorMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You are not root.")) + ret = errorMB.run() + errorMB.destroy() + return ret def unmask_dialog (cpv): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.") % cpv ) - dialog.format_secondary_text(_("Do you want to unmask it and its dependencies?")) - ret = dialog.run() - dialog.destroy() - return ret + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.") % cpv ) + dialog.format_secondary_text(_("Do you want to unmask it and its dependencies?")) + ret = dialog.run() + dialog.destroy() + return ret def nothing_found_dialog (): - dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Package not found!")) - ret = dialog.run() - dialog.destroy() - return ret + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Package not found!")) + ret = dialog.run() + dialog.destroy() + return ret def changed_flags_dialog (what = "flags"): - check = gtk.CheckButton(_("Do not show this dialog again.")) - hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Changed %s") % what) - hintMB.format_secondary_text(_("Portato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy.")) - hintMB.vbox.add(check) - hintMB.vbox.show_all() - ret = hintMB.run() - hintMB.destroy() + check = gtk.CheckButton(_("Do not show this dialog again.")) + hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Changed %s") % what) + hintMB.format_secondary_text(_("Portato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy.")) + hintMB.vbox.add(check) + hintMB.vbox.show_all() + ret = hintMB.run() + hintMB.destroy() - return ret, check.get_active() + return ret, check.get_active() def remove_deps_dialog (): - infoMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("You cannot remove dependencies. :)")) - ret = infoMB.run() - infoMB.destroy() - return ret + infoMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("You cannot remove dependencies. :)")) + ret = infoMB.run() + infoMB.destroy() + return ret def remove_updates_dialog(): - askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("This is the updates queue. You cannot remove single elements.\nDo you want to clear the whole queue instead?")) - ret = askMB.run() - askMB.destroy() - return ret + askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("This is the updates queue. You cannot remove single elements.\nDo you want to clear the whole queue instead?")) + ret = askMB.run() + askMB.destroy() + return ret def remove_queue_dialog (): - askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Do you really want to clear the whole queue?")) - ret = askMB.run() - askMB.destroy() - return ret + askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Do you really want to clear the whole queue?")) + ret = askMB.run() + askMB.destroy() + return ret def file_chooser_dialog (title, parent): - fc = gtk.FileChooserDialog(title = title, parent = parent, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) - fc.set_do_overwrite_confirmation(True) - ret = fc.run() + fc = gtk.FileChooserDialog(title = title, parent = parent, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + fc.set_do_overwrite_confirmation(True) + ret = fc.run() - if ret == gtk.RESPONSE_ACCEPT: - ret = fc.get_filename() - else: - ret = None + if ret == gtk.RESPONSE_ACCEPT: + ret = fc.get_filename() + else: + ret = None - fc.destroy() - return ret + fc.destroy() + return ret diff --git a/portato/gui/exception_handling.py b/portato/gui/exception_handling.py index dae95ed..df555de 100644 --- a/portato/gui/exception_handling.py +++ b/portato/gui/exception_handling.py @@ -24,108 +24,108 @@ from .windows.mailinfo import MailInfoWindow from .utils import GtkThread class UncaughtExceptionDialog(gtk.MessageDialog): - """Original idea by Gustavo Carneiro - original code: http://www.daa.com.au/pipermail/pygtk/attachments/20030828/2d304204/gtkexcepthook.py.""" - - def __init__(self, type, value, tb, thread = None): - - super(UncaughtExceptionDialog,self).__init__(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE, message_format=_("A programming error has been detected during the execution of this program.")) - self.set_title(_("Bug Detected")) - self.format_secondary_text(_("It probably isn't fatal, but should be reported to the developers nonetheless.")) - - self.add_button(_("Show Details"), 1) - self.add_button(_("Send..."), 3) - self.add_button(gtk.STOCK_SAVE_AS, 2) - self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) - - # Details - self.textview = gtk.TextView() - self.textview.set_editable(False) - self.textview.modify_font(pango.FontDescription("Monospace")) - - self.sw = gtk.ScrolledWindow(); - self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.sw.add(self.textview) - - self.tbFrame = gtk.Frame() - self.tbFrame.set_shadow_type(gtk.SHADOW_IN) - self.tbFrame.add(self.sw) - self.tbFrame.set_border_width(6) - - self.vbox.add(self.tbFrame) - - textbuffer = self.textview.get_buffer() - self.text = get_trace(type, value, tb) - if thread: - self.text = _("Exception in thread \"%(thread)s\":\n%(trace)s") % {"thread": thread, "trace": self.text} - textbuffer.set_text(self.text) - self.textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3) - - self.details = self.tbFrame - self.set_position(gtk.WIN_POS_CENTER) - self.set_gravity(gtk.gdk.GRAVITY_CENTER) - - def run (self): - while True: - resp = super(UncaughtExceptionDialog, self).run() - if resp == 1: - self.details.show_all() - self.set_response_sensitive(1, False) - elif resp == 2: - debug("Want to save") - file = file_chooser_dialog(_("Save traceback..."), self) - if file: - debug("Save to %s", file) - - try: - with open(file, "w") as f: - f.writelines(self.text) - except IOError, e: - io_ex_dialog(e) - - else: - debug("Nothing to save") - elif resp == 3: - debug("Send bug per mail") - self.destroy() - MailInfoWindow(None, self.text) - return - else: - break - self.destroy() + """Original idea by Gustavo Carneiro - original code: http://www.daa.com.au/pipermail/pygtk/attachments/20030828/2d304204/gtkexcepthook.py.""" + + def __init__(self, type, value, tb, thread = None): + + super(UncaughtExceptionDialog,self).__init__(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE, message_format=_("A programming error has been detected during the execution of this program.")) + self.set_title(_("Bug Detected")) + self.format_secondary_text(_("It probably isn't fatal, but should be reported to the developers nonetheless.")) + + self.add_button(_("Show Details"), 1) + self.add_button(_("Send..."), 3) + self.add_button(gtk.STOCK_SAVE_AS, 2) + self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE) + + # Details + self.textview = gtk.TextView() + self.textview.set_editable(False) + self.textview.modify_font(pango.FontDescription("Monospace")) + + self.sw = gtk.ScrolledWindow(); + self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.sw.add(self.textview) + + self.tbFrame = gtk.Frame() + self.tbFrame.set_shadow_type(gtk.SHADOW_IN) + self.tbFrame.add(self.sw) + self.tbFrame.set_border_width(6) + + self.vbox.add(self.tbFrame) + + textbuffer = self.textview.get_buffer() + self.text = get_trace(type, value, tb) + if thread: + self.text = _("Exception in thread \"%(thread)s\":\n%(trace)s") % {"thread": thread, "trace": self.text} + textbuffer.set_text(self.text) + self.textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3) + + self.details = self.tbFrame + self.set_position(gtk.WIN_POS_CENTER) + self.set_gravity(gtk.gdk.GRAVITY_CENTER) + + def run (self): + while True: + resp = super(UncaughtExceptionDialog, self).run() + if resp == 1: + self.details.show_all() + self.set_response_sensitive(1, False) + elif resp == 2: + debug("Want to save") + file = file_chooser_dialog(_("Save traceback..."), self) + if file: + debug("Save to %s", file) + + try: + with open(file, "w") as f: + f.writelines(self.text) + except IOError, e: + io_ex_dialog(e) + + else: + debug("Nothing to save") + elif resp == 3: + debug("Send bug per mail") + self.destroy() + MailInfoWindow(None, self.text) + return + else: + break + self.destroy() def convert (version): - """Converts a version given as int-tuple to a normal version string.""" - return ".".join(map(str, version)) + """Converts a version given as int-tuple to a normal version string.""" + return ".".join(map(str, version)) def get_version_infos(): - from ..constants import VERSION - from ..backend import system - - return "\n".join(( - "Portato version: %s" % VERSION, - "Python version: %s" % sys.version, - "Used backend: %s" % system.get_version(), - "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)), - "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)))) + from ..constants import VERSION + from ..backend import system + + return "\n".join(( + "Portato version: %s" % VERSION, + "Python version: %s" % sys.version, + "Used backend: %s" % system.get_version(), + "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)), + "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version)))) def get_trace(type, value, tb): - trace = StringIO() - traceback.print_exception(type, value, tb, None, trace) - traceStr = trace.getvalue() - trace.close() - return traceStr + "\n" + get_version_infos() - + trace = StringIO() + traceback.print_exception(type, value, tb, None, trace) + traceStr = trace.getvalue() + trace.close() + return traceStr + "\n" + get_version_infos() + def register_ex_handler(): - - def handler(type, val, tb, thread = None): - def run_dialog(): - UncaughtExceptionDialog(type, val, tb, thread).run() - - if thread: - error(_("Exception in thread \"%(thread)s\":\n%(trace)s"), {"thread": thread, "trace": get_trace(type, val, tb)}) - else: - error(_("Exception:\n%s"), get_trace(type, val, tb)) - - gobject.idle_add(run_dialog) - - sys.excepthook = handler + + def handler(type, val, tb, thread = None): + def run_dialog(): + UncaughtExceptionDialog(type, val, tb, thread).run() + + if thread: + error(_("Exception in thread \"%(thread)s\":\n%(trace)s"), {"thread": thread, "trace": get_trace(type, val, tb)}) + else: + error(_("Exception:\n%s"), get_trace(type, val, tb)) + + gobject.idle_add(run_dialog) + + sys.excepthook = handler diff --git a/portato/gui/queue.py b/portato/gui/queue.py index b5fb736..5ff600f 100644 --- a/portato/gui/queue.py +++ b/portato/gui/queue.py @@ -31,620 +31,620 @@ from .updater import Updater from .wrapper import GtkConsole, GtkTree class EmergeQueue: - """This class manages the emerge queue.""" - - def __init__ (self, tree = None, console = None, db = None, title_update = None, threadClass = threading.Thread): - """Constructor. - - @param tree: Tree to append all the items to. - @type tree: GtkTree - @param console: Output is shown here. - @type console: GtkConsole - @param db: A database instance. - @type db: Database - @param title_update: A function, which will be called whenever there is a title update. - @type title_update: function(string)""" - - # the different queues - self.mergequeue = [] # for emerge - self.unmergequeue = [] # for emerge -C - self.oneshotmerge = [] # for emerge --oneshot - - # the emerge process - self.process = None - self.threadQueue = WaitingQueue(threadClass = threadClass) - self.pty = None - - # dictionaries with data about the packages in the queue - self.iters = {"install" : {}, "uninstall" : {}, "update" : {}} # iterator in the tree - self.deps = {"install" : {}, "update" : {}} # all the deps of the package - self.blocks = {"install" : OrderedDict(), "update" : OrderedDict()} - - # member vars - self.tree = tree - if self.tree and not isinstance(self.tree, GtkTree): raise TypeError, "tree passed is not a GtkTree-object" - - self.console = console - if self.console and not isinstance(self.console, GtkConsole): raise TypeError, "console passed is not a GtkConsole-object" - - self.db = db - self.title_update = title_update - self.threadClass = threadClass - - if self.console: - self.pty = pty.openpty() - self.console.set_pty(self.pty[0]) - - def _get_pkg_from_cpv (self, cpv, unmask = False): - """Gets a L{backend.Package}-object from a cpv. - - @param cpv: the cpv to get the package for - @type cpv: string (cpv) - @param unmask: if True we will look for masked packages if we cannot find unmasked ones - @type unmask: boolean - @return: created package - @rtype: backend.Package - - @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked.""" - - # for the beginning: let us create a package object - but it is not guaranteed, that it actually exists in portage - pkg = system.new_package(cpv) - masked = not (pkg.is_masked() or pkg.is_testing(use_keywords=True)) # we are setting this to True in case we have unmasked it already, but portage does not know this - - # and now try to find it in portage - pkg = system.find_packages("="+cpv, masked = masked) - - if pkg: # gotcha - pkg = pkg[0] - - elif unmask: # no pkg returned, but we are allowed to unmask it - pkg = system.find_packages("="+cpv, masked = True) - - if not pkg: - raise backend.PackageNotFoundException(cpv) # also not found - else: - pkg = pkg[0] - - if pkg.is_testing(use_keywords = True): - pkg.set_testing(True) - if pkg.is_masked(): - pkg.set_masked() - - else: # no pkg returned - and we are not allowed to unmask - raise backend.PackageNotFoundException(cpv) - - return pkg - - def update_tree (self, it, cpv, unmask = False, oneshot = False, type = "install"): - """This updates the tree recursivly, or? Isn't it? Bjorn! - - @param it: iterator where to append - @type it: Iterator - @param cpv: The package to append. - @type cpv: string (cat/pkg-ver) - @param unmask: True if we are allowed to look for masked packages - @type unmask: boolean - @param oneshot: True if we want to emerge is oneshot - @type oneshot: boolean - @param type: the type of the updating - @type type: string - - @raises backend.BlockedException: When occured during dependency-calculation. - @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked.""" - - if cpv in self.deps[type]: - return # in list already and therefore it's already in the tree too - - # try to find an already installed instance - update = False - downgrade = False - uVersion = None - changedUse = [] - try: - pkg = self._get_pkg_from_cpv(cpv, unmask) - if not pkg.is_installed(): - old = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED) - if old: - old = old[0] # assume we have only one there - cmp = pkg.compare_version(old) - if cmp > 0: - update = True - elif cmp < 0: - downgrade = True - - uVersion = old.get_version() - - old_iuse = set(old.get_iuse_flags()) - new_iuse = set(pkg.get_iuse_flags()) - - for i in old_iuse.difference(new_iuse): - changedUse.append("-"+i) - - for i in new_iuse.difference(old_iuse): - changedUse.append("+"+i) - else: - old_iuse = set(pkg.get_iuse_flags(installed = True)) - new_iuse = set(pkg.get_iuse_flags(installed = False)) - - for i in old_iuse.difference(new_iuse): - changedUse.append("-"+i) - - for i in new_iuse.difference(old_iuse): - changedUse.append("+"+i) - - except backend.PackageNotFoundException, e: # package not found / package is masked -> delete current tree and re-raise the exception - if type == "update": # remove complete tree - self.remove_with_children(self.tree.first_iter(it), removeNewFlags = False) - - elif type == "install": # remove only the intentionally added package - top = self.tree.first_iter(it) - parent = self.tree.parent_iter(it) - - if parent: - while not self.tree.iter_equal(top, parent): - parent = self.tree.parent_iter(parent) - it = self.tree.parent_iter(it) - - self.remove_with_children(it, removeNewFlags = False) - - if not self.tree.iter_has_children(top): # remove completely if nothing left - self.remove(top) - raise - - # add iter - subIt = self.tree.append(it, self.tree.build_append_value(cpv, oneshot = oneshot, update = update, downgrade = downgrade, version = uVersion, useChange = changedUse)) - self.iters[type][cpv] = subIt - - # get dependencies - deps = pkg.get_dep_packages(return_blocks = True) - self.deps[type][cpv] = deps - - for d in deps: - if d[0] == "!": # block - dep = d[1:] - if not dep in self.blocks[type]: - self.blocks[type][dep] = set() - - self.blocks[type][dep].add(cpv) - else: # recursive call - self.update_tree(subIt, d, unmask, type = type) - - def append (self, cpv, type = "install", update = False, forceUpdate = False, unmask = False, oneshot = False): - """Appends a cpv either to the merge queue or to the unmerge-queue. - Also updates the tree-view. - - @param cpv: Package to add - @type cpv: string (cat/pkg-ver) - @param type: The type of this append process. Possible values are "install", "uninstall", "update". - @type type: string - @param update: Set to True if a package is going to be updated (e.g. if the use-flags changed). - @type update: boolean - @param forceUpdate: Set to True if the update should be forced. - @type forceUpdate: boolean - @param unmask: True if we are allowed to look for masked packages - @type unmask: boolean - @param oneshot: True if this package should not be added to the world-file. - @type oneshot: boolean - - @raises portato.backend.PackageNotFoundException: if trying to add a package which does not exist""" - - if type in ("install", "update"): # emerge - if update: - pkg = self._get_pkg_from_cpv(cpv, unmask) - deps = pkg.get_dep_packages(return_blocks = True) - - if not forceUpdate and cpv in self.deps[type] and deps == self.deps[type][cpv]: - return # nothing changed - return - else: - hasBeenInQueue = (cpv in self.mergequeue or cpv in self.oneshotmerge) - parentIt = self.tree.parent_iter(self.iters[type][cpv]) - - # delete it out of the tree - but NOT the changed flags - self.remove_with_children(self.iters[type][cpv], removeNewFlags = False) - - if hasBeenInQueue: # package has been in queue before - self._queue_append(cpv, oneshot) - - self.update_tree(parentIt, cpv, unmask, oneshot = oneshot, type = type) - else: # not update - if type == "install": - if self.tree: - self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot) - self._queue_append(cpv, oneshot) - elif type == "update" and self.tree: - self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot) - - # handle blocks - if self.blocks[type]: - # check whether anything blocks something in the queue - for block in self.blocks[type]: - for c in self.iters[type]: - if system.cpv_matches(c, block): - blocked = ", ".join(self.blocks[type][block]) - warning("'%s' is blocked by: %s", c, blocked) - self.remove_with_children(self.iters[type][c], False) - raise BlockedException(c, blocked) - - # - # check whether we block a version that we are going to replace nevertheless - # - - # get the blocks that block an installed package - inst = [] - for block in self.blocks[type]: - pkgs = system.find_packages(block, system.SET_INSTALLED) - if pkgs: - inst.append((pkgs, block)) - - # the slot-cp's of the packages in the queue - slots = {} - for c in self.iters[type]: - slots[system.new_package(c).get_slot_cp()] = cpv - - # check the installed blocks against the slot-cp's - for pkgs, block in inst[:]: - done = False - for pkg in pkgs: - done = False - if pkg.get_slot_cp() in slots: - debug("Block '%s' can be ignored, because the blocking package is going to be replaced with '%s'.", block, slots[pkg.get_slot_cp()]) - done = True - if done: - inst.remove((pkgs,block)) - - if inst: # there is still something left to block - for pkgs, block in inst: - blocked = ", ".join(self.blocks[type][block]) - warning("'%s' blocks the installation of: %s", pkgs[0].get_cpv(), blocked) - self.remove_with_children(self.iters[type][cpv], False) - raise BlockedException(blocked, pkgs[0].get_cpv()) - - else: # unmerge - self.unmergequeue.append(cpv) - if self.tree: # update tree - self.iters["uninstall"].update({cpv: self.tree.append(self.tree.get_unmerge_it(), self.tree.build_append_value(cpv))}) - - def _queue_append (self, cpv, oneshot = False): - """Convenience function appending a cpv either to self.mergequeue or to self.oneshotmerge. - - @param cpv: cpv to add - @type cpv: string (cpv) - @param oneshot: True if this package should not be added to the world-file. - @type oneshot: boolean""" - - if not oneshot: - if cpv not in self.mergequeue: - self.mergequeue.append(cpv) - else: - if cpv not in self.oneshotmerge: - self.oneshotmerge.append(cpv) - - def doEmerge (self, options, packages, it, *args, **kwargs): - top = None - if self.tree and it: - for v in it.itervalues(): - self.tree.set_in_progress(v) - top = self.tree.first_iter(v) - break - - self.threadQueue.put(self.__emerge, options, packages, it, top, *args, **kwargs) - - def __emerge (self, options, packages, it, top, command = None): - """Calls emerge and updates the terminal. - - @param options: options to send to emerge - @type options: string[] - @param packages: packages to emerge - @type packages: string[] - @param it: Iterators which point to these entries whose children will be removed after completion. - @type it: dict(string -> Iterator) - @param top: The top iterator - @type top: Iterator - @param command: the command to execute - default is "/usr/bin/python /usr/bin/emerge" - @type command: string[]""" - - @plugin.hook("emerge", packages = packages, command = command, console = self.console, title_update = self.title_update) - def sub_emerge(command): - if command is None: - command = system.get_merge_command() - - # open tty - if self.console: - self.console.reset() - - def pre (): - os.setsid() # new session - if self.console: - import fcntl, termios - fcntl.ioctl(self.pty[1], termios.TIOCSCTTY, 0) # set pty-slave as session tty - os.dup2(self.pty[1], 0) - os.dup2(self.pty[1], 1) - os.dup2(self.pty[1], 2) - - # get all categories that are being touched during the emerge process - cats = set(map(lambda x: x.split("/")[0], it.iterkeys())) - - # start emerge - self.process = Popen(command+options+packages, shell = False, env = system.get_environment(), preexec_fn = pre) - - # remove packages from queue - if self.tree and it and not self.tree.is_in_unmerge(top): - self.up = Updater(self, it, self.threadClass) - else: - self.up = None - - # update title - if self.console: - old_title = self.console.get_window_title() - while self.process and self.process.poll() is None: - if self.title_update : - title = self.console.get_window_title() - if title != old_title: - self.title_update(title) - old_title = title - time.sleep(0.5) - - if self.up: - self.up.stop() - if it: - self.tree.set_in_progress(top, False) - else: - self.remove(top) - elif self.tree and it: - self.remove_with_children(top) - - if self.title_update: self.title_update(None) - - if self.process is None: # someone resetted this - self.threadQueue.next() - return - else: - ret = self.process.returncode - self.process = None - self.threadQueue.next() - - @plugin.hook("after_emerge", packages = packages, retcode = ret) - def update_packages(): - if self.db: - for cat in cats: - self.db.reload(cat) - debug("Category %s refreshed", cat) - - update_packages() - - sub_emerge(command) - - def emerge (self, force = False, options = None): - """Emerges everything in the merge-queue. - - @param force: If False, '-pv' is send to emerge. Default: False. - @type force: boolean - @param options: Additional options to send to the emerge command - @type options: string[]""" - - def prepare(queue): - """Prepares the list of iterators and the list of packages.""" - list = [] - its = {} - for k in queue: - list += ["="+k] - if self.tree: - its.update({k : self.iters["install"][k]}) - - return list, its - - if self.tree: - ownit = self.iters["install"] - else: - ownit = {} - - # oneshot-queue - if self.oneshotmerge: - # prepare package-list for oneshot - list, its = prepare(self.oneshotmerge) - if not self.mergequeue :# the other one does not exist - remove completely - its = ownit - - s = system.get_oneshot_option() - if not force: s += system.get_pretend_option() - if options is not None: s += options - - self.doEmerge(s, list, its, caller = self.emerge) - - # normal queue - if self.mergequeue: - # prepare package-list - list, its = prepare(self.mergequeue) - if not self.oneshotmerge: # the other one does not exist - remove completely - its = ownit - - s = [] - if not force: s = system.get_pretend_option() - if options is not None: s += options - - self.doEmerge(s, list, its, caller = self.emerge) - - def unmerge (self, force = False, options = None): - """Unmerges everything in the umerge-queue. - - @param force: If False, '-pv' is send to emerge. Default: False. - @type force: boolean - @param options: Additional options to send to the emerge command - @type options: string[]""" - - if len(self.unmergequeue) == 0: return # nothing in queue - - list = self.unmergequeue[:] # copy the unmerge-queue - - # set options - s = system.get_unmerge_option() - if not force: s += system.get_pretend_option() - if options is not None: s += options - - if self.tree: - it = self.iters["uninstall"] - else: - it = {} - - self.doEmerge(s,list, it, caller = self.unmerge) - - def update_world(self, sets = ("world",), force = False, newuse = False, deep = False, options = None): - """Does an update world. newuse and deep are the arguments handed to emerge. - - @param sets: The sets to update. - @type sets: string[] - @param force: If False, '-pv' is send to emerge. Default: False. - @type force: boolean - @param newuse: If True, append newuse options - @type newuse: boolean - @param deep: If True, append deep options - @type deep: boolean - @param options: Additional options to send to the emerge command - @type options: string[]""" - - opts = system.get_update_option() - - if newuse: opts += system.get_newuse_option() - if deep: opts += system.get_deep_option() - if not force: opts += system.get_pretend_option() - if options is not None: opts += options - - if self.tree: - it = self.iters["update"] - else: - it = {} - - self.doEmerge(opts, list(sets), it, caller = self.update_world) - - def sync (self, command = None): - """Calls "emerge --sync". - - @param command: command to execute to sync. If None "emerge --sync" is taken. - @type command: string[]""" - - if command is None: - command = system.get_sync_command() - - try: - while True: - idx = command.index("&&") - self.doEmerge([],[],{}, command[:idx], caller = self.sync) - command = command[idx+1:] - except ValueError: # no && in command - self.doEmerge([],[],{}, command, caller = self.sync) - - def kill_emerge (self): - """Kills the emerge process.""" - if self.process is not None: - self.threadQueue.clear() # remove all pending emerge threads - try: - pgid = os.getpgid(self.process.pid) - os.killpg(pgid, signal.SIGTERM) - debug("Process should be terminated") - if self.process.poll() is None: - os.killpg(pgid, signal.SIGKILL) - debug("Process should be killed") - except AttributeError: - debug("AttributeError occured ==> process not exisiting - ignore") - except OSError: - debug("OSError occured ==> process already stopped - ignore") - - self.process = None - - def stop_emerge (self): - if self.process is not None: - os.killpg(os.getpgid(self.process.pid), signal.SIGSTOP) - debug("Process should be stopped") - - def continue_emerge (self): - if self.process is not None: - os.killpg(os.getpgid(self.process.pid), signal.SIGCONT) - debug("Process should continue") - - def remove_with_children (self, it, removeNewFlags = True): - """Convenience function which removes all children of an iterator and than the iterator itself. - - @param it: The iter which to remove. - @type it: Iterator - @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. - @type removeNewFlags: boolean""" - - self.remove_children(it, removeNewFlags) - self.remove(it, removeNewFlags) - - def remove_children (self, parentIt, removeNewFlags = True): - """Removes all children of a given parent TreeIter recursivly. - - @param parentIt: The iter from which to remove all children. - @type parentIt: Iterator - @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. - @type removeNewFlags: boolean""" - - childIt = self.tree.first_child_iter(parentIt) - - while childIt: - if (self.tree.iter_has_children(childIt)): # recursive call - self.remove_children(childIt, removeNewFlags) - temp = childIt - childIt = self.tree.next_iter(childIt) - self.remove(temp, removeNewFlags) - - def remove (self, it, removeNewFlags = True): - """Removes a specific item in the tree. This does not remove the top-entries. - - @param it: Iterator which points to the entry we are going to remove. - @type it: Iterator - @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. - @type removeNewFlags: boolean""" - - def __remove (type, cpv): - del self.iters[type][cpv] - try: - del self.deps[type][cpv] - except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;) - debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv) - - for key in self.blocks[type].keys(): - if cpv in self.blocks[type][key]: - self.blocks[type][key].remove(cpv) - - if not self.blocks[type][key]: # list is empty -> remove the whole key - del self.blocks[type][key] - - if removeNewFlags: # remove the changed flags - flags.remove_new_use_flags(cpv) - flags.remove_new_masked(cpv) - flags.remove_new_testing(cpv) - - if self.tree.iter_has_parent(it): - cpv = self.tree.get_value(it, self.tree.get_cpv_column()) - if self.tree.is_in_emerge(it): # Emerge - - __remove("install", cpv) - - try: - self.mergequeue.remove(cpv) - except ValueError: # this is a dependency - ignore - try: - self.oneshotmerge.remove(cpv) - except ValueError: - debug("Catched ValueError => %s seems not to be in merge-queue. Should be no harm.", cpv) - - elif self.tree.is_in_unmerge(it): # in Unmerge - del self.iters["uninstall"][cpv] - self.unmergequeue.remove(cpv) - - elif self.tree.is_in_update(it): - __remove("update", cpv) - - - self.tree.remove(it) - - def is_empty (self): - """Checks whether the current queue is empty and not working. Therefore it looks, whether the queues are empty, - and the process is not running. - - @returns: True if everything is empty and the process is not running. - @rtype: bool""" - - return not (self.process or any(map(len, self.iters.itervalues()))) + """This class manages the emerge queue.""" + + def __init__ (self, tree = None, console = None, db = None, title_update = None, threadClass = threading.Thread): + """Constructor. + + @param tree: Tree to append all the items to. + @type tree: GtkTree + @param console: Output is shown here. + @type console: GtkConsole + @param db: A database instance. + @type db: Database + @param title_update: A function, which will be called whenever there is a title update. + @type title_update: function(string)""" + + # the different queues + self.mergequeue = [] # for emerge + self.unmergequeue = [] # for emerge -C + self.oneshotmerge = [] # for emerge --oneshot + + # the emerge process + self.process = None + self.threadQueue = WaitingQueue(threadClass = threadClass) + self.pty = None + + # dictionaries with data about the packages in the queue + self.iters = {"install" : {}, "uninstall" : {}, "update" : {}} # iterator in the tree + self.deps = {"install" : {}, "update" : {}} # all the deps of the package + self.blocks = {"install" : OrderedDict(), "update" : OrderedDict()} + + # member vars + self.tree = tree + if self.tree and not isinstance(self.tree, GtkTree): raise TypeError, "tree passed is not a GtkTree-object" + + self.console = console + if self.console and not isinstance(self.console, GtkConsole): raise TypeError, "console passed is not a GtkConsole-object" + + self.db = db + self.title_update = title_update + self.threadClass = threadClass + + if self.console: + self.pty = pty.openpty() + self.console.set_pty(self.pty[0]) + + def _get_pkg_from_cpv (self, cpv, unmask = False): + """Gets a L{backend.Package}-object from a cpv. + + @param cpv: the cpv to get the package for + @type cpv: string (cpv) + @param unmask: if True we will look for masked packages if we cannot find unmasked ones + @type unmask: boolean + @return: created package + @rtype: backend.Package + + @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked.""" + + # for the beginning: let us create a package object - but it is not guaranteed, that it actually exists in portage + pkg = system.new_package(cpv) + masked = not (pkg.is_masked() or pkg.is_testing(use_keywords=True)) # we are setting this to True in case we have unmasked it already, but portage does not know this + + # and now try to find it in portage + pkg = system.find_packages("="+cpv, masked = masked) + + if pkg: # gotcha + pkg = pkg[0] + + elif unmask: # no pkg returned, but we are allowed to unmask it + pkg = system.find_packages("="+cpv, masked = True) + + if not pkg: + raise backend.PackageNotFoundException(cpv) # also not found + else: + pkg = pkg[0] + + if pkg.is_testing(use_keywords = True): + pkg.set_testing(True) + if pkg.is_masked(): + pkg.set_masked() + + else: # no pkg returned - and we are not allowed to unmask + raise backend.PackageNotFoundException(cpv) + + return pkg + + def update_tree (self, it, cpv, unmask = False, oneshot = False, type = "install"): + """This updates the tree recursivly, or? Isn't it? Bjorn! + + @param it: iterator where to append + @type it: Iterator + @param cpv: The package to append. + @type cpv: string (cat/pkg-ver) + @param unmask: True if we are allowed to look for masked packages + @type unmask: boolean + @param oneshot: True if we want to emerge is oneshot + @type oneshot: boolean + @param type: the type of the updating + @type type: string + + @raises backend.BlockedException: When occured during dependency-calculation. + @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked.""" + + if cpv in self.deps[type]: + return # in list already and therefore it's already in the tree too + + # try to find an already installed instance + update = False + downgrade = False + uVersion = None + changedUse = [] + try: + pkg = self._get_pkg_from_cpv(cpv, unmask) + if not pkg.is_installed(): + old = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED) + if old: + old = old[0] # assume we have only one there + cmp = pkg.compare_version(old) + if cmp > 0: + update = True + elif cmp < 0: + downgrade = True + + uVersion = old.get_version() + + old_iuse = set(old.get_iuse_flags()) + new_iuse = set(pkg.get_iuse_flags()) + + for i in old_iuse.difference(new_iuse): + changedUse.append("-"+i) + + for i in new_iuse.difference(old_iuse): + changedUse.append("+"+i) + else: + old_iuse = set(pkg.get_iuse_flags(installed = True)) + new_iuse = set(pkg.get_iuse_flags(installed = False)) + + for i in old_iuse.difference(new_iuse): + changedUse.append("-"+i) + + for i in new_iuse.difference(old_iuse): + changedUse.append("+"+i) + + except backend.PackageNotFoundException, e: # package not found / package is masked -> delete current tree and re-raise the exception + if type == "update": # remove complete tree + self.remove_with_children(self.tree.first_iter(it), removeNewFlags = False) + + elif type == "install": # remove only the intentionally added package + top = self.tree.first_iter(it) + parent = self.tree.parent_iter(it) + + if parent: + while not self.tree.iter_equal(top, parent): + parent = self.tree.parent_iter(parent) + it = self.tree.parent_iter(it) + + self.remove_with_children(it, removeNewFlags = False) + + if not self.tree.iter_has_children(top): # remove completely if nothing left + self.remove(top) + raise + + # add iter + subIt = self.tree.append(it, self.tree.build_append_value(cpv, oneshot = oneshot, update = update, downgrade = downgrade, version = uVersion, useChange = changedUse)) + self.iters[type][cpv] = subIt + + # get dependencies + deps = pkg.get_dep_packages(return_blocks = True) + self.deps[type][cpv] = deps + + for d in deps: + if d[0] == "!": # block + dep = d[1:] + if not dep in self.blocks[type]: + self.blocks[type][dep] = set() + + self.blocks[type][dep].add(cpv) + else: # recursive call + self.update_tree(subIt, d, unmask, type = type) + + def append (self, cpv, type = "install", update = False, forceUpdate = False, unmask = False, oneshot = False): + """Appends a cpv either to the merge queue or to the unmerge-queue. + Also updates the tree-view. + + @param cpv: Package to add + @type cpv: string (cat/pkg-ver) + @param type: The type of this append process. Possible values are "install", "uninstall", "update". + @type type: string + @param update: Set to True if a package is going to be updated (e.g. if the use-flags changed). + @type update: boolean + @param forceUpdate: Set to True if the update should be forced. + @type forceUpdate: boolean + @param unmask: True if we are allowed to look for masked packages + @type unmask: boolean + @param oneshot: True if this package should not be added to the world-file. + @type oneshot: boolean + + @raises portato.backend.PackageNotFoundException: if trying to add a package which does not exist""" + + if type in ("install", "update"): # emerge + if update: + pkg = self._get_pkg_from_cpv(cpv, unmask) + deps = pkg.get_dep_packages(return_blocks = True) + + if not forceUpdate and cpv in self.deps[type] and deps == self.deps[type][cpv]: + return # nothing changed - return + else: + hasBeenInQueue = (cpv in self.mergequeue or cpv in self.oneshotmerge) + parentIt = self.tree.parent_iter(self.iters[type][cpv]) + + # delete it out of the tree - but NOT the changed flags + self.remove_with_children(self.iters[type][cpv], removeNewFlags = False) + + if hasBeenInQueue: # package has been in queue before + self._queue_append(cpv, oneshot) + + self.update_tree(parentIt, cpv, unmask, oneshot = oneshot, type = type) + else: # not update + if type == "install": + if self.tree: + self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot) + self._queue_append(cpv, oneshot) + elif type == "update" and self.tree: + self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot) + + # handle blocks + if self.blocks[type]: + # check whether anything blocks something in the queue + for block in self.blocks[type]: + for c in self.iters[type]: + if system.cpv_matches(c, block): + blocked = ", ".join(self.blocks[type][block]) + warning("'%s' is blocked by: %s", c, blocked) + self.remove_with_children(self.iters[type][c], False) + raise BlockedException(c, blocked) + + # + # check whether we block a version that we are going to replace nevertheless + # + + # get the blocks that block an installed package + inst = [] + for block in self.blocks[type]: + pkgs = system.find_packages(block, system.SET_INSTALLED) + if pkgs: + inst.append((pkgs, block)) + + # the slot-cp's of the packages in the queue + slots = {} + for c in self.iters[type]: + slots[system.new_package(c).get_slot_cp()] = cpv + + # check the installed blocks against the slot-cp's + for pkgs, block in inst[:]: + done = False + for pkg in pkgs: + done = False + if pkg.get_slot_cp() in slots: + debug("Block '%s' can be ignored, because the blocking package is going to be replaced with '%s'.", block, slots[pkg.get_slot_cp()]) + done = True + if done: + inst.remove((pkgs,block)) + + if inst: # there is still something left to block + for pkgs, block in inst: + blocked = ", ".join(self.blocks[type][block]) + warning("'%s' blocks the installation of: %s", pkgs[0].get_cpv(), blocked) + self.remove_with_children(self.iters[type][cpv], False) + raise BlockedException(blocked, pkgs[0].get_cpv()) + + else: # unmerge + self.unmergequeue.append(cpv) + if self.tree: # update tree + self.iters["uninstall"].update({cpv: self.tree.append(self.tree.get_unmerge_it(), self.tree.build_append_value(cpv))}) + + def _queue_append (self, cpv, oneshot = False): + """Convenience function appending a cpv either to self.mergequeue or to self.oneshotmerge. + + @param cpv: cpv to add + @type cpv: string (cpv) + @param oneshot: True if this package should not be added to the world-file. + @type oneshot: boolean""" + + if not oneshot: + if cpv not in self.mergequeue: + self.mergequeue.append(cpv) + else: + if cpv not in self.oneshotmerge: + self.oneshotmerge.append(cpv) + + def doEmerge (self, options, packages, it, *args, **kwargs): + top = None + if self.tree and it: + for v in it.itervalues(): + self.tree.set_in_progress(v) + top = self.tree.first_iter(v) + break + + self.threadQueue.put(self.__emerge, options, packages, it, top, *args, **kwargs) + + def __emerge (self, options, packages, it, top, command = None): + """Calls emerge and updates the terminal. + + @param options: options to send to emerge + @type options: string[] + @param packages: packages to emerge + @type packages: string[] + @param it: Iterators which point to these entries whose children will be removed after completion. + @type it: dict(string -> Iterator) + @param top: The top iterator + @type top: Iterator + @param command: the command to execute - default is "/usr/bin/python /usr/bin/emerge" + @type command: string[]""" + + @plugin.hook("emerge", packages = packages, command = command, console = self.console, title_update = self.title_update) + def sub_emerge(command): + if command is None: + command = system.get_merge_command() + + # open tty + if self.console: + self.console.reset() + + def pre (): + os.setsid() # new session + if self.console: + import fcntl, termios + fcntl.ioctl(self.pty[1], termios.TIOCSCTTY, 0) # set pty-slave as session tty + os.dup2(self.pty[1], 0) + os.dup2(self.pty[1], 1) + os.dup2(self.pty[1], 2) + + # get all categories that are being touched during the emerge process + cats = set(map(lambda x: x.split("/")[0], it.iterkeys())) + + # start emerge + self.process = Popen(command+options+packages, shell = False, env = system.get_environment(), preexec_fn = pre) + + # remove packages from queue + if self.tree and it and not self.tree.is_in_unmerge(top): + self.up = Updater(self, it, self.threadClass) + else: + self.up = None + + # update title + if self.console: + old_title = self.console.get_window_title() + while self.process and self.process.poll() is None: + if self.title_update : + title = self.console.get_window_title() + if title != old_title: + self.title_update(title) + old_title = title + time.sleep(0.5) + + if self.up: + self.up.stop() + if it: + self.tree.set_in_progress(top, False) + else: + self.remove(top) + elif self.tree and it: + self.remove_with_children(top) + + if self.title_update: self.title_update(None) + + if self.process is None: # someone resetted this + self.threadQueue.next() + return + else: + ret = self.process.returncode + self.process = None + self.threadQueue.next() + + @plugin.hook("after_emerge", packages = packages, retcode = ret) + def update_packages(): + if self.db: + for cat in cats: + self.db.reload(cat) + debug("Category %s refreshed", cat) + + update_packages() + + sub_emerge(command) + + def emerge (self, force = False, options = None): + """Emerges everything in the merge-queue. + + @param force: If False, '-pv' is send to emerge. Default: False. + @type force: boolean + @param options: Additional options to send to the emerge command + @type options: string[]""" + + def prepare(queue): + """Prepares the list of iterators and the list of packages.""" + list = [] + its = {} + for k in queue: + list += ["="+k] + if self.tree: + its.update({k : self.iters["install"][k]}) + + return list, its + + if self.tree: + ownit = self.iters["install"] + else: + ownit = {} + + # oneshot-queue + if self.oneshotmerge: + # prepare package-list for oneshot + list, its = prepare(self.oneshotmerge) + if not self.mergequeue :# the other one does not exist - remove completely + its = ownit + + s = system.get_oneshot_option() + if not force: s += system.get_pretend_option() + if options is not None: s += options + + self.doEmerge(s, list, its, caller = self.emerge) + + # normal queue + if self.mergequeue: + # prepare package-list + list, its = prepare(self.mergequeue) + if not self.oneshotmerge: # the other one does not exist - remove completely + its = ownit + + s = [] + if not force: s = system.get_pretend_option() + if options is not None: s += options + + self.doEmerge(s, list, its, caller = self.emerge) + + def unmerge (self, force = False, options = None): + """Unmerges everything in the umerge-queue. + + @param force: If False, '-pv' is send to emerge. Default: False. + @type force: boolean + @param options: Additional options to send to the emerge command + @type options: string[]""" + + if len(self.unmergequeue) == 0: return # nothing in queue + + list = self.unmergequeue[:] # copy the unmerge-queue + + # set options + s = system.get_unmerge_option() + if not force: s += system.get_pretend_option() + if options is not None: s += options + + if self.tree: + it = self.iters["uninstall"] + else: + it = {} + + self.doEmerge(s,list, it, caller = self.unmerge) + + def update_world(self, sets = ("world",), force = False, newuse = False, deep = False, options = None): + """Does an update world. newuse and deep are the arguments handed to emerge. + + @param sets: The sets to update. + @type sets: string[] + @param force: If False, '-pv' is send to emerge. Default: False. + @type force: boolean + @param newuse: If True, append newuse options + @type newuse: boolean + @param deep: If True, append deep options + @type deep: boolean + @param options: Additional options to send to the emerge command + @type options: string[]""" + + opts = system.get_update_option() + + if newuse: opts += system.get_newuse_option() + if deep: opts += system.get_deep_option() + if not force: opts += system.get_pretend_option() + if options is not None: opts += options + + if self.tree: + it = self.iters["update"] + else: + it = {} + + self.doEmerge(opts, list(sets), it, caller = self.update_world) + + def sync (self, command = None): + """Calls "emerge --sync". + + @param command: command to execute to sync. If None "emerge --sync" is taken. + @type command: string[]""" + + if command is None: + command = system.get_sync_command() + + try: + while True: + idx = command.index("&&") + self.doEmerge([],[],{}, command[:idx], caller = self.sync) + command = command[idx+1:] + except ValueError: # no && in command + self.doEmerge([],[],{}, command, caller = self.sync) + + def kill_emerge (self): + """Kills the emerge process.""" + if self.process is not None: + self.threadQueue.clear() # remove all pending emerge threads + try: + pgid = os.getpgid(self.process.pid) + os.killpg(pgid, signal.SIGTERM) + debug("Process should be terminated") + if self.process.poll() is None: + os.killpg(pgid, signal.SIGKILL) + debug("Process should be killed") + except AttributeError: + debug("AttributeError occured ==> process not exisiting - ignore") + except OSError: + debug("OSError occured ==> process already stopped - ignore") + + self.process = None + + def stop_emerge (self): + if self.process is not None: + os.killpg(os.getpgid(self.process.pid), signal.SIGSTOP) + debug("Process should be stopped") + + def continue_emerge (self): + if self.process is not None: + os.killpg(os.getpgid(self.process.pid), signal.SIGCONT) + debug("Process should continue") + + def remove_with_children (self, it, removeNewFlags = True): + """Convenience function which removes all children of an iterator and than the iterator itself. + + @param it: The iter which to remove. + @type it: Iterator + @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. + @type removeNewFlags: boolean""" + + self.remove_children(it, removeNewFlags) + self.remove(it, removeNewFlags) + + def remove_children (self, parentIt, removeNewFlags = True): + """Removes all children of a given parent TreeIter recursivly. + + @param parentIt: The iter from which to remove all children. + @type parentIt: Iterator + @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. + @type removeNewFlags: boolean""" + + childIt = self.tree.first_child_iter(parentIt) + + while childIt: + if (self.tree.iter_has_children(childIt)): # recursive call + self.remove_children(childIt, removeNewFlags) + temp = childIt + childIt = self.tree.next_iter(childIt) + self.remove(temp, removeNewFlags) + + def remove (self, it, removeNewFlags = True): + """Removes a specific item in the tree. This does not remove the top-entries. + + @param it: Iterator which points to the entry we are going to remove. + @type it: Iterator + @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True. + @type removeNewFlags: boolean""" + + def __remove (type, cpv): + del self.iters[type][cpv] + try: + del self.deps[type][cpv] + except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;) + debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv) + + for key in self.blocks[type].keys(): + if cpv in self.blocks[type][key]: + self.blocks[type][key].remove(cpv) + + if not self.blocks[type][key]: # list is empty -> remove the whole key + del self.blocks[type][key] + + if removeNewFlags: # remove the changed flags + flags.remove_new_use_flags(cpv) + flags.remove_new_masked(cpv) + flags.remove_new_testing(cpv) + + if self.tree.iter_has_parent(it): + cpv = self.tree.get_value(it, self.tree.get_cpv_column()) + if self.tree.is_in_emerge(it): # Emerge + + __remove("install", cpv) + + try: + self.mergequeue.remove(cpv) + except ValueError: # this is a dependency - ignore + try: + self.oneshotmerge.remove(cpv) + except ValueError: + debug("Catched ValueError => %s seems not to be in merge-queue. Should be no harm.", cpv) + + elif self.tree.is_in_unmerge(it): # in Unmerge + del self.iters["uninstall"][cpv] + self.unmergequeue.remove(cpv) + + elif self.tree.is_in_update(it): + __remove("update", cpv) + + + self.tree.remove(it) + + def is_empty (self): + """Checks whether the current queue is empty and not working. Therefore it looks, whether the queues are empty, + and the process is not running. + + @returns: True if everything is empty and the process is not running. + @rtype: bool""" + + return not (self.process or any(map(len, self.iters.itervalues()))) diff --git a/portato/gui/session.py b/portato/gui/session.py index a37cd85..1eca248 100644 --- a/portato/gui/session.py +++ b/portato/gui/session.py @@ -18,16 +18,16 @@ SESSION_VERSION = 1 class SessionException (Exception): - error = _("Version mismatch.") - def __init__ (self, got, expected): - self.got = got - self.expected = expected + error = _("Version mismatch.") + def __init__ (self, got, expected): + self.got = got + self.expected = expected - def __str__ (self): - return "%s %s" % (self.error, (_("Got '%d' - expected '%d'.") % (self.got, self.expected))) + def __str__ (self): + return "%s %s" % (self.error, (_("Got '%d' - expected '%d'.") % (self.got, self.expected))) class OldSessionException (SessionException): - error = _("Current session format is too old.") + error = _("Current session format is too old.") class NewSessionException (SessionException): - error = _("Current session format is newer than this version supports.") + error = _("Current session format is newer than this version supports.") diff --git a/portato/gui/updater.py b/portato/gui/updater.py index c4c81e7..c4ad2a5 100644 --- a/portato/gui/updater.py +++ b/portato/gui/updater.py @@ -18,109 +18,109 @@ import threading, subprocess, time from ..helper import debug, warning, error class Updater (object): - """ - This class is intended to check what package is currently being installed and remove this one from the queue. + """ + This class is intended to check what package is currently being installed and remove this one from the queue. - @cvar SED_EXP: The sed expression to strip the package name out of the qlop call. - """ - - SED_EXP = r""" + @cvar SED_EXP: The sed expression to strip the package name out of the qlop call. + """ + + SED_EXP = r""" /\*/{ s/ \* // n } d""" - - def __init__ (self, queue, iterators, threadClass = threading.Thread): - """ - Constructor. - Also directly initializes the thread. - - @param queue: an emerge queue instance - @type queue: EmergeQueue - @param iterators: a dictionary of iterators in the current queue - @type iterators: dict(string->Iterator) - """ - - if not issubclass(threadClass, threading.Thread): - raise ValueError, "Only subclasses of threading.Thread are allowed." - - self.queue = queue - self.iterators = iterators - self.threadClass = threadClass - self.stopEvent = threading.Event() - self.removed = set() - - t = threadClass(name = "Queue Updater Thread", target = self.run) - t.setDaemon(True) - t.start() - - def run (self): - """ - Run and run and run ... - Checks the packages until being stopped. - """ - - curr = set() - while not self.stopEvent.isSet(): - - # this = $(qlop -cCq | sed $SED_EXP) - p1 = subprocess.Popen(["qlop", "--current", "--nocolor", "--quiet"], stdout = subprocess.PIPE) - this = subprocess.Popen(["sed", self.SED_EXP], stdout = subprocess.PIPE, stdin = p1.stdout).communicate()[0] - - this = set(this.split()) if this else set() - for removed in curr - this: - self.remove(self.find(removed)) # remove the previous - curr = this - - time.sleep(2.0) - - self.removed = set() - - def stop (self): - """ - Stops the current updater. - """ - self.stopEvent.set() - - def find (self, pv, masked = False): - """ - As qlop only returns 'package-version' we need to assign it to a cpv. - This is done here. - """ - - pkgs = system.find_packages("=%s" % pv, only_cpv = True, masked = masked) - - if len(pkgs) > 1: # ambigous - try to find the one which is also in the iterators - for p in pkgs: - if p in self.iterators: - return p - elif not pkgs: # nothing found =| - if not masked: - warning(_("No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..."), pv) - return self.find(pv, True) - - else: - error(_("Trying to remove package '%s' from queue which does not exist in system."), pv) - return None - else: # only one choice =) - return pkgs[0] - - def remove (self, cpv): - """ - Remove a package from the queue. - """ - - if cpv is None: - debug("Nothing to remove.") - return - - if cpv in self.removed: - return - - self.removed.add(cpv) - - try: - self.queue.remove_with_children(self.iterators[cpv]) - except KeyError: - debug("'%s' should be removed, but is not in queue.", cpv) + + def __init__ (self, queue, iterators, threadClass = threading.Thread): + """ + Constructor. + Also directly initializes the thread. + + @param queue: an emerge queue instance + @type queue: EmergeQueue + @param iterators: a dictionary of iterators in the current queue + @type iterators: dict(string->Iterator) + """ + + if not issubclass(threadClass, threading.Thread): + raise ValueError, "Only subclasses of threading.Thread are allowed." + + self.queue = queue + self.iterators = iterators + self.threadClass = threadClass + self.stopEvent = threading.Event() + self.removed = set() + + t = threadClass(name = "Queue Updater Thread", target = self.run) + t.setDaemon(True) + t.start() + + def run (self): + """ + Run and run and run ... + Checks the packages until being stopped. + """ + + curr = set() + while not self.stopEvent.isSet(): + + # this = $(qlop -cCq | sed $SED_EXP) + p1 = subprocess.Popen(["qlop", "--current", "--nocolor", "--quiet"], stdout = subprocess.PIPE) + this = subprocess.Popen(["sed", self.SED_EXP], stdout = subprocess.PIPE, stdin = p1.stdout).communicate()[0] + + this = set(this.split()) if this else set() + for removed in curr - this: + self.remove(self.find(removed)) # remove the previous + curr = this + + time.sleep(2.0) + + self.removed = set() + + def stop (self): + """ + Stops the current updater. + """ + self.stopEvent.set() + + def find (self, pv, masked = False): + """ + As qlop only returns 'package-version' we need to assign it to a cpv. + This is done here. + """ + + pkgs = system.find_packages("=%s" % pv, only_cpv = True, masked = masked) + + if len(pkgs) > 1: # ambigous - try to find the one which is also in the iterators + for p in pkgs: + if p in self.iterators: + return p + elif not pkgs: # nothing found =| + if not masked: + warning(_("No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..."), pv) + return self.find(pv, True) + + else: + error(_("Trying to remove package '%s' from queue which does not exist in system."), pv) + return None + else: # only one choice =) + return pkgs[0] + + def remove (self, cpv): + """ + Remove a package from the queue. + """ + + if cpv is None: + debug("Nothing to remove.") + return + + if cpv in self.removed: + return + + self.removed.add(cpv) + + try: + self.queue.remove_with_children(self.iterators[cpv]) + except KeyError: + debug("'%s' should be removed, but is not in queue.", cpv) diff --git a/portato/gui/utils.py b/portato/gui/utils.py index cd5c50c..661af6b 100644 --- a/portato/gui/utils.py +++ b/portato/gui/utils.py @@ -32,280 +32,280 @@ from ..constants import APP, LOCALE_DIR from ..config_parser import ConfigParser def get_color (cfg, name): - return gtk.gdk.color_parse("#%s" % cfg.get(name, section = "COLORS")) + return gtk.gdk.color_parse("#%s" % cfg.get(name, section = "COLORS")) class GtkThread (Thread): - def run(self): - # for some reason, I have to install this for each thread ... - gettext.install(APP, LOCALE_DIR, unicode = True) - try: - Thread.run(self) - except SystemExit: - raise # let normal thread handle it - except: - type, val, tb = sys.exc_info() - try: - sys.excepthook(type, val, tb, thread = self.getName()) - except TypeError: - raise type, val, tb # let normal thread handle it - finally: - del type, val, tb + def run(self): + # for some reason, I have to install this for each thread ... + gettext.install(APP, LOCALE_DIR, unicode = True) + try: + Thread.run(self) + except SystemExit: + raise # let normal thread handle it + except: + type, val, tb = sys.exc_info() + try: + sys.excepthook(type, val, tb, thread = self.getName()) + except TypeError: + raise type, val, tb # let normal thread handle it + finally: + del type, val, tb class Config (ConfigParser): - - def __init__ (self, cfgFile): - """Constructor. - - @param cfgFile: path to config file - @type cfgFile: string""" - - ConfigParser.__init__(self, cfgFile) - - # read config - self.parse() - - # local configs - self.local = {} - - def modify_flags_config (self): - """Sets the internal config of the L{flags}-module. - @see: L{flags.set_config()}""" - - flagCfg = { - "usefile": self.get("useFile"), - "usePerVersion" : self.get_boolean("usePerVersion"), - "maskfile" : self.get("maskFile"), - "maskPerVersion" : self.get_boolean("maskPerVersion"), - "testingfile" : self.get("keywordFile"), - "testingPerVersion" : self.get_boolean("keywordPerVersion")} - flags.set_config(flagCfg) - - def modify_debug_config (self): - if self.get_boolean("debug"): - level = logging.DEBUG - else: - level = logging.INFO - - set_log_level(level) - - def modify_system_config (self): - """Sets the system config. - @see: L{backend.set_system()}""" - set_system(self.get("system")) - - def modify_external_configs (self): - """Convenience function setting all external configs.""" - self.modify_debug_config() - self.modify_flags_config() - self.modify_system_config() - - def set_local(self, cpv, name, val): - """Sets some local config. - - @param cpv: the cpv describing the package for which to set this option - @type cpv: string (cpv) - @param name: the option's name - @type name: string - @param val: the value to set - @type val: any""" - - if not cpv in self.local: - self.local[cpv] = {} - - self.local[cpv].update({name:val}) - - def get_local(self, cpv, name): - """Returns something out of the local config. - - @param cpv: the cpv describing the package from which to get this option - @type cpv: string (cpv) - @param name: the option's name - @type name: string - @return: value stored for the cpv and name or None if not found - @rtype: any""" - - if not cpv in self.local: - return None - if not name in self.local[cpv]: - return None - - return self.local[cpv][name] - - def write(self): - """Writes to the config file and modify any external configs.""" - ConfigParser.write(self) - self.modify_external_configs() + + def __init__ (self, cfgFile): + """Constructor. + + @param cfgFile: path to config file + @type cfgFile: string""" + + ConfigParser.__init__(self, cfgFile) + + # read config + self.parse() + + # local configs + self.local = {} + + def modify_flags_config (self): + """Sets the internal config of the L{flags}-module. + @see: L{flags.set_config()}""" + + flagCfg = { + "usefile": self.get("useFile"), + "usePerVersion" : self.get_boolean("usePerVersion"), + "maskfile" : self.get("maskFile"), + "maskPerVersion" : self.get_boolean("maskPerVersion"), + "testingfile" : self.get("keywordFile"), + "testingPerVersion" : self.get_boolean("keywordPerVersion")} + flags.set_config(flagCfg) + + def modify_debug_config (self): + if self.get_boolean("debug"): + level = logging.DEBUG + else: + level = logging.INFO + + set_log_level(level) + + def modify_system_config (self): + """Sets the system config. + @see: L{backend.set_system()}""" + set_system(self.get("system")) + + def modify_external_configs (self): + """Convenience function setting all external configs.""" + self.modify_debug_config() + self.modify_flags_config() + self.modify_system_config() + + def set_local(self, cpv, name, val): + """Sets some local config. + + @param cpv: the cpv describing the package for which to set this option + @type cpv: string (cpv) + @param name: the option's name + @type name: string + @param val: the value to set + @type val: any""" + + if not cpv in self.local: + self.local[cpv] = {} + + self.local[cpv].update({name:val}) + + def get_local(self, cpv, name): + """Returns something out of the local config. + + @param cpv: the cpv describing the package from which to get this option + @type cpv: string (cpv) + @param name: the option's name + @type name: string + @return: value stored for the cpv and name or None if not found + @rtype: any""" + + if not cpv in self.local: + return None + if not name in self.local[cpv]: + return None + + return self.local[cpv][name] + + def write(self): + """Writes to the config file and modify any external configs.""" + ConfigParser.write(self) + self.modify_external_configs() class PkgData (object): - __slots__ = ("cat", "pkg", "inst") + __slots__ = ("cat", "pkg", "inst") - def __init__ (self, cat, pkg, inst): - self.cat = cat - self.pkg = pkg - self.inst = inst + def __init__ (self, cat, pkg, inst): + self.cat = cat + self.pkg = pkg + self.inst = inst - def __iter__ (self): - return iter((self.cat, self.pkg, self.inst)) + def __iter__ (self): + return iter((self.cat, self.pkg, self.inst)) - def __cmp__ (self, other): - return cmp(self.pkg.lower(), other.pkg.lower()) + def __cmp__ (self, other): + return cmp(self.pkg.lower(), other.pkg.lower()) - def __repr__ (self): - return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst} + def __repr__ (self): + return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst} class Database (object): - """An internal database which holds a simple dictionary cat -> [package_list].""" - - ALL = _("ALL") - - def __init__ (self): - """Constructor.""" - self.__initialize() - self._lock = RLock() - - def lock (f): - @wraps(f) - def wrapper (self, *args, **kwargs): - with self._lock: - r = f(self, *args, **kwargs) - return r - - return wrapper - - def __initialize (self): - self._db = defaultdict(list) - self.inst_cats = set([self.ALL]) - self._restrict = None - - def __sort_key (self, x): - return x.pkg.lower() - - @lock - def populate (self, category = None): - """Populates the database. - - @param category: An optional category - so only packages of this category are inserted. - @type category: string - """ - - # get the lists - packages = system.find_packages(category, with_version = False) - installed = system.find_packages(category, system.SET_INSTALLED, with_version = False) - - # cycle through packages - for p in packages: - cat, pkg = p.split("/") - inst = p in installed - t = PkgData(cat, pkg, inst) - self._db[cat].append(t) - self._db[self.ALL].append(t) - - if inst: - self.inst_cats.add(cat) - - for key in self._db: # sort alphabetically - self._db[key].sort(key = self.__sort_key) - - @lock - def get_cat (self, cat = None, byName = True): - """Returns the packages in the category. - - @param cat: category to return the packages from; if None it defaults to "ALL" - @type cat: string - @param byName: selects whether to return the list sorted by name or by installation - @type byName: boolean - @return: an iterator over a list of tuples: (category, name, is_installed) or [] - @rtype: (string, string, boolean)<iterator> - """ - - if not cat: - cat = self.ALL - - def get_pkgs(): - if byName: - for pkg in self._db[cat]: - yield pkg - else: - ninst = [] - for pkg in self._db[cat]: - if pkg.inst: - yield pkg - else: - ninst.append(pkg) - - for pkg in ninst: - yield pkg - - try: - if self.restrict: - return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.pkg))#if pkg[1].find(self.restrict) != -1) - else: - return get_pkgs() - - except KeyError: # cat is in category list - but not in portage - info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat) - - @lock - def get_categories (self, installed = False): - """Returns all categories. - - @param installed: Only return these with at least one installed package. - @type installed: boolean - @returns: the list of categories - @rtype: string<iterator> - """ - - if not self.restrict: - if installed: - cats = self.inst_cats - else: - cats = self._db.iterkeys() - - else: - if installed: - cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst)) - else: - cats = set((pkg.cat for pkg in self.get_cat(self.ALL))) - - if len(cats)>1: - cats.add(self.ALL) - - return (cat for cat in cats) - - @lock - def reload (self, cat = None): - """Reloads the given category. - - @param cat: category - @type cat: string - """ - - if cat: - del self._db[cat] - try: - self.inst_cats.remove(cat) - except KeyError: # not in inst_cats - can be ignored - pass - - self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL]) - self.populate(cat+"/*") - else: - self.__initialize() - self.populate() - - def get_restrict (self): - return self._restrict - - @lock - def set_restrict (self, restrict): - if not restrict: - self._restrict = None - else: - try: - regex = re.compile(restrict, re.I) - except re.error, e: - info(_("Error while compiling search expression: '%s'."), str(e)) - else: # only set self._restrict if no error occurred - self._restrict = regex - - restrict = property(get_restrict, set_restrict) + """An internal database which holds a simple dictionary cat -> [package_list].""" + + ALL = _("ALL") + + def __init__ (self): + """Constructor.""" + self.__initialize() + self._lock = RLock() + + def lock (f): + @wraps(f) + def wrapper (self, *args, **kwargs): + with self._lock: + r = f(self, *args, **kwargs) + return r + + return wrapper + + def __initialize (self): + self._db = defaultdict(list) + self.inst_cats = set([self.ALL]) + self._restrict = None + + def __sort_key (self, x): + return x.pkg.lower() + + @lock + def populate (self, category = None): + """Populates the database. + + @param category: An optional category - so only packages of this category are inserted. + @type category: string + """ + + # get the lists + packages = system.find_packages(category, with_version = False) + installed = system.find_packages(category, system.SET_INSTALLED, with_version = False) + + # cycle through packages + for p in packages: + cat, pkg = p.split("/") + inst = p in installed + t = PkgData(cat, pkg, inst) + self._db[cat].append(t) + self._db[self.ALL].append(t) + + if inst: + self.inst_cats.add(cat) + + for key in self._db: # sort alphabetically + self._db[key].sort(key = self.__sort_key) + + @lock + def get_cat (self, cat = None, byName = True): + """Returns the packages in the category. + + @param cat: category to return the packages from; if None it defaults to "ALL" + @type cat: string + @param byName: selects whether to return the list sorted by name or by installation + @type byName: boolean + @return: an iterator over a list of tuples: (category, name, is_installed) or [] + @rtype: (string, string, boolean)<iterator> + """ + + if not cat: + cat = self.ALL + + def get_pkgs(): + if byName: + for pkg in self._db[cat]: + yield pkg + else: + ninst = [] + for pkg in self._db[cat]: + if pkg.inst: + yield pkg + else: + ninst.append(pkg) + + for pkg in ninst: + yield pkg + + try: + if self.restrict: + return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.pkg))#if pkg[1].find(self.restrict) != -1) + else: + return get_pkgs() + + except KeyError: # cat is in category list - but not in portage + info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat) + + @lock + def get_categories (self, installed = False): + """Returns all categories. + + @param installed: Only return these with at least one installed package. + @type installed: boolean + @returns: the list of categories + @rtype: string<iterator> + """ + + if not self.restrict: + if installed: + cats = self.inst_cats + else: + cats = self._db.iterkeys() + + else: + if installed: + cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst)) + else: + cats = set((pkg.cat for pkg in self.get_cat(self.ALL))) + + if len(cats)>1: + cats.add(self.ALL) + + return (cat for cat in cats) + + @lock + def reload (self, cat = None): + """Reloads the given category. + + @param cat: category + @type cat: string + """ + + if cat: + del self._db[cat] + try: + self.inst_cats.remove(cat) + except KeyError: # not in inst_cats - can be ignored + pass + + self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL]) + self.populate(cat+"/*") + else: + self.__initialize() + self.populate() + + def get_restrict (self): + return self._restrict + + @lock + def set_restrict (self, restrict): + if not restrict: + self._restrict = None + else: + try: + regex = re.compile(restrict, re.I) + except re.error, e: + info(_("Error while compiling search expression: '%s'."), str(e)) + else: # only set self._restrict if no error occurred + self._restrict = regex + + restrict = property(get_restrict, set_restrict) diff --git a/portato/gui/views.py b/portato/gui/views.py index bd98ad8..a2d0468 100644 --- a/portato/gui/views.py +++ b/portato/gui/views.py @@ -20,130 +20,130 @@ import logging from ..helper import warning class LazyView (object): - def __init__ (self): - self.connect("map", self.cb_mapped) + def __init__ (self): + self.connect("map", self.cb_mapped) - self.pkg = None - self.updated = False + self.pkg = None + self.updated = False - def update (self, pkg, force = False): - self.pkg = pkg - self.updated = True - - if force: - self.cb_mapped() + def update (self, pkg, force = False): + self.pkg = pkg + self.updated = True + + if force: + self.cb_mapped() - def cb_mapped (self, *args): - if self.updated and self.pkg: - self.set_text("".join(self._get_content())) - self.updated = False + def cb_mapped (self, *args): + if self.updated and self.pkg: + self.set_text("".join(self._get_content())) + self.updated = False - return False + return False - def set_text (self, text): - raise NotImplementedError + def set_text (self, text): + raise NotImplementedError - def _get_content (self): - raise NotImplementedError + def _get_content (self): + raise NotImplementedError class ListView (gtk.TextView, LazyView): - def __init__ (self, content_fn): - self.content_fn = content_fn + def __init__ (self, content_fn): + self.content_fn = content_fn - gtk.TextView.__init__(self) - LazyView.__init__(self) + gtk.TextView.__init__(self) + LazyView.__init__(self) - self.set_editable(False) - self.set_cursor_visible(False) + self.set_editable(False) + self.set_cursor_visible(False) - def set_text (self, text): - self.get_buffer().set_text(text) + def set_text (self, text): + self.get_buffer().set_text(text) - def _get_content (self): - return self.content_fn(self.pkg) + def _get_content (self): + return self.content_fn(self.pkg) class InstalledOnlyView (ListView): - def _get_content (self): - if self.pkg: - if not self.pkg.is_installed(): - return _("Package is not installed") - else: - return ListView._get_content(self) - else: - return "Huh?" + def _get_content (self): + if self.pkg: + if not self.pkg.is_installed(): + return _("Package is not installed") + else: + return ListView._get_content(self) + else: + return "Huh?" class HighlightView (gtksourceview2.View, LazyView): - def __init__ (self, get_file_fn, languages = []): - self.get_fn = get_file_fn - - man = gtksourceview2.LanguageManager() - - language = None - old_lang = None - for lang in languages: - if old_lang: - warning(_("No %(old)s language file installed. Falling back to %(new)s."), {"old" : old_lang, "new" : lang}) - - language = man.get_language(lang) - if language: - break - else: - old_lang = lang - - if not language and old_lang: - warning(_("No %(old)s language file installed. Disable highlighting."), {"old" : old_lang}) - - buf = gtksourceview2.Buffer() - buf.set_language(language) - - gtksourceview2.View.__init__(self, buf) - LazyView.__init__(self) - - self.set_editable(False) - self.set_cursor_visible(False) - - def set_text (self, text): - self.get_buffer().set_text(text) - - def _get_content (self): - try: - with open(self.get_fn(self.pkg)) as f: - return f.readlines() - except IOError, e: - return _("Error: %s") % e.strerror - + def __init__ (self, get_file_fn, languages = []): + self.get_fn = get_file_fn + + man = gtksourceview2.LanguageManager() + + language = None + old_lang = None + for lang in languages: + if old_lang: + warning(_("No %(old)s language file installed. Falling back to %(new)s."), {"old" : old_lang, "new" : lang}) + + language = man.get_language(lang) + if language: + break + else: + old_lang = lang + + if not language and old_lang: + warning(_("No %(old)s language file installed. Disable highlighting."), {"old" : old_lang}) + + buf = gtksourceview2.Buffer() + buf.set_language(language) + + gtksourceview2.View.__init__(self, buf) + LazyView.__init__(self) + + self.set_editable(False) + self.set_cursor_visible(False) + + def set_text (self, text): + self.get_buffer().set_text(text) + + def _get_content (self): + try: + with open(self.get_fn(self.pkg)) as f: + return f.readlines() + except IOError, e: + return _("Error: %s") % e.strerror + class LogView (logging.Handler): - colors = ( - (logging.DEBUG, "debug", "blue"), - (logging.INFO, "info", "green"), - (logging.WARNING, "warning", "yellow"), - (-1, "error", "red") - ) - - def __init__ (self, view): - logging.Handler.__init__(self, logging.DEBUG) - - self.view = view - self.buf = view.get_buffer() - - # set tags - for lvl, name, color in self.colors: - self.buf.create_tag("log_%s" % name, foreground = color,weight = pango.WEIGHT_BOLD) - - logging.getLogger("portatoLogger").addHandler(self) - - def emit (self, record): - - for lvl, name, color in self.colors: - if lvl == -1 or record.levelno <= lvl: - tag = "log_%s" % name - break - - def _add(): - self.buf.insert_with_tags_by_name(self.buf.get_end_iter(), "* ", tag) - self.buf.insert(self.buf.get_end_iter(), record.getMessage()+"\n") - - gobject.idle_add(_add) # logger might be called from another thread + colors = ( + (logging.DEBUG, "debug", "blue"), + (logging.INFO, "info", "green"), + (logging.WARNING, "warning", "yellow"), + (-1, "error", "red") + ) + + def __init__ (self, view): + logging.Handler.__init__(self, logging.DEBUG) + + self.view = view + self.buf = view.get_buffer() + + # set tags + for lvl, name, color in self.colors: + self.buf.create_tag("log_%s" % name, foreground = color,weight = pango.WEIGHT_BOLD) + + logging.getLogger("portatoLogger").addHandler(self) + + def emit (self, record): + + for lvl, name, color in self.colors: + if lvl == -1 or record.levelno <= lvl: + tag = "log_%s" % name + break + + def _add(): + self.buf.insert_with_tags_by_name(self.buf.get_end_iter(), "* ", tag) + self.buf.insert(self.buf.get_end_iter(), record.getMessage()+"\n") + + gobject.idle_add(_add) # logger might be called from another thread diff --git a/portato/gui/windows/about.py b/portato/gui/windows/about.py index df724f3..21608c0 100644 --- a/portato/gui/windows/about.py +++ b/portato/gui/windows/about.py @@ -18,17 +18,17 @@ from .basic import AbstractDialog from ...constants import VERSION, APP_ICON class AboutWindow (AbstractDialog): - """A window showing the "about"-informations.""" + """A window showing the "about"-informations.""" - def __init__ (self, parent): + def __init__ (self, parent): - AbstractDialog.__init__(self, parent) + AbstractDialog.__init__(self, parent) - img = gtk.Image() - img.set_from_file(APP_ICON) + img = gtk.Image() + img.set_from_file(APP_ICON) - self.window.set_version(VERSION) - self.window.set_logo(img.get_pixbuf()) + self.window.set_version(VERSION) + self.window.set_logo(img.get_pixbuf()) - self.window.show_all() + self.window.show_all() diff --git a/portato/gui/windows/basic.py b/portato/gui/windows/basic.py index 09d0d7e..6d74858 100644 --- a/portato/gui/windows/basic.py +++ b/portato/gui/windows/basic.py @@ -27,101 +27,101 @@ gtk.glade.bindtextdomain (APP, LOCALE_DIR) gtk.glade.textdomain (APP) class WrappedTree (object): - __slots__ = ("klass", "tree", "get_widget") - def __init__ (self, klass, tree): - self.tree = tree - self.klass = klass - - def __getattribute__ (self, name): - if name in WrappedTree.__slots__: - return object.__getattribute__(self, name) - else: - return getattr(self.tree, name) - - def get_widget(self, name): - w = self.tree.get_widget(name) - if w is None: - error("Widget '%s' could not be found in class '%s'.", name, self.klass) - return w + __slots__ = ("klass", "tree", "get_widget") + def __init__ (self, klass, tree): + self.tree = tree + self.klass = klass + + def __getattribute__ (self, name): + if name in WrappedTree.__slots__: + return object.__getattribute__(self, name) + else: + return getattr(self.tree, name) + + def get_widget(self, name): + w = self.tree.get_widget(name) + if w is None: + error("Widget '%s' could not be found in class '%s'.", name, self.klass) + return w class Window (object): - def __init__ (self): - - if not hasattr(self, "__tree__"): - self.__tree__ = self.__class__.__name__ - - if not hasattr(self, "__window__"): - self.__window__ = self.__class__.__name__ - - if not hasattr(self, "__file__"): - self.__file__ = self.__class__.__name__ - - self.tree = self.get_tree(self.__tree__) - self.tree.signal_autoconnect(self) - self.window = self.tree.get_widget(self.__window__) - self.window.set_icon_from_file(APP_ICON) - - @staticmethod - def watch_cursor (func): - """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor. - @attention: this function relies on the gtk.Window-Object being stored as self.window""" - - @wraps(func) - def wrapper (self, *args, **kwargs): - ret = None - def cb_idle(): - try: - ret = func(self, *args, **kwargs) - finally: - self.window.window.set_cursor(None) - return False - - watch = gtk.gdk.Cursor(gtk.gdk.WATCH) - self.window.window.set_cursor(watch) - gobject.idle_add(cb_idle) - return ret - - return wrapper - - def get_tree (self, name): - return WrappedTree(self.__class__.__name__, gtk.glade.XML(os.path.join(TEMPLATE_DIR, self.__file__+".glade"), name)) + def __init__ (self): + + if not hasattr(self, "__tree__"): + self.__tree__ = self.__class__.__name__ + + if not hasattr(self, "__window__"): + self.__window__ = self.__class__.__name__ + + if not hasattr(self, "__file__"): + self.__file__ = self.__class__.__name__ + + self.tree = self.get_tree(self.__tree__) + self.tree.signal_autoconnect(self) + self.window = self.tree.get_widget(self.__window__) + self.window.set_icon_from_file(APP_ICON) + + @staticmethod + def watch_cursor (func): + """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor. + @attention: this function relies on the gtk.Window-Object being stored as self.window""" + + @wraps(func) + def wrapper (self, *args, **kwargs): + ret = None + def cb_idle(): + try: + ret = func(self, *args, **kwargs) + finally: + self.window.window.set_cursor(None) + return False + + watch = gtk.gdk.Cursor(gtk.gdk.WATCH) + self.window.window.set_cursor(watch) + gobject.idle_add(cb_idle) + return ret + + return wrapper + + def get_tree (self, name): + return WrappedTree(self.__class__.__name__, gtk.glade.XML(os.path.join(TEMPLATE_DIR, self.__file__+".glade"), name)) class AbstractDialog (Window): - """A class all our dialogs get derived from. It sets useful default vars and automatically handles the ESC-Button.""" - - def __init__ (self, parent): - """Constructor. - - @param parent: the parent window - @type parent: gtk.Window""" - - Window.__init__(self) - - # set parent - self.window.set_transient_for(parent) - self.parent = parent - - # catch the ESC-key - self.window.connect("key-press-event", self.cb_key_pressed) - - def cb_key_pressed (self, widget, event): - """Closes the window if ESC is pressed.""" - keyname = gtk.gdk.keyval_name(event.keyval) - if keyname == "Escape": - self.close() - return True - else: - return False - - def close (self, *args): - self.window.destroy() + """A class all our dialogs get derived from. It sets useful default vars and automatically handles the ESC-Button.""" + + def __init__ (self, parent): + """Constructor. + + @param parent: the parent window + @type parent: gtk.Window""" + + Window.__init__(self) + + # set parent + self.window.set_transient_for(parent) + self.parent = parent + + # catch the ESC-key + self.window.connect("key-press-event", self.cb_key_pressed) + + def cb_key_pressed (self, widget, event): + """Closes the window if ESC is pressed.""" + keyname = gtk.gdk.keyval_name(event.keyval) + if keyname == "Escape": + self.close() + return True + else: + return False + + def close (self, *args): + self.window.destroy() class Popup (object): - def __init__ (self, name, parent, file = "popups"): - self.tree = gtk.glade.XML(os.path.join(TEMPLATE_DIR, file+".glade"), root = name) - self.tree.signal_autoconnect(parent) - self._popup = self.tree.get_widget(name) + def __init__ (self, name, parent, file = "popups"): + self.tree = gtk.glade.XML(os.path.join(TEMPLATE_DIR, file+".glade"), root = name) + self.tree.signal_autoconnect(parent) + self._popup = self.tree.get_widget(name) - def popup (self, *args): - self._popup.popup(*args) + def popup (self, *args): + self._popup.popup(*args) diff --git a/portato/gui/windows/mailinfo.py b/portato/gui/windows/mailinfo.py index 5d0a24c..22e750a 100644 --- a/portato/gui/windows/mailinfo.py +++ b/portato/gui/windows/mailinfo.py @@ -22,69 +22,69 @@ from ...helper import debug, info from ...constants import VERSION class MailInfoWindow (AbstractDialog): - TO = "bugs@portato.necoro.net" + TO = "bugs@portato.necoro.net" - def __init__ (self, parent, tb): + def __init__ (self, parent, tb): - AbstractDialog.__init__(self, parent) - - self.tb = tb - self.window.show_all() + AbstractDialog.__init__(self, parent) + + self.tb = tb + self.window.show_all() - def set_data (self): - name = self.tree.get_widget("nameEntry").get_text() - addr = self.tree.get_widget("mailEntry").get_text() + def set_data (self): + name = self.tree.get_widget("nameEntry").get_text() + addr = self.tree.get_widget("mailEntry").get_text() - if not addr: - addr = self.TO + if not addr: + addr = self.TO - if name: - fro = "%s <%s>" % (name, addr) - else: - fro = addr + if name: + fro = "%s <%s>" % (name, addr) + else: + fro = addr - commentBuffer = self.tree.get_widget("commentEntry").get_buffer() - text = commentBuffer.get_text(*commentBuffer.get_bounds()) + commentBuffer = self.tree.get_widget("commentEntry").get_buffer() + text = commentBuffer.get_text(*commentBuffer.get_bounds()) - if text: - text += "\n\n===========\n" + if text: + text += "\n\n===========\n" - text += self.tb + text += self.tb - message = """From: %s + message = """From: %s To: %s Subject: %s %s""" % ( fro, self.TO, ("[Bug Report] Bug in Portato %s" % VERSION), text) - self.addr = addr - self.message = message - - def send (self): - try: - debug("Connecting to server") - server = smtplib.SMTP("mail.necoro.eu") - debug("Sending mail") - try: - try: - server.sendmail(self.addr, self.TO, self.message) - except smtplib.SMTPRecipientsRefused, e: - info(_("An error occurred while sending. I think we were greylisted. The error: %s") % e) - info(_("Retrying after waiting 60 seconds.")) - time.sleep(60) - server.sendmail(self.addr, self.TO, self.message) - debug("Sent") - finally: - server.quit() - except socket.error, e: - mail_failure_dialog("%s (Code: %s)" % (e.args[1], e.args[0])) - - def cb_cancel_clicked (self, *args): - - self.close() - return True - - def cb_send_clicked (self, *args): - self.set_data() - GtkThread(target = self.send, name = "Mail Send Thread").start() - self.close() - return True + self.addr = addr + self.message = message + + def send (self): + try: + debug("Connecting to server") + server = smtplib.SMTP("mail.necoro.eu") + debug("Sending mail") + try: + try: + server.sendmail(self.addr, self.TO, self.message) + except smtplib.SMTPRecipientsRefused, e: + info(_("An error occurred while sending. I think we were greylisted. The error: %s") % e) + info(_("Retrying after waiting 60 seconds.")) + time.sleep(60) + server.sendmail(self.addr, self.TO, self.message) + debug("Sent") + finally: + server.quit() + except socket.error, e: + mail_failure_dialog("%s (Code: %s)" % (e.args[1], e.args[0])) + + def cb_cancel_clicked (self, *args): + + self.close() + return True + + def cb_send_clicked (self, *args): + self.set_data() + GtkThread(target = self.send, name = "Mail Send Thread").start() + self.close() + return True diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py index 1b7c0f0..fca4535 100644 --- a/portato/gui/windows/main.py +++ b/portato/gui/windows/main.py @@ -36,8 +36,8 @@ from ..session import SESSION_VERSION, SessionException, OldSessionException, Ne from ..wrapper import GtkTree, GtkConsole from ..views import LogView, HighlightView, InstalledOnlyView from ..dialogs import (blocked_dialog, changed_flags_dialog, io_ex_dialog, - nothing_found_dialog, queue_not_empty_dialog, remove_deps_dialog, - remove_queue_dialog, remove_updates_dialog, unmask_dialog) + nothing_found_dialog, queue_not_empty_dialog, remove_deps_dialog, + remove_queue_dialog, remove_updates_dialog, unmask_dialog) # even more GUI stuff from .basic import Window, Popup @@ -48,1744 +48,1744 @@ from .search import SearchWindow from .update import UpdateWindow class PackageTable: - """A window with data about a specfic package.""" - - def __init__ (self, main): - """Build up window contents. - - @param main: the main window - @type main: MainWindow""" - - self.main = main - self.tree = main.tree - self.window = main.window - self.tree.signal_autoconnect(self) - - # all the package data is in this one VB - self.vb = self.tree.get_widget("packageVB") - - # the notebook - self.notebook = self.tree.get_widget("packageNotebook") - - # chechboxes - self.installedCheck = self.tree.get_widget("installedCheck") - self.maskedCheck = self.tree.get_widget("maskedCheck") - self.testingCheck = self.tree.get_widget("testingCheck") - self.maskedLabel = self.tree.get_widget("maskedLabel") - - # labels - generalVB = self.tree.get_widget("generalVB") - generalVB.modify_bg(gtk.STATE_NORMAL, get_color(self.main.cfg, "packagetable")) - - self.nameLabel = self.tree.get_widget("nameLabel") - self.descLabel = self.tree.get_widget("descLabel") - self.overlayLabel = self.tree.get_widget("overlayLabel") - self.overlayLL = self.tree.get_widget("overlayLabelLabel") - self.licenseLabel = self.tree.get_widget("licenseLabel") - self.linkBox = self.tree.get_widget("linkBox") - self.notInSysLabel = self.tree.get_widget("notInSysLabel") - self.missingLabel = self.tree.get_widget("missingLabel") - self.useFlagsLabel = self.tree.get_widget("useFlagsLabel") - self.useFlagsLL = self.tree.get_widget("useFlagsLabelLabel") - - # buttons - self.emergeBtn = self.tree.get_widget("pkgEmergeBtn") - self.unmergeBtn = self.tree.get_widget("pkgUnmergeBtn") - self.revertBtn = self.tree.get_widget("pkgRevertBtn") - - # useList - self.useList = self.tree.get_widget("useList") - self.build_use_list() - - # depList - self.depList = self.tree.get_widget("dependencyList") - self.build_dep_list() - - # views - self.ebuildView = self.tree.get_widget("ebuildScroll").get_child() - self.changelogView = self.tree.get_widget("changelogScroll").get_child() - self.filesView = self.tree.get_widget("filesScroll").get_child() - - # icons - self.icons = {} - self.icons["use"] = self.window.render_icon(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) - self.icons["installed"] = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) - self.icons["or"] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU) - self.icons["block"] = self.window.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) - - def update (self, pkg, queue = None, doEmerge = True, instantChange = False, type = None): - """Updates the table to show the contents for the package. - - @param pkg: the selected package - @type pkg: Package - @param queue: emerge-queue (if None the emerge-buttons are disabled) - @type queue: EmergeQueue - @param doEmerge: if False, the emerge buttons are disabled - @type doEmerge: boolean - @param instantChange: if True the changed keywords are updated instantly - @type instantChange: boolean - @param type: the type of the queue this package is in; if None there is no queue :) - @type type: string""" - - self.pkg = pkg - self.queue = queue - self.doEmerge = doEmerge - self.instantChange = instantChange - self.type = type - - if not self.queue or not self.doEmerge: - self.emergeBtn.set_sensitive(False) - self.unmergeBtn.set_sensitive(False) - - # current status - self._update_table() - self.vb.show_all() - - def hide (self): - self.vb.hide_all() - - def set_labels (self): - pkg = self.pkg - - # name - self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cpv()) - - # description - desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>") - self.descLabel.set_label(desc) - - # overlay - if pkg.is_in_overlay(): - self.overlayLabel.set_label(pkg.get_overlay_path()) - self.overlayLabel.show() - self.overlayLL.show() - else: - self.overlayLabel.hide() - self.overlayLL.hide() - - # license - self.licenseLabel.set_label(pkg.get_package_settings("LICENSE")) - - # link - for c in self.linkBox.get_children(): - self.linkBox.remove(c) - - text = pkg.get_package_settings("HOMEPAGE") - texts = text.split(" ") - ftexts = [] - - for count, t in enumerate(texts): - if not t.startswith(("http", "ftp")): - if count == 0: - error(_("The first homepage part does not start with 'http' or 'ftp'.")) - ftexts.append(t) - continue - else: - info(_("Blank inside homepage.")) - ftexts[-1] += " %s" % t - else: - ftexts.append(t) - - for t in ftexts: - link = gtk.LinkButton(t) - link.set_alignment(0.0, 0.5) - link.set_border_width(0) - self.linkBox.add(link) - - # useflags - flaglist = list(itt.ifilterfalse(pkg.use_expanded, pkg.get_iuse_flags())) - flaglist.sort() - flags = ", ".join(flaglist) - - if flags: - self.useFlagsLL.show() - self.useFlagsLabel.show() - self.useFlagsLabel.set_label(flags) - else: - self.useFlagsLL.hide() - self.useFlagsLabel.hide() - - def fill_dep_list(self): - - store = self.depList.get_model() - - def add (tree, it): - - def get_icon (dep): - if dep.satisfied: - return self.icons["installed"] - elif dep.dep[0] == "!": - return self.icons["block"] - else: - return None - - # useflags - for use, usetree in tree.flags.iteritems(): - if use[0] == "!": - usestring = _("If '%s' is disabled") % use[1:] - else: - usestring = _("If '%s' is enabled") % use - useit = store.append(it, [self.icons["use"], usestring]) - add(usetree, useit) - - # ORs - ordeps = (dep for dep in tree.deps if isinstance(dep, dependency.OrDependency)) - - for ordep in ordeps: - orit = store.append(it, [self.icons["or"], _("One of the following")]) - - for dep in ordep.dep: - if isinstance(dep, dependency.AllOfDependency): # a list inside or - allit = store.append(orit, [None, _("All of the following")]) - for adep in dep.dep: - store.append(allit, [get_icon(adep), adep.dep]) - else: - store.append(orit, [get_icon(dep), dep.dep]) - - # normal - def sort_key (x): - split = system.split_cpv(x.dep) - - if split is None: # split_cpv returns None if this is only a CP; we assume there are only valid deps - return x.dep - else: - return "/".join(split[0:2]) - - ndeps = [dep for dep in tree.deps if not isinstance(dep, dependency.OrDependency)] - ndeps.sort(key = sort_key) - for dep in ndeps: - store.append(it, [get_icon(dep), dep.dep]) - - try: - deptree = self.pkg.get_dependencies() - except AssertionError: - w = _("Can't display dependencies: This package has an unsupported dependency string.") - error(w) - store.append(None, [None, w]) - else: - add(deptree, None) - - def fill_use_list(self): - - pkg = self.pkg - pkg_flags = pkg.get_iuse_flags() - pkg_flags.sort() - - actual_exp = None - actual_exp_it = None - - euse = pkg.get_actual_use_flags() - instuse = pkg.get_installed_use_flags() - - store = self.useList.get_model() - - for use in pkg_flags: - exp = pkg.use_expanded(use, suggest = actual_exp) - if exp is not None: - if exp != actual_exp: - actual_exp_it = store.append(None, [None, None, exp, "<i>%s</i>" % _("This is an expanded use flag and cannot be selected")]) - actual_exp = exp - else: - actual_exp_it = None - actual_exp = None - - enabled = use in euse - installed = use in instuse - store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.pkg.get_cp())]) - - def build_dep_list (self): - store = gtk.TreeStore(gtk.gdk.Pixbuf, str) - - self.depList.set_model(store) - - col = gtk.TreeViewColumn() - - cell = gtk.CellRendererPixbuf() - col.pack_start(cell, False) - col.add_attribute(cell, "pixbuf", 0) - - cell = gtk.CellRendererText() - col.pack_start(cell, True) - col.add_attribute(cell, "text", 1) - - self.depList.append_column(col) - - def build_use_list (self): - """Builds the useList.""" - store = gtk.TreeStore(bool, bool, str, str) - self.useList.set_model(store) - - # build view - cell = gtk.CellRendererText() - iCell = gtk.CellRendererToggle() - iCell.set_property("activatable", False) - tCell = gtk.CellRendererToggle() - tCell.set_property("activatable", True) - tCell.connect("toggled", self.cb_use_flag_toggled, store) - self.useList.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0)) - self.useList.append_column(gtk.TreeViewColumn(_("Installed"), iCell, active = 1)) - self.useList.append_column(gtk.TreeViewColumn(_("Flag"), cell, text = 2)) - self.useList.append_column(gtk.TreeViewColumn(_("Description"), cell, markup = 3)) - - self.useList.set_search_column(2) - self.useList.set_enable_tree_lines(True) - - def _update_keywords (self, emerge, update = False): - if emerge: - type = "install" if not self.type else self.type - try: - try: - self.queue.append(self.pkg.get_cpv(), type = type, update = update) - except PackageNotFoundException, e: - if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - self.queue.append(self.pkg.get_cpv(), type = type, unmask = True, update = update) - except BlockedException, e: - blocked_dialog(e[0], e[1]) - else: - try: - self.queue.append(self.pkg.get_cpv(), type = "uninstall") - except PackageNotFoundException, e: - error(_("Package could not be found: %s"), e[0]) - #masked_dialog(e[0]) - - def _update_table (self, *args): - - pkg = self.pkg - - # set the views - for v in (self.ebuildView, self.changelogView, self.filesView): - v.update(pkg, force = self.notebook.get_nth_page(self.notebook.get_current_page()) == v.get_parent()) - - # set the labels - self.set_labels() - - # set use list - self.useList.get_model().clear() - self.useList.columns_autosize() - self.fill_use_list() - - # set dep list - self.depList.get_model().clear() - self.useList.columns_autosize() - self.fill_dep_list() - - # - # rebuild the buttons and checkboxes in all the different manners which are possible - # - if (not pkg.is_in_system()) or pkg.is_missing_keyword(): - if not pkg.is_in_system(): - self.missingLabel.hide() - self.notInSysLabel.show() - else: # missing keyword - self.missingLabel.show() - self.notInSysLabel.hide() -# - self.installedCheck.hide() - self.maskedCheck.hide() - self.maskedLabel.hide() - self.testingCheck.hide() - self.emergeBtn.set_sensitive(False) - else: # normal package - self.missingLabel.hide() - self.notInSysLabel.hide() - self.installedCheck.show() - self.maskedCheck.show() - self.maskedLabel.show() - self.testingCheck.show() - if self.doEmerge: - self.emergeBtn.set_sensitive(True) - self.installedCheck.set_active(pkg.is_installed()) - - reason = pkg.get_masking_reason() or " " - if pkg.is_masked(use_changed = False) and not pkg.is_masked(use_changed = True): - self.maskedCheck.set_label("<i>(%s)</i>" % _("Masked")) - self.maskedCheck.get_child().set_use_markup(True) - else: - self.maskedCheck.set_label(_("Masked")) - - if pkg.is_locally_masked(): - self.maskedCheck.set_label("<b>%s</b>" % _("Masked")) - self.maskedCheck.get_child().set_use_markup(True) - self.maskedCheck.set_active(True) - reason = _("Masked by user") - else: - self.maskedCheck.set_active(pkg.is_masked(use_changed = False)) - - if reason: - self.maskedLabel.set_label(reason) - - if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True): - self.testingCheck.set_label("<i>(%s)</i>" % _("Testing")) - self.testingCheck.get_child().set_use_markup(True) - else: - self.testingCheck.set_label(_("Testing")) - - self.testingCheck.set_active(pkg.is_testing(use_keywords = False)) - - if self.doEmerge: - # set emerge-button-label - if not pkg.is_installed(): - self.unmergeBtn.set_sensitive(False) - else: - self.unmergeBtn.set_sensitive(True) - - self.vb.show_all() - return True - - def cb_button_pressed (self, b, event): - """Callback for pressed checkboxes. Just quits the event-loop - no redrawing.""" - if not isinstance(b, gtk.CellRendererToggle): - b.emit_stop_by_name("button-press-event") - return True - - def cb_package_revert_clicked (self, button): - """Callback for pressed revert-button.""" - self.pkg.remove_new_use_flags() - self.pkg.remove_new_masked() - self.pkg.remove_new_testing() - self._update_table() - if self.instantChange: - self._update_keywords(True, update = True) - return True - - def cb_package_emerge_clicked (self, button): - """Callback for pressed emerge-button. Adds the package to the EmergeQueue.""" - self._update_keywords(True) - self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) - return True - - def cb_package_unmerge_clicked (self, button): - """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue.""" - self._update_keywords(False) - self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) - return True - - def cb_testing_toggled (self, button): - """Callback for toggled testing-checkbox.""" - status = button.get_active() - - # end of recursion :) - if self.pkg.is_testing(use_keywords = False) == status: - return False - - # if the package is not testing - don't allow to set it as such - if not self.pkg.is_testing(use_keywords = False): - button.set_active(False) - return True - - # re-set to testing status - if not self.pkg.is_testing(use_keywords = True): - self.pkg.set_testing(False) - button.set_label(_("Testing")) - button.set_active(True) - else: # disable testing - self.pkg.set_testing(True) - button.set_label("<i>(%s)</i>" % _("Testing")) - button.get_child().set_use_markup(True) - button.set_active(True) - - if self.instantChange: - self._update_keywords(True, update = True) - - return True - - def cb_masked_toggled (self, button): - """Callback for toggled masking-checkbox.""" - status = button.get_active() - pkg = self.pkg - - if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked(): - return False - - if pkg.is_locally_masked() and status: - return False - - if not pkg.is_masked(use_changed = True): - pkg.set_masked(True) - if pkg.is_locally_masked(): - button.set_label("<b>%s</b>" % _("Masked")) - button.get_child().set_use_markup(True) - self.maskedLabel.set_label(_("Masked by user")) - else: - button.set_label(_("Masked")) - - button.set_active(True) - else: - locally = pkg.is_locally_masked() - pkg.set_masked(False) - if pkg.is_masked(use_changed=False) and not locally: - button.set_label("<i>(%s)</i>" % _("Masked")) - button.get_child().set_use_markup(True) - button.set_active(True) - else: - button.set_label(_("Masked")) - self.maskedLabel.set_label("") - - if self.instantChange: - self._update_keywords(True, update = True) - - return True - - def cb_use_flag_toggled (self, cell, path, store): - """Callback for a toggled use-flag button.""" - flag = store[path][2] - pkg = self.pkg - - if pkg.use_expanded(flag): # ignore expanded flags - return False - - store[path][0] = not store[path][0] - prefix = "" - if not store[path][0]: - prefix = "-" - - pkg.set_use_flag(prefix+flag) - if self.instantChange: - self._update_keywords(True, update = True) - - return True + """A window with data about a specfic package.""" + + def __init__ (self, main): + """Build up window contents. + + @param main: the main window + @type main: MainWindow""" + + self.main = main + self.tree = main.tree + self.window = main.window + self.tree.signal_autoconnect(self) + + # all the package data is in this one VB + self.vb = self.tree.get_widget("packageVB") + + # the notebook + self.notebook = self.tree.get_widget("packageNotebook") + + # chechboxes + self.installedCheck = self.tree.get_widget("installedCheck") + self.maskedCheck = self.tree.get_widget("maskedCheck") + self.testingCheck = self.tree.get_widget("testingCheck") + self.maskedLabel = self.tree.get_widget("maskedLabel") + + # labels + generalVB = self.tree.get_widget("generalVB") + generalVB.modify_bg(gtk.STATE_NORMAL, get_color(self.main.cfg, "packagetable")) + + self.nameLabel = self.tree.get_widget("nameLabel") + self.descLabel = self.tree.get_widget("descLabel") + self.overlayLabel = self.tree.get_widget("overlayLabel") + self.overlayLL = self.tree.get_widget("overlayLabelLabel") + self.licenseLabel = self.tree.get_widget("licenseLabel") + self.linkBox = self.tree.get_widget("linkBox") + self.notInSysLabel = self.tree.get_widget("notInSysLabel") + self.missingLabel = self.tree.get_widget("missingLabel") + self.useFlagsLabel = self.tree.get_widget("useFlagsLabel") + self.useFlagsLL = self.tree.get_widget("useFlagsLabelLabel") + + # buttons + self.emergeBtn = self.tree.get_widget("pkgEmergeBtn") + self.unmergeBtn = self.tree.get_widget("pkgUnmergeBtn") + self.revertBtn = self.tree.get_widget("pkgRevertBtn") + + # useList + self.useList = self.tree.get_widget("useList") + self.build_use_list() + + # depList + self.depList = self.tree.get_widget("dependencyList") + self.build_dep_list() + + # views + self.ebuildView = self.tree.get_widget("ebuildScroll").get_child() + self.changelogView = self.tree.get_widget("changelogScroll").get_child() + self.filesView = self.tree.get_widget("filesScroll").get_child() + + # icons + self.icons = {} + self.icons["use"] = self.window.render_icon(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) + self.icons["installed"] = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) + self.icons["or"] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU) + self.icons["block"] = self.window.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) + + def update (self, pkg, queue = None, doEmerge = True, instantChange = False, type = None): + """Updates the table to show the contents for the package. + + @param pkg: the selected package + @type pkg: Package + @param queue: emerge-queue (if None the emerge-buttons are disabled) + @type queue: EmergeQueue + @param doEmerge: if False, the emerge buttons are disabled + @type doEmerge: boolean + @param instantChange: if True the changed keywords are updated instantly + @type instantChange: boolean + @param type: the type of the queue this package is in; if None there is no queue :) + @type type: string""" + + self.pkg = pkg + self.queue = queue + self.doEmerge = doEmerge + self.instantChange = instantChange + self.type = type + + if not self.queue or not self.doEmerge: + self.emergeBtn.set_sensitive(False) + self.unmergeBtn.set_sensitive(False) + + # current status + self._update_table() + self.vb.show_all() + + def hide (self): + self.vb.hide_all() + + def set_labels (self): + pkg = self.pkg + + # name + self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cpv()) + + # description + desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>") + self.descLabel.set_label(desc) + + # overlay + if pkg.is_in_overlay(): + self.overlayLabel.set_label(pkg.get_overlay_path()) + self.overlayLabel.show() + self.overlayLL.show() + else: + self.overlayLabel.hide() + self.overlayLL.hide() + + # license + self.licenseLabel.set_label(pkg.get_package_settings("LICENSE")) + + # link + for c in self.linkBox.get_children(): + self.linkBox.remove(c) + + text = pkg.get_package_settings("HOMEPAGE") + texts = text.split(" ") + ftexts = [] + + for count, t in enumerate(texts): + if not t.startswith(("http", "ftp")): + if count == 0: + error(_("The first homepage part does not start with 'http' or 'ftp'.")) + ftexts.append(t) + continue + else: + info(_("Blank inside homepage.")) + ftexts[-1] += " %s" % t + else: + ftexts.append(t) + + for t in ftexts: + link = gtk.LinkButton(t) + link.set_alignment(0.0, 0.5) + link.set_border_width(0) + self.linkBox.add(link) + + # useflags + flaglist = list(itt.ifilterfalse(pkg.use_expanded, pkg.get_iuse_flags())) + flaglist.sort() + flags = ", ".join(flaglist) + + if flags: + self.useFlagsLL.show() + self.useFlagsLabel.show() + self.useFlagsLabel.set_label(flags) + else: + self.useFlagsLL.hide() + self.useFlagsLabel.hide() + + def fill_dep_list(self): + + store = self.depList.get_model() + + def add (tree, it): + + def get_icon (dep): + if dep.satisfied: + return self.icons["installed"] + elif dep.dep[0] == "!": + return self.icons["block"] + else: + return None + + # useflags + for use, usetree in tree.flags.iteritems(): + if use[0] == "!": + usestring = _("If '%s' is disabled") % use[1:] + else: + usestring = _("If '%s' is enabled") % use + useit = store.append(it, [self.icons["use"], usestring]) + add(usetree, useit) + + # ORs + ordeps = (dep for dep in tree.deps if isinstance(dep, dependency.OrDependency)) + + for ordep in ordeps: + orit = store.append(it, [self.icons["or"], _("One of the following")]) + + for dep in ordep.dep: + if isinstance(dep, dependency.AllOfDependency): # a list inside or + allit = store.append(orit, [None, _("All of the following")]) + for adep in dep.dep: + store.append(allit, [get_icon(adep), adep.dep]) + else: + store.append(orit, [get_icon(dep), dep.dep]) + + # normal + def sort_key (x): + split = system.split_cpv(x.dep) + + if split is None: # split_cpv returns None if this is only a CP; we assume there are only valid deps + return x.dep + else: + return "/".join(split[0:2]) + + ndeps = [dep for dep in tree.deps if not isinstance(dep, dependency.OrDependency)] + ndeps.sort(key = sort_key) + for dep in ndeps: + store.append(it, [get_icon(dep), dep.dep]) + + try: + deptree = self.pkg.get_dependencies() + except AssertionError: + w = _("Can't display dependencies: This package has an unsupported dependency string.") + error(w) + store.append(None, [None, w]) + else: + add(deptree, None) + + def fill_use_list(self): + + pkg = self.pkg + pkg_flags = pkg.get_iuse_flags() + pkg_flags.sort() + + actual_exp = None + actual_exp_it = None + + euse = pkg.get_actual_use_flags() + instuse = pkg.get_installed_use_flags() + + store = self.useList.get_model() + + for use in pkg_flags: + exp = pkg.use_expanded(use, suggest = actual_exp) + if exp is not None: + if exp != actual_exp: + actual_exp_it = store.append(None, [None, None, exp, "<i>%s</i>" % _("This is an expanded use flag and cannot be selected")]) + actual_exp = exp + else: + actual_exp_it = None + actual_exp = None + + enabled = use in euse + installed = use in instuse + store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.pkg.get_cp())]) + + def build_dep_list (self): + store = gtk.TreeStore(gtk.gdk.Pixbuf, str) + + self.depList.set_model(store) + + col = gtk.TreeViewColumn() + + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.depList.append_column(col) + + def build_use_list (self): + """Builds the useList.""" + store = gtk.TreeStore(bool, bool, str, str) + self.useList.set_model(store) + + # build view + cell = gtk.CellRendererText() + iCell = gtk.CellRendererToggle() + iCell.set_property("activatable", False) + tCell = gtk.CellRendererToggle() + tCell.set_property("activatable", True) + tCell.connect("toggled", self.cb_use_flag_toggled, store) + self.useList.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0)) + self.useList.append_column(gtk.TreeViewColumn(_("Installed"), iCell, active = 1)) + self.useList.append_column(gtk.TreeViewColumn(_("Flag"), cell, text = 2)) + self.useList.append_column(gtk.TreeViewColumn(_("Description"), cell, markup = 3)) + + self.useList.set_search_column(2) + self.useList.set_enable_tree_lines(True) + + def _update_keywords (self, emerge, update = False): + if emerge: + type = "install" if not self.type else self.type + try: + try: + self.queue.append(self.pkg.get_cpv(), type = type, update = update) + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + self.queue.append(self.pkg.get_cpv(), type = type, unmask = True, update = update) + except BlockedException, e: + blocked_dialog(e[0], e[1]) + else: + try: + self.queue.append(self.pkg.get_cpv(), type = "uninstall") + except PackageNotFoundException, e: + error(_("Package could not be found: %s"), e[0]) + #masked_dialog(e[0]) + + def _update_table (self, *args): + + pkg = self.pkg + + # set the views + for v in (self.ebuildView, self.changelogView, self.filesView): + v.update(pkg, force = self.notebook.get_nth_page(self.notebook.get_current_page()) == v.get_parent()) + + # set the labels + self.set_labels() + + # set use list + self.useList.get_model().clear() + self.useList.columns_autosize() + self.fill_use_list() + + # set dep list + self.depList.get_model().clear() + self.useList.columns_autosize() + self.fill_dep_list() + + # + # rebuild the buttons and checkboxes in all the different manners which are possible + # + if (not pkg.is_in_system()) or pkg.is_missing_keyword(): + if not pkg.is_in_system(): + self.missingLabel.hide() + self.notInSysLabel.show() + else: # missing keyword + self.missingLabel.show() + self.notInSysLabel.hide() +# + self.installedCheck.hide() + self.maskedCheck.hide() + self.maskedLabel.hide() + self.testingCheck.hide() + self.emergeBtn.set_sensitive(False) + else: # normal package + self.missingLabel.hide() + self.notInSysLabel.hide() + self.installedCheck.show() + self.maskedCheck.show() + self.maskedLabel.show() + self.testingCheck.show() + if self.doEmerge: + self.emergeBtn.set_sensitive(True) + self.installedCheck.set_active(pkg.is_installed()) + + reason = pkg.get_masking_reason() or " " + if pkg.is_masked(use_changed = False) and not pkg.is_masked(use_changed = True): + self.maskedCheck.set_label("<i>(%s)</i>" % _("Masked")) + self.maskedCheck.get_child().set_use_markup(True) + else: + self.maskedCheck.set_label(_("Masked")) + + if pkg.is_locally_masked(): + self.maskedCheck.set_label("<b>%s</b>" % _("Masked")) + self.maskedCheck.get_child().set_use_markup(True) + self.maskedCheck.set_active(True) + reason = _("Masked by user") + else: + self.maskedCheck.set_active(pkg.is_masked(use_changed = False)) + + if reason: + self.maskedLabel.set_label(reason) + + if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True): + self.testingCheck.set_label("<i>(%s)</i>" % _("Testing")) + self.testingCheck.get_child().set_use_markup(True) + else: + self.testingCheck.set_label(_("Testing")) + + self.testingCheck.set_active(pkg.is_testing(use_keywords = False)) + + if self.doEmerge: + # set emerge-button-label + if not pkg.is_installed(): + self.unmergeBtn.set_sensitive(False) + else: + self.unmergeBtn.set_sensitive(True) + + self.vb.show_all() + return True + + def cb_button_pressed (self, b, event): + """Callback for pressed checkboxes. Just quits the event-loop - no redrawing.""" + if not isinstance(b, gtk.CellRendererToggle): + b.emit_stop_by_name("button-press-event") + return True + + def cb_package_revert_clicked (self, button): + """Callback for pressed revert-button.""" + self.pkg.remove_new_use_flags() + self.pkg.remove_new_masked() + self.pkg.remove_new_testing() + self._update_table() + if self.instantChange: + self._update_keywords(True, update = True) + return True + + def cb_package_emerge_clicked (self, button): + """Callback for pressed emerge-button. Adds the package to the EmergeQueue.""" + self._update_keywords(True) + self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) + return True + + def cb_package_unmerge_clicked (self, button): + """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue.""" + self._update_keywords(False) + self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE) + return True + + def cb_testing_toggled (self, button): + """Callback for toggled testing-checkbox.""" + status = button.get_active() + + # end of recursion :) + if self.pkg.is_testing(use_keywords = False) == status: + return False + + # if the package is not testing - don't allow to set it as such + if not self.pkg.is_testing(use_keywords = False): + button.set_active(False) + return True + + # re-set to testing status + if not self.pkg.is_testing(use_keywords = True): + self.pkg.set_testing(False) + button.set_label(_("Testing")) + button.set_active(True) + else: # disable testing + self.pkg.set_testing(True) + button.set_label("<i>(%s)</i>" % _("Testing")) + button.get_child().set_use_markup(True) + button.set_active(True) + + if self.instantChange: + self._update_keywords(True, update = True) + + return True + + def cb_masked_toggled (self, button): + """Callback for toggled masking-checkbox.""" + status = button.get_active() + pkg = self.pkg + + if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked(): + return False + + if pkg.is_locally_masked() and status: + return False + + if not pkg.is_masked(use_changed = True): + pkg.set_masked(True) + if pkg.is_locally_masked(): + button.set_label("<b>%s</b>" % _("Masked")) + button.get_child().set_use_markup(True) + self.maskedLabel.set_label(_("Masked by user")) + else: + button.set_label(_("Masked")) + + button.set_active(True) + else: + locally = pkg.is_locally_masked() + pkg.set_masked(False) + if pkg.is_masked(use_changed=False) and not locally: + button.set_label("<i>(%s)</i>" % _("Masked")) + button.get_child().set_use_markup(True) + button.set_active(True) + else: + button.set_label(_("Masked")) + self.maskedLabel.set_label("") + + if self.instantChange: + self._update_keywords(True, update = True) + + return True + + def cb_use_flag_toggled (self, cell, path, store): + """Callback for a toggled use-flag button.""" + flag = store[path][2] + pkg = self.pkg + + if pkg.use_expanded(flag): # ignore expanded flags + return False + + store[path][0] = not store[path][0] + prefix = "" + if not store[path][0]: + prefix = "-" + + pkg.set_use_flag(prefix+flag) + if self.instantChange: + self._update_keywords(True, update = True) + + return True class MainWindow (Window): - """ - Application main window. - """ - - # NOTEBOOK PAGE CONSTANTS - ( - QUEUE_PAGE, - CONSOLE_PAGE, - LOG_PAGE - ) = range(3) - - def __init__ (self, splash = None): - """ - Build up window. - - @param splash: the splash screen =) - @type splash: SplashScreen - """ - - if splash is None: - splash = lambda x: True - - # the title - self.main_title = "Portato (%s)" % VERSION - - # main window stuff - Window.__init__(self) - self.window.set_title(self.main_title) - self.window.set_geometry_hints (self.window, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width()) - - # booleans - self.doUpdate = False - self.showAll = True # show only installed or all packages? - self.__searchChanged = False - - # installed pixbuf - self.instPixbuf = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) - - # get the logging window as soon as possible - self.logView = LogView(self.tree.get_widget("logView")) - - # config - splash(_("Loading Config")) - try: - self.cfg = Config(CONFIG_LOCATION) - except IOError, e: - io_ex_dialog(e) - raise - - self.cfg.modify_external_configs() - self.set_uri_hook(self.cfg.get("browserCmd", section = "GUI")) - gtk.about_dialog_set_url_hook(lambda *args: True) # dummy - if not set link is not set as link; if link is clicked the normal uuri_hook is called too - thus do not call browser here - - # package db - splash(_("Creating Database")) - self.db = Database() - self.db.populate() - - # set plugins and plugin-menu - splash(_("Loading Plugins")) - - plugin.load_plugins() - menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()] - if menus: - self.tree.get_widget("pluginMenuItem").set_no_show_all(False) - pluginMenu = self.tree.get_widget("pluginMenu") - - for m in itt.chain(*menus): - item = gtk.MenuItem(m.label) - item.connect("activate", m.call) - pluginMenu.append(item) - - splash(_("Building frontend")) - # set paned position - self.vpaned = self.tree.get_widget("vpaned") - self.vpaned.set_position(int(self.window.get_size()[1]/2)) - self.hpaned = self.tree.get_widget("hpaned") - self.hpaned.set_position(int(self.window.get_size()[0]/1.5)) - - # lists - self.selCatName = "" - self.selCP = "" - self.selCPV = "" - self.sortPkgListByName = True - self.catList = self.tree.get_widget("catList") - self.pkgList = self.tree.get_widget("pkgList") - self.versionList = self.tree.get_widget("versionList") - self.build_cat_list() - self.build_pkg_list() - self.build_version_list() - - # search entry - self.searchEntry = self.tree.get_widget("searchEntry") - - # queue list - self.queueOneshot = self.tree.get_widget("oneshotCB") - self.queueOneshotHandler = self.queueOneshot.connect("toggled", self.cb_oneshot_clicked) - self.queueList = self.tree.get_widget("queueList") - self.build_queue_list() - - # the terminal - self.console = GtkConsole() - self.termHB = self.tree.get_widget("termHB") - self.build_terminal() - - # notebooks - self.sysNotebook = self.tree.get_widget("systemNotebook") - self.pkgNotebook = self.tree.get_widget("packageNotebook") - self.set_notebook_tabpos(map(PreferenceWindow.tabpos.get, map(int, (self.cfg.get("packageTabPos", "GUI"), self.cfg.get("systemTabPos", "GUI"))))) - - # the different scrolls - ebuildScroll = self.tree.get_widget("ebuildScroll") - ebuildScroll.add(HighlightView(lambda p: p.get_ebuild_path(), ["gentoo", "sh"])) - - changelogScroll = self.tree.get_widget("changelogScroll") - changelogScroll.add(HighlightView(lambda p: os.path.join(p.get_package_path(), "ChangeLog"), ["changelog"])) - - def show_files (p): - try: - for f in p.get_files(): - yield " %s\n" % f - except IOError, e: - yield _("Error: %s") % e.strerror - - filesScroll = self.tree.get_widget("filesScroll") - filesScroll.add(InstalledOnlyView(show_files)) - - # table - self.packageTable = PackageTable(self) - - # popups - self.consolePopup = Popup("consolePopup", self, self.__file__) - self.trayPopup = Popup("systrayPopup", self) - - # pause menu items - self.emergePaused = False - self.pauseItems = {} - self.pauseItems["tray"] = self.trayPopup.tree.get_widget("pauseItemTray") - self.pauseItems["popup"] = self.consolePopup.tree.get_widget("pauseItemPopup") - self.pauseItems["menu"] = self.tree.get_widget("pauseItemMenu") - - for k,v in self.pauseItems.iteritems(): - self.pauseItems[k] = (v, v.connect_after("activate", self.cb_pause_emerge(k))) - - # systray - if self.cfg.get_boolean("showSystray", "GUI"): - self.tray = gtk.status_icon_new_from_file(APP_ICON) - self.tray.connect("activate", self.cb_systray_activated) - self.tray.connect("popup-menu", lambda icon, btn, time: self.trayPopup.popup(None, None, None, btn, time)) - else: - self.tray = None - - # set emerge queue - self.queueTree = GtkTree(self.queueList.get_model()) - self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread) - - # session - splash(_("Restoring Session")) - try: - try: - self.load_session() - except OldSessionException, e: - self.load_session(e) - except SessionException, e: - warning(str(e)) - - splash(_("Finishing startup")) - - self.window.show_all() - - def show_package (self, pkg = None, cpv = None, cp = None, version = None, **kwargs): - p = None - - if pkg: - p = pkg - elif cpv: - p = system.find_packages("="+cpv, masked = True)[0] - elif cp: - if version: - p = system.find_packages("=%s-%s" % (cp, version), masked = True)[0] - - else: - best = system.find_best_match(cp) - if best: - p = best - else: - p = system.find_packages(cp)[0] - - self.packageTable.update(p, **kwargs) - - def build_terminal (self): - """ - Builds the terminal. - """ - - self.console.set_scrollback_lines(int(self.cfg.get("scrollbacklines", "GUI"))) - self.console.set_scroll_on_output(True) - self.console.set_font_from_string(self.cfg.get("consolefont", "GUI")) - self.console.connect("button-press-event", self.cb_right_click) - self.termHB.pack_start(self.console, True, True) - - # add scrollbar - termScroll = gtk.VScrollbar(self.console.get_adjustment()) - self.termHB.pack_start(termScroll, False) - - def build_queue_list (self): - """ - Builds the queue list. - """ - - store = gtk.TreeStore(str,str,bool) - - self.queueList.set_model(store) - - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Queue"), cell, markup = 0) - self.queueList.append_column(col) - - col = gtk.TreeViewColumn(_("Options"), cell, markup = 1) - self.queueList.append_column(col) - - self.queueList.get_selection().connect("changed", self.cb_queue_list_selection) - - def build_cat_list (self): - """ - Builds the category list. - """ - - store = gtk.TreeStore(str) - - self.fill_cat_store(store) - - self.catList.set_model(store) - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Categories"), cell, text = 0) - self.catList.append_column(col) - - self.catList.get_selection().connect("changed", self.cb_cat_list_selection) - - def fill_cat_store (self, store = None): - """ - Fills the category store with data. - - @param store: the store to fill - @type store: gtk.ListStore - """ - - if store is None: - store = self.catList.get_model() - - store.clear() - - cats = self.db.get_categories(installed = not self.showAll) - - if not self.cfg.get_boolean("collapseCats", "GUI"): - for p in cats: - store.append(None, [p]) - else: - splitCats = defaultdict(list) - for c in cats: - try: - pre, post = c.split("-", 1) - except ValueError: # no "-" in cat name -- do not split - debug("Category '%s' can't be split up. Should be no harm.", c) - splitCats["not-split"].append(c) - else: - splitCats[pre].append(post) - - for sc in splitCats: - if sc == "not-split": - it = None # append not splitted stuff to root - else: - it = store.append(None, [sc]) - for cat in splitCats[sc]: - store.append(it, [cat]) - - # sort them alphabetically - store.set_sort_column_id(0, gtk.SORT_ASCENDING) - - def build_pkg_list (self, name = None): - """ - Builds the package list. - - @param name: name of the selected catetegory - @type name: string - """ - - store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) - self.fill_pkg_store(store, name) - - # build view - self.pkgList.set_model(store) - - col = gtk.TreeViewColumn(_("Packages")) - col.set_clickable(True) - col.connect("clicked", self.cb_pkg_list_header_clicked) - - # adding the pixbuf - cell = gtk.CellRendererPixbuf() - col.pack_start(cell, False) - col.add_attribute(cell, "pixbuf", 0) - - # adding the package name - cell = gtk.CellRendererText() - col.pack_start(cell, True) - col.add_attribute(cell, "text", 1) - - self.pkgList.append_column(col) - - self.pkgList.get_selection().connect("changed", self.cb_pkg_list_selection) - - def fill_pkg_store (self, store = None, name = None): - """ - Fills a given ListStore with the packages in a category. - - @param store: the store to fill - @type store: gtk.ListStore - @param name: the name of the category - @type name: string - """ - - if store is None: - store = self.pkgList.get_model() - store.clear() - - if name: - for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName): - if is_inst: - icon = self.instPixbuf - elif not self.showAll: - continue # ignore not installed packages - else: - icon = None - store.append([icon, pkg, cat]) - - def build_version_list (self): - store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) - - # build view - self.versionList.set_model(store) - - col = gtk.TreeViewColumn(_("Versions")) - col.set_property("expand", True) - - self.slotcol = gtk.TreeViewColumn(_("Slot")) - self.slotcol.set_property("expand", True) - - # adding the pixbuf - cell = gtk.CellRendererPixbuf() - col.pack_start(cell, False) - col.add_attribute(cell, "pixbuf", 0) - - # adding the package name - cell = gtk.CellRendererText() - col.pack_start(cell, True) - col.add_attribute(cell, "text", 1) - - # adding the slot - cell = gtk.CellRendererText() - self.slotcol.pack_start(cell, True) - self.slotcol.add_attribute(cell, "text", 2) - - self.versionList.append_column(col) - self.versionList.append_column(self.slotcol) - - self.versionList.get_selection().connect("changed", self.cb_vers_list_selection) - - def fill_version_list (self, cp, version = None): - - store = self.versionList.get_model() - store.clear() - - # this is here for performance reasons - # to not query the package with info, we do not need - if self.cfg.get_boolean("showSlots", "GUI"): - def get_slot(pkg): - return pkg.get_package_settings("SLOT") - - self.slotcol.set_visible(True) - - else: - def get_slot(*args): - return "" - - self.slotcol.set_visible(False) - - packages = system.sort_package_list(system.find_packages(cp, masked=True)) - - # append versions - for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages): - if inst: - icon = self.instPixbuf - else: - icon = None - - store.append([icon, vers, slot]) - - pos = ((0,)) # default - - # activate the first one - try: - best_version = "" - if version: - best_version = version - else: - best_version = system.find_best_match(packages[0].get_cp()).get_version() - for i, p in enumerate(packages): - if p.get_version() == best_version: - pos = (i,) - break - except AttributeError: # no package found - pass - - self.versionList.get_selection().select_path(pos) - self.versionList.scroll_to_cell(pos) - - def refresh_stores (self): - """ - Refreshes the category and package stores. - """ - self.fill_cat_store() - - if self.selCatName: - self.fill_pkg_store(name = self.selCatName) - else: # no selCatName -> so no category selected --> ignore - debug("No category selected --> should be no harm.") - - def load_session(self, sessionEx = None): - """ - Loads the session data. - """ - try: - self.session = Session("gtk_session.cfg") - except (OSError, IOError), e: - io_ex_dialog(e) - return - - oldVersion = SESSION_VERSION - allowedVersions = (0,1) - - if sessionEx and isinstance(sessionEx, SessionException): - if sessionEx.got in allowedVersions: - info(_("Translating session from version %d to %d.") % (sessionEx.got, sessionEx.expected)) - oldVersion = sessionEx.got - else: - warning(_("Cannot translate session from version %d to %d.") % (sessionEx.got, sessionEx.expected)) - raise sessionEx - - # - # the callbacks for the different session variables - # - - # QUEUE - def load_queue (merge, unmerge, oneshot): - def _load(q, **kwargs): - if q: - for i in q.split(","): - self.queue.append(i, **kwargs) - - _load(merge) - _load(unmerge, unmerge = True) - _load(oneshot, oneshot = True) - - def save_queue (): - if self.__save_queue: - return (",".join(self.queue.mergequeue), ",".join(self.queue.unmergequeue), ",".join(self.queue.oneshotmerge)) - else: - return ("", "", "") - - # PANED - def load_paned (*pos): - pos = map(int, pos) - [x.set_position(p) for x,p in zip((self.vpaned, self.hpaned), pos)] - - def save_paned (): - return [x.get_position() for x in (self.vpaned, self.hpaned)] - - # SELECTION - def load_selection (list, col): - def _load (name): - pos = "0" # default - - if name: - for cname, path in ((x[col], x.path) for x in list.get_model()): - if cname == name: - pos = path - break - - if self.cfg.get_boolean("collapseCats", "GUI") and \ - pos == "0" and isinstance(list.get_model(), gtk.TreeStore): # try the new split up - - try: - pre, post = name.split("-", 1) - except ValueError: # nothing to split - pass - else: - for row in list.get_model(): - if row[col] == pre: # found first part - pos = row.path - list.expand_row(pos, False) - for cname, path in ((x[col], x.path) for x in row.iterchildren()): - if cname == post: # found second - pos = ":".join(map(str,path)) - break - break - - debug("Selecting path '%s'.", pos) - list.get_selection().select_path(pos) - list.scroll_to_cell(pos) - - return _load - - def save_pkg_selection (): - store, iter = self.pkgList.get_selection().get_selected() - if iter: - return store.get_value(iter, 1) - else: - return "" - - def save_cat_selection (): - # try to find the correct category using the pkgList selection - # so we do not select ALL =) - # if no package has been selected - return selCatName - store, iter = self.pkgList.get_selection().get_selected() - if iter: - return store.get_value(iter, 2) - else: - return self.selCatName - - # PLUGIN - def load_plugin (p): - def _load(val): - if val: - p.status = int(val)*2 - - return _load - - def save_plugin (p): - def _save (): - if p.status == p.STAT_HARD_DISABLED: - return "" - - return int(p.status >= p.STAT_ENABLED) - - return _save - - # SESSION VERSION - def load_session_version (version): - if oldVersion != SESSION_VERSION: # we are trying to convert - return - - version = int(version) - - if version < SESSION_VERSION: - raise OldSessionException(version, SESSION_VERSION) - elif version > SESSION_VERSION: - raise NewSessionException(version, SESSION_VERSION) - - def _add (value): - if len(value) == 4: - self.session.add_handler(value[:3], default = value[3]) - else: - self.session.add_handler(value) - - # set the simple ones :) - map(_add,[ - ([("gtksessionversion", "session")], load_session_version, lambda: SESSION_VERSION), - ([("width", "window"), ("height", "window")], lambda w,h: self.window.resize(int(w), int(h)), self.window.get_size), - ([("vpanedpos", "window"), ("hpanedpos", "window")], load_paned, save_paned), - ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection, ["app-portage"]), - ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection, ["portato"]) - #([("merge", "queue"), ("unmerge", "queue"), ("oneshot", "queue")], load_queue, save_queue), - ]) - - # set the plugins - queue = plugin.get_plugin_queue() - if queue: - for p in queue.get_plugins(): - self.session.add_handler(([(p.name.replace(" ","_"), "plugins")], load_plugin(p), save_plugin(p))) - - # now we have the handlers -> load - self.session.load() - - def jump_to (self, cp, version = None): - """ - Is called when we want to jump to a specific package. - - @param cp: the CP to jump to - @type cp: string - @param version: if not None jump to a specific version - @type version: string - """ - - cat, pkg = cp.split("/") - - for list, idx, what, expr in ((self.catList, 0, "categories", cat), (self.pkgList, 1, "packages", pkg)): - pathes = [row.path for row in list.get_model() if row[idx] == expr] - - if len(pathes) == 1: - list.get_selection().select_path(pathes[0]) - list.scroll_to_cell(pathes[0]) - else: - debug("Unexpected number of %s returned after search: %d", what, len(pathes)) - break - - self.show_package(cp = cp, version = version, queue = self.queue) - - def set_uri_hook (self, browser): - """ - Sets the browser command which is called when a URL is going to be opened. - - @param browser: the browser command - @type browser: string - """ - - browser = browser.split() - gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd(browser+[btn.get_uri()])) - - def set_notebook_tabpos (self, tabposlist): - """ - Sets the positions of the tabs of the notebooks. - - @param tabposlist: the list of positions: first comes the one for package tabs; sndly for sys tabs - @type tabposlist: int[] - """ - self.pkgNotebook.set_tab_pos(tabposlist[0]) - self.sysNotebook.set_tab_pos(tabposlist[1]) - - def title_update (self, title): - """ - Updates the titles of the window and the systray. - Mainly used with emerge term titles. - - @param title: the title - @type title: string - """ - - def window_title_update (title): - """ - Updates the title of the main window. - """ - if title is None or not self.cfg.get_boolean("updateTitle", "GUI"): - self.window.set_title(self.main_title) - else: - title = title.strip() - if title[0] == '*': - self.window.set_title(self.main_title) - else: - space_idx = title.rfind(" ") - if space_idx != -1: - title = title[:space_idx] - - self.window.set_title(("Portato >>> %s" % title)) - - def __update(title): - if self.tray: - self.tray.set_tooltip(title) - - window_title_update(title) - if title is None or not self.cfg.get_boolean("updateConsole", "GUI"): - title = _("Console") - else: - title = ("%s (%s)") % (_("Console"), title) - - tlength = int(self.cfg.get("titlelength", "GUI")) - if (len(title) > tlength): title = "%s..." % title[:tlength-3] - self.sysNotebook.set_tab_label_text(self.termHB, title) - - return False - - # as this might get called from other threads use gobject.idle_add - gobject.idle_add(__update, title) - - def cb_cat_list_selection (self, selection): - """ - Callback for a category-list selection. - Updates the package list with the packages in the category. - """ - # get the selected category - store, it = selection.get_selected() - if it: - if not self.cfg.get_boolean("collapseCats", "GUI"): - self.selCatName = store.get_value(it, 0) - else: - parent = store.iter_parent(it) - if parent is None: - if store.iter_has_child(it): # this is a split up selector -> do nothing - return True - else: - self.selCatName = store.get_value(it, 0) # this is a non-split up top - else: - self.selCatName = ("%s-%s" % (store.get_value(parent, 0), store.get_value(it, 0))) - - self.fill_pkg_store(name = self.selCatName) - return True - - def cb_pkg_list_selection (self, selection): - """ - Callback for a package-list selection. - Updates the version list. - """ - store, it = selection.get_selected() - if it: - self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1)) - self.fill_version_list(self.selCP) - return True - - def cb_pkg_list_header_clicked(self, col): - self.sortPkgListByName = not self.sortPkgListByName - self.fill_pkg_store(name = self.selCatName) - return True - - def cb_vers_list_selection (self, selection): - """ - Callback for a package-list selection. - Updates the version list. - """ - store, it = selection.get_selected() - if it: - self.selCPV = "%s-%s" % (self.selCP, store.get_value(it, 1)) - self.show_package(cpv = self.selCPV, queue = self.queue) - - return True - - def cb_queue_list_selection (self, selection): - - def set_val (val): - self.queueOneshot.handler_block(self.queueOneshotHandler) - self.queueOneshot.set_active(val) - self.queueOneshot.handler_unblock(self.queueOneshotHandler) - - store, it = selection.get_selected() - if it: - parent = self.queueTree.parent_iter(it) - if self.queueTree.is_in_emerge(it) and parent and not self.queueTree.iter_has_parent(parent): - package = store.get_value(it, 0) - self.queueOneshot.set_sensitive(True) - set_val(package in self.queue.oneshotmerge) - return True - - self.queueOneshot.set_sensitive(False) - set_val(False) - return True - - def cb_queue_row_activated (self, view, path, *args): - """Callback for an activated row in the emergeQueue. Opens a package window.""" - store = self.queueTree - if len(path) > 1: - iterator = store.get_original().get_iter(path) - if store.iter_has_parent(iterator): - package = store.get_value(iterator, store.get_cpv_column()) - - if store.is_in_emerge(iterator): - type = "install" - elif store.is_in_unmerge(iterator): - type = "uninstall" - elif store.is_in_update(iterator): - type = "update" - - self.show_package(cpv = package, queue = self.queue, instantChange = True, doEmerge = False, type = type) - return True - - def cb_queue_tooltip_queried (self, view, x, y, is_keyboard, tooltip): - store = self.queueList.get_model() - path = self.queueList.get_path_at_pos(x,y) - - if path is None: - return False - - it = store.get_iter(path[0]) - - if store.iter_parent(it) is None: - return False # do not show tooltips for the root entries - - pkg = system.new_package(store.get_value(it, 0)) - - enabled = [] - disabled = [] - expanded = set() - - pkg_flags = pkg.get_iuse_flags() - pkg_flags.sort() - if not pkg_flags: # no flags - stop here - return None - - actual = set(pkg.get_actual_use_flags()) - - if pkg.is_installed(): - installed = set(pkg.get_iuse_flags()).intersection(pkg.get_installed_use_flags()) - else: - inst = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED) - if inst: - installed = set(inst[0].get_iuse_flags()).intersection(inst[0].get_installed_use_flags()) - else: - installed = set() - - diff = actual.symmetric_difference(installed) - - for use in pkg_flags: - exp = pkg.use_expanded(use) - if exp: - expanded.add(exp) - - else: - useStr = use - if installed and use in diff: - useStr += " %" - if use in actual: - enabled.append(useStr) - else: - disabled.append(useStr) - - string = "" - - if enabled: - string = "<b>+%s</b>" % ("\n+".join(enabled),) - if len(disabled) > 0: - string = string + "\n" - - if disabled: - string = string+"<i>- %s</i>" % ("\n- ".join(disabled),) - - if expanded: - string = string+"\n\n"+"\n".join(expanded) - - tooltip.set_markup(string) - return string != "" - - def cb_execute_clicked (self, action): - """Execute the current queue.""" - - if len(flags.newUseFlags) > 0: - if not self.session.get_boolean("useflags", "dialogs"): - self.session.set("useflags", changed_flags_dialog(_("use flags"))[1], "dialogs") - try: - flags.write_use_flags() - except IOError, e: - io_ex_dialog(e) - return True - - if len(flags.new_masked)>0 or len(flags.new_unmasked)>0 or len(flags.newTesting)>0: - debug("new masked: %s",flags.new_masked) - debug("new unmasked: %s", flags.new_unmasked) - debug("new testing: %s", flags.newTesting) - if not self.session.get_boolean("keywords", "dialogs"): - self.session.set("keywords", changed_flags_dialog(_("masking keywords"))[1], "dialogs") - try: - flags.write_masked() - flags.write_testing() - except IOError, e: - io_ex_dialog(e) - return True - else: - system.reload_settings() - - model, iter = self.queueList.get_selection().get_selected() - - if iter is None: - if model.iter_n_children(None) == 1: # only one queue there - take this as being selected - iter = model.get_iter_root() - else: - return False - - self.sysNotebook.set_current_page(self.CONSOLE_PAGE) - - # test which type of queue we have here - if self.queueTree.is_in_emerge(iter): - self.queue.emerge(force = True) - elif self.queueTree.is_in_unmerge(iter): - self.queue.unmerge(force = True) - else: - self.queue.update_world(sets = self.updateSets, force=True, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) - - return True - - def cb_update_clicked (self, action): - def __update(): - - def cb_idle_append (updating): - try: - try: - for pkg, old_pkg in updating: - self.queue.append(pkg.get_cpv(), type = "update", unmask = False) - except PackageNotFoundException, e: - if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - for pkg, old_pkg in updating: - self.queue.append(pkg.get_cpv(), type = "update", unmask = True) - - except BlockedException, e: - blocked_dialog(e[0], e[1]) - self.queue.remove_children(self.queueTree.get_update_it()) - - return False - - watch = gtk.gdk.Cursor(gtk.gdk.WATCH) - self.window.window.set_cursor(watch) - try: - if system.has_set_support(): - confsets = [x.strip() for x in self.cfg.get("updatesets").split(",")] - self.updateSets = [s for s in confsets if s in system.get_sets()] - updating = system.update_world(sets = self.updateSets, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) - else: - updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) - self.updateSets = ("world",) - - debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating)) - gobject.idle_add(cb_idle_append, updating) - finally: - self.window.window.set_cursor(None) - - GtkThread(name="Update-Thread", target=__update).start() - - return True - - def cb_remove_clicked (self, button): - """Removes a selected item in the (un)emerge-queue if possible.""" - model, iter = self.queueList.get_selection().get_selected() - - if iter: - parent = model.iter_parent(iter) - - if self.queueTree.is_in_update(iter) and parent: - if remove_updates_dialog() == gtk.RESPONSE_YES: - self.queue.remove_with_children(self.queueTree.get_update_it()) - - elif not parent: # top-level - if model.iter_n_children(iter) > 0: # and has children which can be removed :) - if remove_queue_dialog() == gtk.RESPONSE_YES : - self.queue.remove_with_children(iter) - else: - self.queue.remove(iter) - - elif model.iter_parent(parent): # this is in the 3rd level => dependency - remove_deps_dialog() - else: - self.queue.remove_with_children(iter) - - if model.iter_n_children(parent) == 0: # no more children left - remove queue too - self.queue.remove(parent) - - return True - return False - - def cb_sync_clicked (self, action): - self.sysNotebook.set_current_page(self.CONSOLE_PAGE) - cmd = self.cfg.get("syncCommand") - - if cmd != "emerge --sync": - cmd = cmd.split() - self.queue.sync(cmd) - else: - self.queue.sync() - - def cb_save_flags_clicked (self, action): - try: - flags.write_use_flags() - flags.write_testing() - flags.write_masked() - except IOError, e: - io_ex_dialog(e) - - @Window.watch_cursor - def cb_reload_clicked (self, action): - """Reloads the portage settings and the database.""" - system.reload_settings() - self.db.reload() - - @Window.watch_cursor - def cb_search_clicked (self, entry): - """Do a search.""" - text = entry.get_text() - if text != "": - if "/" not in text: - text = "/.*"+text # only look for package names - - packages = system.find_packages(text, with_version = False) - - if packages == []: - nothing_found_dialog() - else: - if len(packages) == 1: - self.jump_to(packages[0]) - else: - SearchWindow(self.window, packages, self.jump_to) - - return True - - def cb_search_changed (self, *args): - """ - Called when the user enters something in the search field. - Updates the packages according to the search expression. - """ - if not self.__searchChanged and self.cfg.get_boolean("searchOnType", section="GUI"): - self.__searchChanged = True - - def __update(): - self.__searchChanged = False - txt = self.searchEntry.get_text() - - if txt or self.db.restrict: - self.db.restrict = txt - - self.refresh_stores() - self.catList.get_selection().select_path("0") # XXX make this smarter - - return False # not again ;) - - gobject.timeout_add(100, __update) - - def cb_delete_search_clicked (self, *args): - self.searchEntry.set_text("") - return True - - def cb_preferences_clicked (self, *args): - """ - User wants to open preferences. - """ - PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos, self.fill_cat_store) - return True - - def cb_about_clicked (self, *args): - """ - User wants to open about dialog. - """ - AboutWindow(self.window) - return True - - def cb_plugins_clicked (self, *args): - """ - User wants to open plugin dialog. - """ - queue = plugin.get_plugin_queue() - if queue is None: - plugins = [] - else: - plugins = list(queue.get_plugins()) - - PluginWindow(self.window, plugins, self.queue) - return True - - def cb_show_updates_clicked (self, *args): - """ - Show the list of updateble packages. - """ - - def __update(): - def cb_idle_show(packages): - """ - Callback opening the menu when the calculation is finished. - - @returns: False to signal that it is finished - """ - UpdateWindow(self.window, packages, self.queue, self.jump_to) - return False - - watch = gtk.gdk.Cursor(gtk.gdk.WATCH) - self.window.window.set_cursor(watch) - - packages = [] - try: - packages.extend(system.get_updated_packages()) - finally: - self.window.window.set_cursor(None) - - gobject.idle_add(cb_idle_show, packages) - - GtkThread(name="Show Updates Thread", target = __update).start() - return True - - def cb_show_installed_toggled (self, *args): - """ - Toggle the "show only installed" option. - """ - self.showAll = not self.showAll - self.refresh_stores() - - def cb_right_click (self, object, event): - """ - Called when the user right clicks somewhere. - Used to display a menu. - - This method should handle ALL such menus. - - @param object: the object/widget where the click is done - @type object: gtk.Widget - @param event: the event triggered - @type event: gtk.gdk.Event - """ - - if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # 3 == right click - x = int(event.x) - y = int(event.y) - time = event.time - - if object == self.console: - self.consolePopup.popup(None, None, None, event.button, time) - else: - return False - else: - return False - - def cb_oneshot_clicked (self, *args): - """ - Mark a package as oneshot. - """ - sel = self.queueList.get_selection() - store, it = sel.get_selected() - if it: - if self.queueTree.is_in_emerge(it) and self.queueTree.iter_has_parent(it): - package = store.get_value(it, 0) - set = (package not in self.queue.oneshotmerge) - - self.queue.append(package, update = True, oneshot = set, forceUpdate = True) - - def cb_pause_emerge (self, curr): - """ - This method returns a callback for a "pause emerge" toggle button. - It is needed as there are different toggle buttons of this type and if one is clicked, - the others should be marked too. - - @param curr: The button to return the callback for. - @type curr: gtk.ToggleButton - """ - def pause (cb): - """ - The actual callback. - - Mark all other buttons too. - - @param cb: The button which got toggled. - @type cb: gtk.ToggleButton - """ - - # pause or continue - self.emergePaused = cb.get_active() - if not self.emergePaused: - self.queue.continue_emerge() - #self.tray.set_from_file(APP_ICON) - else: - self.queue.stop_emerge() - #self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png")) - - # block the handlers of the other buttons - # so that calling "set_active" does not call this callback recursivly - for v in self.pauseItems.itervalues(): - v[0].handler_block(v[1]) - - # mark the others - for k, v in self.pauseItems.iteritems(): - if k != curr: - v[0].set_active(self.emergePaused) - - # unblock - for v in self.pauseItems.itervalues(): - v[0].handler_unblock(v[1]) - - return False - return pause - - def cb_kill_clicked (self, *args): - """ - Kill emerge. - """ - self.queue.kill_emerge() - if self.emergePaused: # unmark the "pause emerge" buttons - self.pauseItems["menu"][0].set_active(False) # calling one button is enough (see: cb_pause_emerge) - - def cb_copy_clicked (self, *args): - """ - Copy marked text in the terminal to clipboard. - """ - self.console.copy_clipboard() - - def cb_delete (self, *args): - """ - Called when the user wants to quit the application. - - Asks the user for confirmation if there is something in the queue. - Also saves session data. - """ - - self.__save_queue = False - - if not self.queue.is_empty(): - ret = queue_not_empty_dialog() - if ret == gtk.RESPONSE_CANCEL: - return True - else: # there is sth in queue AND the user still wants to close -> kill emerge - self.__save_queue = (ret == gtk.RESPONSE_YES) - self.queue.kill_emerge() - - # write session - self.session.save() - - return False - - def cb_minimized (self, window, event): - """ - User wants to minimize the window. - If it is possible to minimize to tray, it is done. - """ - - if self.tray and self.cfg.get_boolean("hideOnMinimize", "GUI"): - if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: - if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: - self.window.hide() - return True - - return False - - def cb_systray_activated (self, *args): - """ - Systray was activated. Show or hide the window. - """ - if self.window.iconify_initially: - self.window.deiconify() - self.window.show() - self.window.window.show() - else: - self.window.iconify() - - def cb_close (self, *args): - """ - "Close" menu entry called. - Emulate normal quitting. - """ - if not self.cb_delete(): # do the checks - self.window.destroy() - - def cb_destroy (self, *args): - """ - Calls main_quit(). - """ - gtk.main_quit() - - def main (self): - """ - Main. - """ - gobject.threads_init() - # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add(). - # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp - plugin.hook("main")(gtk.main)() + """ + Application main window. + """ + + # NOTEBOOK PAGE CONSTANTS + ( + QUEUE_PAGE, + CONSOLE_PAGE, + LOG_PAGE + ) = range(3) + + def __init__ (self, splash = None): + """ + Build up window. + + @param splash: the splash screen =) + @type splash: SplashScreen + """ + + if splash is None: + splash = lambda x: True + + # the title + self.main_title = "Portato (%s)" % VERSION + + # main window stuff + Window.__init__(self) + self.window.set_title(self.main_title) + self.window.set_geometry_hints (self.window, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width()) + + # booleans + self.doUpdate = False + self.showAll = True # show only installed or all packages? + self.__searchChanged = False + + # installed pixbuf + self.instPixbuf = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) + + # get the logging window as soon as possible + self.logView = LogView(self.tree.get_widget("logView")) + + # config + splash(_("Loading Config")) + try: + self.cfg = Config(CONFIG_LOCATION) + except IOError, e: + io_ex_dialog(e) + raise + + self.cfg.modify_external_configs() + self.set_uri_hook(self.cfg.get("browserCmd", section = "GUI")) + gtk.about_dialog_set_url_hook(lambda *args: True) # dummy - if not set link is not set as link; if link is clicked the normal uuri_hook is called too - thus do not call browser here + + # package db + splash(_("Creating Database")) + self.db = Database() + self.db.populate() + + # set plugins and plugin-menu + splash(_("Loading Plugins")) + + plugin.load_plugins() + menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()] + if menus: + self.tree.get_widget("pluginMenuItem").set_no_show_all(False) + pluginMenu = self.tree.get_widget("pluginMenu") + + for m in itt.chain(*menus): + item = gtk.MenuItem(m.label) + item.connect("activate", m.call) + pluginMenu.append(item) + + splash(_("Building frontend")) + # set paned position + self.vpaned = self.tree.get_widget("vpaned") + self.vpaned.set_position(int(self.window.get_size()[1]/2)) + self.hpaned = self.tree.get_widget("hpaned") + self.hpaned.set_position(int(self.window.get_size()[0]/1.5)) + + # lists + self.selCatName = "" + self.selCP = "" + self.selCPV = "" + self.sortPkgListByName = True + self.catList = self.tree.get_widget("catList") + self.pkgList = self.tree.get_widget("pkgList") + self.versionList = self.tree.get_widget("versionList") + self.build_cat_list() + self.build_pkg_list() + self.build_version_list() + + # search entry + self.searchEntry = self.tree.get_widget("searchEntry") + + # queue list + self.queueOneshot = self.tree.get_widget("oneshotCB") + self.queueOneshotHandler = self.queueOneshot.connect("toggled", self.cb_oneshot_clicked) + self.queueList = self.tree.get_widget("queueList") + self.build_queue_list() + + # the terminal + self.console = GtkConsole() + self.termHB = self.tree.get_widget("termHB") + self.build_terminal() + + # notebooks + self.sysNotebook = self.tree.get_widget("systemNotebook") + self.pkgNotebook = self.tree.get_widget("packageNotebook") + self.set_notebook_tabpos(map(PreferenceWindow.tabpos.get, map(int, (self.cfg.get("packageTabPos", "GUI"), self.cfg.get("systemTabPos", "GUI"))))) + + # the different scrolls + ebuildScroll = self.tree.get_widget("ebuildScroll") + ebuildScroll.add(HighlightView(lambda p: p.get_ebuild_path(), ["gentoo", "sh"])) + + changelogScroll = self.tree.get_widget("changelogScroll") + changelogScroll.add(HighlightView(lambda p: os.path.join(p.get_package_path(), "ChangeLog"), ["changelog"])) + + def show_files (p): + try: + for f in p.get_files(): + yield " %s\n" % f + except IOError, e: + yield _("Error: %s") % e.strerror + + filesScroll = self.tree.get_widget("filesScroll") + filesScroll.add(InstalledOnlyView(show_files)) + + # table + self.packageTable = PackageTable(self) + + # popups + self.consolePopup = Popup("consolePopup", self, self.__file__) + self.trayPopup = Popup("systrayPopup", self) + + # pause menu items + self.emergePaused = False + self.pauseItems = {} + self.pauseItems["tray"] = self.trayPopup.tree.get_widget("pauseItemTray") + self.pauseItems["popup"] = self.consolePopup.tree.get_widget("pauseItemPopup") + self.pauseItems["menu"] = self.tree.get_widget("pauseItemMenu") + + for k,v in self.pauseItems.iteritems(): + self.pauseItems[k] = (v, v.connect_after("activate", self.cb_pause_emerge(k))) + + # systray + if self.cfg.get_boolean("showSystray", "GUI"): + self.tray = gtk.status_icon_new_from_file(APP_ICON) + self.tray.connect("activate", self.cb_systray_activated) + self.tray.connect("popup-menu", lambda icon, btn, time: self.trayPopup.popup(None, None, None, btn, time)) + else: + self.tray = None + + # set emerge queue + self.queueTree = GtkTree(self.queueList.get_model()) + self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread) + + # session + splash(_("Restoring Session")) + try: + try: + self.load_session() + except OldSessionException, e: + self.load_session(e) + except SessionException, e: + warning(str(e)) + + splash(_("Finishing startup")) + + self.window.show_all() + + def show_package (self, pkg = None, cpv = None, cp = None, version = None, **kwargs): + p = None + + if pkg: + p = pkg + elif cpv: + p = system.find_packages("="+cpv, masked = True)[0] + elif cp: + if version: + p = system.find_packages("=%s-%s" % (cp, version), masked = True)[0] + + else: + best = system.find_best_match(cp) + if best: + p = best + else: + p = system.find_packages(cp)[0] + + self.packageTable.update(p, **kwargs) + + def build_terminal (self): + """ + Builds the terminal. + """ + + self.console.set_scrollback_lines(int(self.cfg.get("scrollbacklines", "GUI"))) + self.console.set_scroll_on_output(True) + self.console.set_font_from_string(self.cfg.get("consolefont", "GUI")) + self.console.connect("button-press-event", self.cb_right_click) + self.termHB.pack_start(self.console, True, True) + + # add scrollbar + termScroll = gtk.VScrollbar(self.console.get_adjustment()) + self.termHB.pack_start(termScroll, False) + + def build_queue_list (self): + """ + Builds the queue list. + """ + + store = gtk.TreeStore(str,str,bool) + + self.queueList.set_model(store) + + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn(_("Queue"), cell, markup = 0) + self.queueList.append_column(col) + + col = gtk.TreeViewColumn(_("Options"), cell, markup = 1) + self.queueList.append_column(col) + + self.queueList.get_selection().connect("changed", self.cb_queue_list_selection) + + def build_cat_list (self): + """ + Builds the category list. + """ + + store = gtk.TreeStore(str) + + self.fill_cat_store(store) + + self.catList.set_model(store) + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn(_("Categories"), cell, text = 0) + self.catList.append_column(col) + + self.catList.get_selection().connect("changed", self.cb_cat_list_selection) + + def fill_cat_store (self, store = None): + """ + Fills the category store with data. + + @param store: the store to fill + @type store: gtk.ListStore + """ + + if store is None: + store = self.catList.get_model() + + store.clear() + + cats = self.db.get_categories(installed = not self.showAll) + + if not self.cfg.get_boolean("collapseCats", "GUI"): + for p in cats: + store.append(None, [p]) + else: + splitCats = defaultdict(list) + for c in cats: + try: + pre, post = c.split("-", 1) + except ValueError: # no "-" in cat name -- do not split + debug("Category '%s' can't be split up. Should be no harm.", c) + splitCats["not-split"].append(c) + else: + splitCats[pre].append(post) + + for sc in splitCats: + if sc == "not-split": + it = None # append not splitted stuff to root + else: + it = store.append(None, [sc]) + for cat in splitCats[sc]: + store.append(it, [cat]) + + # sort them alphabetically + store.set_sort_column_id(0, gtk.SORT_ASCENDING) + + def build_pkg_list (self, name = None): + """ + Builds the package list. + + @param name: name of the selected catetegory + @type name: string + """ + + store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) + self.fill_pkg_store(store, name) + + # build view + self.pkgList.set_model(store) + + col = gtk.TreeViewColumn(_("Packages")) + col.set_clickable(True) + col.connect("clicked", self.cb_pkg_list_header_clicked) + + # adding the pixbuf + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + # adding the package name + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.pkgList.append_column(col) + + self.pkgList.get_selection().connect("changed", self.cb_pkg_list_selection) + + def fill_pkg_store (self, store = None, name = None): + """ + Fills a given ListStore with the packages in a category. + + @param store: the store to fill + @type store: gtk.ListStore + @param name: the name of the category + @type name: string + """ + + if store is None: + store = self.pkgList.get_model() + store.clear() + + if name: + for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName): + if is_inst: + icon = self.instPixbuf + elif not self.showAll: + continue # ignore not installed packages + else: + icon = None + store.append([icon, pkg, cat]) + + def build_version_list (self): + store = gtk.ListStore(gtk.gdk.Pixbuf, str, str) + + # build view + self.versionList.set_model(store) + + col = gtk.TreeViewColumn(_("Versions")) + col.set_property("expand", True) + + self.slotcol = gtk.TreeViewColumn(_("Slot")) + self.slotcol.set_property("expand", True) + + # adding the pixbuf + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + # adding the package name + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + # adding the slot + cell = gtk.CellRendererText() + self.slotcol.pack_start(cell, True) + self.slotcol.add_attribute(cell, "text", 2) + + self.versionList.append_column(col) + self.versionList.append_column(self.slotcol) + + self.versionList.get_selection().connect("changed", self.cb_vers_list_selection) + + def fill_version_list (self, cp, version = None): + + store = self.versionList.get_model() + store.clear() + + # this is here for performance reasons + # to not query the package with info, we do not need + if self.cfg.get_boolean("showSlots", "GUI"): + def get_slot(pkg): + return pkg.get_package_settings("SLOT") + + self.slotcol.set_visible(True) + + else: + def get_slot(*args): + return "" + + self.slotcol.set_visible(False) + + packages = system.sort_package_list(system.find_packages(cp, masked=True)) + + # append versions + for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages): + if inst: + icon = self.instPixbuf + else: + icon = None + + store.append([icon, vers, slot]) + + pos = ((0,)) # default + + # activate the first one + try: + best_version = "" + if version: + best_version = version + else: + best_version = system.find_best_match(packages[0].get_cp()).get_version() + for i, p in enumerate(packages): + if p.get_version() == best_version: + pos = (i,) + break + except AttributeError: # no package found + pass + + self.versionList.get_selection().select_path(pos) + self.versionList.scroll_to_cell(pos) + + def refresh_stores (self): + """ + Refreshes the category and package stores. + """ + self.fill_cat_store() + + if self.selCatName: + self.fill_pkg_store(name = self.selCatName) + else: # no selCatName -> so no category selected --> ignore + debug("No category selected --> should be no harm.") + + def load_session(self, sessionEx = None): + """ + Loads the session data. + """ + try: + self.session = Session("gtk_session.cfg") + except (OSError, IOError), e: + io_ex_dialog(e) + return + + oldVersion = SESSION_VERSION + allowedVersions = (0,1) + + if sessionEx and isinstance(sessionEx, SessionException): + if sessionEx.got in allowedVersions: + info(_("Translating session from version %d to %d.") % (sessionEx.got, sessionEx.expected)) + oldVersion = sessionEx.got + else: + warning(_("Cannot translate session from version %d to %d.") % (sessionEx.got, sessionEx.expected)) + raise sessionEx + + # + # the callbacks for the different session variables + # + + # QUEUE + def load_queue (merge, unmerge, oneshot): + def _load(q, **kwargs): + if q: + for i in q.split(","): + self.queue.append(i, **kwargs) + + _load(merge) + _load(unmerge, unmerge = True) + _load(oneshot, oneshot = True) + + def save_queue (): + if self.__save_queue: + return (",".join(self.queue.mergequeue), ",".join(self.queue.unmergequeue), ",".join(self.queue.oneshotmerge)) + else: + return ("", "", "") + + # PANED + def load_paned (*pos): + pos = map(int, pos) + [x.set_position(p) for x,p in zip((self.vpaned, self.hpaned), pos)] + + def save_paned (): + return [x.get_position() for x in (self.vpaned, self.hpaned)] + + # SELECTION + def load_selection (list, col): + def _load (name): + pos = "0" # default + + if name: + for cname, path in ((x[col], x.path) for x in list.get_model()): + if cname == name: + pos = path + break + + if self.cfg.get_boolean("collapseCats", "GUI") and \ + pos == "0" and isinstance(list.get_model(), gtk.TreeStore): # try the new split up + + try: + pre, post = name.split("-", 1) + except ValueError: # nothing to split + pass + else: + for row in list.get_model(): + if row[col] == pre: # found first part + pos = row.path + list.expand_row(pos, False) + for cname, path in ((x[col], x.path) for x in row.iterchildren()): + if cname == post: # found second + pos = ":".join(map(str,path)) + break + break + + debug("Selecting path '%s'.", pos) + list.get_selection().select_path(pos) + list.scroll_to_cell(pos) + + return _load + + def save_pkg_selection (): + store, iter = self.pkgList.get_selection().get_selected() + if iter: + return store.get_value(iter, 1) + else: + return "" + + def save_cat_selection (): + # try to find the correct category using the pkgList selection + # so we do not select ALL =) + # if no package has been selected - return selCatName + store, iter = self.pkgList.get_selection().get_selected() + if iter: + return store.get_value(iter, 2) + else: + return self.selCatName + + # PLUGIN + def load_plugin (p): + def _load(val): + if val: + p.status = int(val)*2 + + return _load + + def save_plugin (p): + def _save (): + if p.status == p.STAT_HARD_DISABLED: + return "" + + return int(p.status >= p.STAT_ENABLED) + + return _save + + # SESSION VERSION + def load_session_version (version): + if oldVersion != SESSION_VERSION: # we are trying to convert + return + + version = int(version) + + if version < SESSION_VERSION: + raise OldSessionException(version, SESSION_VERSION) + elif version > SESSION_VERSION: + raise NewSessionException(version, SESSION_VERSION) + + def _add (value): + if len(value) == 4: + self.session.add_handler(value[:3], default = value[3]) + else: + self.session.add_handler(value) + + # set the simple ones :) + map(_add,[ + ([("gtksessionversion", "session")], load_session_version, lambda: SESSION_VERSION), + ([("width", "window"), ("height", "window")], lambda w,h: self.window.resize(int(w), int(h)), self.window.get_size), + ([("vpanedpos", "window"), ("hpanedpos", "window")], load_paned, save_paned), + ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection, ["app-portage"]), + ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection, ["portato"]) + #([("merge", "queue"), ("unmerge", "queue"), ("oneshot", "queue")], load_queue, save_queue), + ]) + + # set the plugins + queue = plugin.get_plugin_queue() + if queue: + for p in queue.get_plugins(): + self.session.add_handler(([(p.name.replace(" ","_"), "plugins")], load_plugin(p), save_plugin(p))) + + # now we have the handlers -> load + self.session.load() + + def jump_to (self, cp, version = None): + """ + Is called when we want to jump to a specific package. + + @param cp: the CP to jump to + @type cp: string + @param version: if not None jump to a specific version + @type version: string + """ + + cat, pkg = cp.split("/") + + for list, idx, what, expr in ((self.catList, 0, "categories", cat), (self.pkgList, 1, "packages", pkg)): + pathes = [row.path for row in list.get_model() if row[idx] == expr] + + if len(pathes) == 1: + list.get_selection().select_path(pathes[0]) + list.scroll_to_cell(pathes[0]) + else: + debug("Unexpected number of %s returned after search: %d", what, len(pathes)) + break + + self.show_package(cp = cp, version = version, queue = self.queue) + + def set_uri_hook (self, browser): + """ + Sets the browser command which is called when a URL is going to be opened. + + @param browser: the browser command + @type browser: string + """ + + browser = browser.split() + gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd(browser+[btn.get_uri()])) + + def set_notebook_tabpos (self, tabposlist): + """ + Sets the positions of the tabs of the notebooks. + + @param tabposlist: the list of positions: first comes the one for package tabs; sndly for sys tabs + @type tabposlist: int[] + """ + self.pkgNotebook.set_tab_pos(tabposlist[0]) + self.sysNotebook.set_tab_pos(tabposlist[1]) + + def title_update (self, title): + """ + Updates the titles of the window and the systray. + Mainly used with emerge term titles. + + @param title: the title + @type title: string + """ + + def window_title_update (title): + """ + Updates the title of the main window. + """ + if title is None or not self.cfg.get_boolean("updateTitle", "GUI"): + self.window.set_title(self.main_title) + else: + title = title.strip() + if title[0] == '*': + self.window.set_title(self.main_title) + else: + space_idx = title.rfind(" ") + if space_idx != -1: + title = title[:space_idx] + + self.window.set_title(("Portato >>> %s" % title)) + + def __update(title): + if self.tray: + self.tray.set_tooltip(title) + + window_title_update(title) + if title is None or not self.cfg.get_boolean("updateConsole", "GUI"): + title = _("Console") + else: + title = ("%s (%s)") % (_("Console"), title) + + tlength = int(self.cfg.get("titlelength", "GUI")) + if (len(title) > tlength): title = "%s..." % title[:tlength-3] + self.sysNotebook.set_tab_label_text(self.termHB, title) + + return False + + # as this might get called from other threads use gobject.idle_add + gobject.idle_add(__update, title) + + def cb_cat_list_selection (self, selection): + """ + Callback for a category-list selection. + Updates the package list with the packages in the category. + """ + # get the selected category + store, it = selection.get_selected() + if it: + if not self.cfg.get_boolean("collapseCats", "GUI"): + self.selCatName = store.get_value(it, 0) + else: + parent = store.iter_parent(it) + if parent is None: + if store.iter_has_child(it): # this is a split up selector -> do nothing + return True + else: + self.selCatName = store.get_value(it, 0) # this is a non-split up top + else: + self.selCatName = ("%s-%s" % (store.get_value(parent, 0), store.get_value(it, 0))) + + self.fill_pkg_store(name = self.selCatName) + return True + + def cb_pkg_list_selection (self, selection): + """ + Callback for a package-list selection. + Updates the version list. + """ + store, it = selection.get_selected() + if it: + self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1)) + self.fill_version_list(self.selCP) + return True + + def cb_pkg_list_header_clicked(self, col): + self.sortPkgListByName = not self.sortPkgListByName + self.fill_pkg_store(name = self.selCatName) + return True + + def cb_vers_list_selection (self, selection): + """ + Callback for a package-list selection. + Updates the version list. + """ + store, it = selection.get_selected() + if it: + self.selCPV = "%s-%s" % (self.selCP, store.get_value(it, 1)) + self.show_package(cpv = self.selCPV, queue = self.queue) + + return True + + def cb_queue_list_selection (self, selection): + + def set_val (val): + self.queueOneshot.handler_block(self.queueOneshotHandler) + self.queueOneshot.set_active(val) + self.queueOneshot.handler_unblock(self.queueOneshotHandler) + + store, it = selection.get_selected() + if it: + parent = self.queueTree.parent_iter(it) + if self.queueTree.is_in_emerge(it) and parent and not self.queueTree.iter_has_parent(parent): + package = store.get_value(it, 0) + self.queueOneshot.set_sensitive(True) + set_val(package in self.queue.oneshotmerge) + return True + + self.queueOneshot.set_sensitive(False) + set_val(False) + return True + + def cb_queue_row_activated (self, view, path, *args): + """Callback for an activated row in the emergeQueue. Opens a package window.""" + store = self.queueTree + if len(path) > 1: + iterator = store.get_original().get_iter(path) + if store.iter_has_parent(iterator): + package = store.get_value(iterator, store.get_cpv_column()) + + if store.is_in_emerge(iterator): + type = "install" + elif store.is_in_unmerge(iterator): + type = "uninstall" + elif store.is_in_update(iterator): + type = "update" + + self.show_package(cpv = package, queue = self.queue, instantChange = True, doEmerge = False, type = type) + return True + + def cb_queue_tooltip_queried (self, view, x, y, is_keyboard, tooltip): + store = self.queueList.get_model() + path = self.queueList.get_path_at_pos(x,y) + + if path is None: + return False + + it = store.get_iter(path[0]) + + if store.iter_parent(it) is None: + return False # do not show tooltips for the root entries + + pkg = system.new_package(store.get_value(it, 0)) + + enabled = [] + disabled = [] + expanded = set() + + pkg_flags = pkg.get_iuse_flags() + pkg_flags.sort() + if not pkg_flags: # no flags - stop here + return None + + actual = set(pkg.get_actual_use_flags()) + + if pkg.is_installed(): + installed = set(pkg.get_iuse_flags()).intersection(pkg.get_installed_use_flags()) + else: + inst = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED) + if inst: + installed = set(inst[0].get_iuse_flags()).intersection(inst[0].get_installed_use_flags()) + else: + installed = set() + + diff = actual.symmetric_difference(installed) + + for use in pkg_flags: + exp = pkg.use_expanded(use) + if exp: + expanded.add(exp) + + else: + useStr = use + if installed and use in diff: + useStr += " %" + if use in actual: + enabled.append(useStr) + else: + disabled.append(useStr) + + string = "" + + if enabled: + string = "<b>+%s</b>" % ("\n+".join(enabled),) + if len(disabled) > 0: + string = string + "\n" + + if disabled: + string = string+"<i>- %s</i>" % ("\n- ".join(disabled),) + + if expanded: + string = string+"\n\n"+"\n".join(expanded) + + tooltip.set_markup(string) + return string != "" + + def cb_execute_clicked (self, action): + """Execute the current queue.""" + + if len(flags.newUseFlags) > 0: + if not self.session.get_boolean("useflags", "dialogs"): + self.session.set("useflags", changed_flags_dialog(_("use flags"))[1], "dialogs") + try: + flags.write_use_flags() + except IOError, e: + io_ex_dialog(e) + return True + + if len(flags.new_masked)>0 or len(flags.new_unmasked)>0 or len(flags.newTesting)>0: + debug("new masked: %s",flags.new_masked) + debug("new unmasked: %s", flags.new_unmasked) + debug("new testing: %s", flags.newTesting) + if not self.session.get_boolean("keywords", "dialogs"): + self.session.set("keywords", changed_flags_dialog(_("masking keywords"))[1], "dialogs") + try: + flags.write_masked() + flags.write_testing() + except IOError, e: + io_ex_dialog(e) + return True + else: + system.reload_settings() + + model, iter = self.queueList.get_selection().get_selected() + + if iter is None: + if model.iter_n_children(None) == 1: # only one queue there - take this as being selected + iter = model.get_iter_root() + else: + return False + + self.sysNotebook.set_current_page(self.CONSOLE_PAGE) + + # test which type of queue we have here + if self.queueTree.is_in_emerge(iter): + self.queue.emerge(force = True) + elif self.queueTree.is_in_unmerge(iter): + self.queue.unmerge(force = True) + else: + self.queue.update_world(sets = self.updateSets, force=True, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) + + return True + + def cb_update_clicked (self, action): + def __update(): + + def cb_idle_append (updating): + try: + try: + for pkg, old_pkg in updating: + self.queue.append(pkg.get_cpv(), type = "update", unmask = False) + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + for pkg, old_pkg in updating: + self.queue.append(pkg.get_cpv(), type = "update", unmask = True) + + except BlockedException, e: + blocked_dialog(e[0], e[1]) + self.queue.remove_children(self.queueTree.get_update_it()) + + return False + + watch = gtk.gdk.Cursor(gtk.gdk.WATCH) + self.window.window.set_cursor(watch) + try: + if system.has_set_support(): + confsets = [x.strip() for x in self.cfg.get("updatesets").split(",")] + self.updateSets = [s for s in confsets if s in system.get_sets()] + updating = system.update_world(sets = self.updateSets, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) + else: + updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep")) + self.updateSets = ("world",) + + debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating)) + gobject.idle_add(cb_idle_append, updating) + finally: + self.window.window.set_cursor(None) + + GtkThread(name="Update-Thread", target=__update).start() + + return True + + def cb_remove_clicked (self, button): + """Removes a selected item in the (un)emerge-queue if possible.""" + model, iter = self.queueList.get_selection().get_selected() + + if iter: + parent = model.iter_parent(iter) + + if self.queueTree.is_in_update(iter) and parent: + if remove_updates_dialog() == gtk.RESPONSE_YES: + self.queue.remove_with_children(self.queueTree.get_update_it()) + + elif not parent: # top-level + if model.iter_n_children(iter) > 0: # and has children which can be removed :) + if remove_queue_dialog() == gtk.RESPONSE_YES : + self.queue.remove_with_children(iter) + else: + self.queue.remove(iter) + + elif model.iter_parent(parent): # this is in the 3rd level => dependency + remove_deps_dialog() + else: + self.queue.remove_with_children(iter) + + if model.iter_n_children(parent) == 0: # no more children left - remove queue too + self.queue.remove(parent) + + return True + return False + + def cb_sync_clicked (self, action): + self.sysNotebook.set_current_page(self.CONSOLE_PAGE) + cmd = self.cfg.get("syncCommand") + + if cmd != "emerge --sync": + cmd = cmd.split() + self.queue.sync(cmd) + else: + self.queue.sync() + + def cb_save_flags_clicked (self, action): + try: + flags.write_use_flags() + flags.write_testing() + flags.write_masked() + except IOError, e: + io_ex_dialog(e) + + @Window.watch_cursor + def cb_reload_clicked (self, action): + """Reloads the portage settings and the database.""" + system.reload_settings() + self.db.reload() + + @Window.watch_cursor + def cb_search_clicked (self, entry): + """Do a search.""" + text = entry.get_text() + if text != "": + if "/" not in text: + text = "/.*"+text # only look for package names + + packages = system.find_packages(text, with_version = False) + + if packages == []: + nothing_found_dialog() + else: + if len(packages) == 1: + self.jump_to(packages[0]) + else: + SearchWindow(self.window, packages, self.jump_to) + + return True + + def cb_search_changed (self, *args): + """ + Called when the user enters something in the search field. + Updates the packages according to the search expression. + """ + if not self.__searchChanged and self.cfg.get_boolean("searchOnType", section="GUI"): + self.__searchChanged = True + + def __update(): + self.__searchChanged = False + txt = self.searchEntry.get_text() + + if txt or self.db.restrict: + self.db.restrict = txt + + self.refresh_stores() + self.catList.get_selection().select_path("0") # XXX make this smarter + + return False # not again ;) + + gobject.timeout_add(100, __update) + + def cb_delete_search_clicked (self, *args): + self.searchEntry.set_text("") + return True + + def cb_preferences_clicked (self, *args): + """ + User wants to open preferences. + """ + PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos, self.fill_cat_store) + return True + + def cb_about_clicked (self, *args): + """ + User wants to open about dialog. + """ + AboutWindow(self.window) + return True + + def cb_plugins_clicked (self, *args): + """ + User wants to open plugin dialog. + """ + queue = plugin.get_plugin_queue() + if queue is None: + plugins = [] + else: + plugins = list(queue.get_plugins()) + + PluginWindow(self.window, plugins, self.queue) + return True + + def cb_show_updates_clicked (self, *args): + """ + Show the list of updateble packages. + """ + + def __update(): + def cb_idle_show(packages): + """ + Callback opening the menu when the calculation is finished. + + @returns: False to signal that it is finished + """ + UpdateWindow(self.window, packages, self.queue, self.jump_to) + return False + + watch = gtk.gdk.Cursor(gtk.gdk.WATCH) + self.window.window.set_cursor(watch) + + packages = [] + try: + packages.extend(system.get_updated_packages()) + finally: + self.window.window.set_cursor(None) + + gobject.idle_add(cb_idle_show, packages) + + GtkThread(name="Show Updates Thread", target = __update).start() + return True + + def cb_show_installed_toggled (self, *args): + """ + Toggle the "show only installed" option. + """ + self.showAll = not self.showAll + self.refresh_stores() + + def cb_right_click (self, object, event): + """ + Called when the user right clicks somewhere. + Used to display a menu. + + This method should handle ALL such menus. + + @param object: the object/widget where the click is done + @type object: gtk.Widget + @param event: the event triggered + @type event: gtk.gdk.Event + """ + + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # 3 == right click + x = int(event.x) + y = int(event.y) + time = event.time + + if object == self.console: + self.consolePopup.popup(None, None, None, event.button, time) + else: + return False + else: + return False + + def cb_oneshot_clicked (self, *args): + """ + Mark a package as oneshot. + """ + sel = self.queueList.get_selection() + store, it = sel.get_selected() + if it: + if self.queueTree.is_in_emerge(it) and self.queueTree.iter_has_parent(it): + package = store.get_value(it, 0) + set = (package not in self.queue.oneshotmerge) + + self.queue.append(package, update = True, oneshot = set, forceUpdate = True) + + def cb_pause_emerge (self, curr): + """ + This method returns a callback for a "pause emerge" toggle button. + It is needed as there are different toggle buttons of this type and if one is clicked, + the others should be marked too. + + @param curr: The button to return the callback for. + @type curr: gtk.ToggleButton + """ + def pause (cb): + """ + The actual callback. + + Mark all other buttons too. + + @param cb: The button which got toggled. + @type cb: gtk.ToggleButton + """ + + # pause or continue + self.emergePaused = cb.get_active() + if not self.emergePaused: + self.queue.continue_emerge() + #self.tray.set_from_file(APP_ICON) + else: + self.queue.stop_emerge() + #self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png")) + + # block the handlers of the other buttons + # so that calling "set_active" does not call this callback recursivly + for v in self.pauseItems.itervalues(): + v[0].handler_block(v[1]) + + # mark the others + for k, v in self.pauseItems.iteritems(): + if k != curr: + v[0].set_active(self.emergePaused) + + # unblock + for v in self.pauseItems.itervalues(): + v[0].handler_unblock(v[1]) + + return False + return pause + + def cb_kill_clicked (self, *args): + """ + Kill emerge. + """ + self.queue.kill_emerge() + if self.emergePaused: # unmark the "pause emerge" buttons + self.pauseItems["menu"][0].set_active(False) # calling one button is enough (see: cb_pause_emerge) + + def cb_copy_clicked (self, *args): + """ + Copy marked text in the terminal to clipboard. + """ + self.console.copy_clipboard() + + def cb_delete (self, *args): + """ + Called when the user wants to quit the application. + + Asks the user for confirmation if there is something in the queue. + Also saves session data. + """ + + self.__save_queue = False + + if not self.queue.is_empty(): + ret = queue_not_empty_dialog() + if ret == gtk.RESPONSE_CANCEL: + return True + else: # there is sth in queue AND the user still wants to close -> kill emerge + self.__save_queue = (ret == gtk.RESPONSE_YES) + self.queue.kill_emerge() + + # write session + self.session.save() + + return False + + def cb_minimized (self, window, event): + """ + User wants to minimize the window. + If it is possible to minimize to tray, it is done. + """ + + if self.tray and self.cfg.get_boolean("hideOnMinimize", "GUI"): + if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: + if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: + self.window.hide() + return True + + return False + + def cb_systray_activated (self, *args): + """ + Systray was activated. Show or hide the window. + """ + if self.window.iconify_initially: + self.window.deiconify() + self.window.show() + self.window.window.show() + else: + self.window.iconify() + + def cb_close (self, *args): + """ + "Close" menu entry called. + Emulate normal quitting. + """ + if not self.cb_delete(): # do the checks + self.window.destroy() + + def cb_destroy (self, *args): + """ + Calls main_quit(). + """ + gtk.main_quit() + + def main (self): + """ + Main. + """ + gobject.threads_init() + # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add(). + # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp + plugin.hook("main")(gtk.main)() diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py index a0694be..eccf302 100644 --- a/portato/gui/windows/plugin.py +++ b/portato/gui/windows/plugin.py @@ -21,161 +21,161 @@ from ...backend.exceptions import PackageNotFoundException, BlockedException from ...helper import debug class PluginWindow (AbstractDialog): - - statsStore = gtk.ListStore(str) - - for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")): - statsStore.append([s]) - - def __init__ (self, parent, plugins, queue = None): - """Constructor. - - @param parent: the parent window - @type parent: gtk.Window""" - - AbstractDialog.__init__(self, parent) - self.plugins = plugins - self.queue = queue - self.changedPlugins = {} - self.inst = [] - self.ninst = [] - - self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) - map(lambda b: b.set_mode(False), self.buttons) - - self.descrLabel = self.tree.get_widget("descrLabel") - self.authorLabel = self.tree.get_widget("authorLabel") - - self.depExpander = self.tree.get_widget("depExpander") - self.installBtn = self.tree.get_widget("installBtn") - self.depList = self.tree.get_widget("depList") - self.build_dep_list() - - self.buttonBox = self.tree.get_widget("buttonBox") - - self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) - - self.view = self.tree.get_widget("pluginList") - self.store = gtk.ListStore(str) - - self.view.set_model(self.store) - - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn("Plugin", cell, markup = 0) - self.view.append_column(col) - - for p in plugins: - self.store.append(["<b>%s</b>" % p.name]) - - self.view.get_selection().connect("changed", self.cb_list_selection) - - self.window.show_all() - - def build_dep_list (self): - store = gtk.ListStore(gtk.gdk.Pixbuf, str) - - self.depList.set_model(store) - - col = gtk.TreeViewColumn() - - cell = gtk.CellRendererPixbuf() - col.pack_start(cell, False) - col.add_attribute(cell, "pixbuf", 0) - - cell = gtk.CellRendererText() - col.pack_start(cell, True) - col.add_attribute(cell, "text", 1) - - self.depList.append_column(col) - - def fill_dep_list (self, inst = [], ninst = []): - store = self.depList.get_model() - store.clear() - - for dep in inst: - store.append([self.instIcon, dep]) - for dep in ninst: - store.append([None, dep]) - - def cb_state_toggled (self, rb): - - plugin = self.get_actual() - - if plugin: - state = self.buttons.index(rb) - - self.changedPlugins[plugin] = state - debug("new changed plugins: %s => %d", plugin.name, state) - - def cb_ok_clicked (self, btn): - for plugin, val in self.changedPlugins.iteritems(): - plugin.status = val - - self.close() - return True - - def cb_list_selection (self, selection): - plugin = self.get_actual() - self.inst = [] - self.ninst = [] - - if plugin: - if not plugin.description: - self.descrLabel.hide() - else: - self.descrLabel.set_markup(plugin.description) - self.descrLabel.show() - - self.authorLabel.set_label(plugin.author) - - status = self.changedPlugins.get(plugin, plugin.status) - self.buttons[status].set_active(True) - - if plugin.deps: - - for dep in plugin.deps: - if system.find_packages(dep, pkgSet = system.SET_INSTALLED, with_version = False): - self.inst.append(dep) - else: - self.ninst.append(dep) - - self.fill_dep_list(self.inst, self.ninst) - self.depExpander.show() - - self.installBtn.show() - self.installBtn.set_sensitive(bool(self.ninst)) - - else: - self.installBtn.hide() - self.depExpander.hide() - - self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED) - - def cb_install_clicked (self, *args): - if not self.queue: - return False - - for cpv in self.ninst: - - pkg = system.find_best_match(cpv, masked = False, only_cpv = True) - if not pkg: - pkg = system.find_best_match(cpv, masked = True, only_cpv = True) - - try: - try: - self.queue.append(pkg, type = "install") - except PackageNotFoundException, e: - if unmask_dialog(e[0]) == gtk.RESPONSE_YES: - self.queue.append(pkg, type = "install", unmask = True) - except BlockedException, e: - blocked_dialog(e[0], e[1]) - - return True - - def get_actual (self): - store, it = self.view.get_selection().get_selected() - - if it: - return self.plugins[int(store.get_path(it)[0])] - else: - return None + + statsStore = gtk.ListStore(str) + + for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")): + statsStore.append([s]) + + def __init__ (self, parent, plugins, queue = None): + """Constructor. + + @param parent: the parent window + @type parent: gtk.Window""" + + AbstractDialog.__init__(self, parent) + self.plugins = plugins + self.queue = queue + self.changedPlugins = {} + self.inst = [] + self.ninst = [] + + self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB")) + map(lambda b: b.set_mode(False), self.buttons) + + self.descrLabel = self.tree.get_widget("descrLabel") + self.authorLabel = self.tree.get_widget("authorLabel") + + self.depExpander = self.tree.get_widget("depExpander") + self.installBtn = self.tree.get_widget("installBtn") + self.depList = self.tree.get_widget("depList") + self.build_dep_list() + + self.buttonBox = self.tree.get_widget("buttonBox") + + self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) + + self.view = self.tree.get_widget("pluginList") + self.store = gtk.ListStore(str) + + self.view.set_model(self.store) + + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn("Plugin", cell, markup = 0) + self.view.append_column(col) + + for p in plugins: + self.store.append(["<b>%s</b>" % p.name]) + + self.view.get_selection().connect("changed", self.cb_list_selection) + + self.window.show_all() + + def build_dep_list (self): + store = gtk.ListStore(gtk.gdk.Pixbuf, str) + + self.depList.set_model(store) + + col = gtk.TreeViewColumn() + + cell = gtk.CellRendererPixbuf() + col.pack_start(cell, False) + col.add_attribute(cell, "pixbuf", 0) + + cell = gtk.CellRendererText() + col.pack_start(cell, True) + col.add_attribute(cell, "text", 1) + + self.depList.append_column(col) + + def fill_dep_list (self, inst = [], ninst = []): + store = self.depList.get_model() + store.clear() + + for dep in inst: + store.append([self.instIcon, dep]) + for dep in ninst: + store.append([None, dep]) + + def cb_state_toggled (self, rb): + + plugin = self.get_actual() + + if plugin: + state = self.buttons.index(rb) + + self.changedPlugins[plugin] = state + debug("new changed plugins: %s => %d", plugin.name, state) + + def cb_ok_clicked (self, btn): + for plugin, val in self.changedPlugins.iteritems(): + plugin.status = val + + self.close() + return True + + def cb_list_selection (self, selection): + plugin = self.get_actual() + self.inst = [] + self.ninst = [] + + if plugin: + if not plugin.description: + self.descrLabel.hide() + else: + self.descrLabel.set_markup(plugin.description) + self.descrLabel.show() + + self.authorLabel.set_label(plugin.author) + + status = self.changedPlugins.get(plugin, plugin.status) + self.buttons[status].set_active(True) + + if plugin.deps: + + for dep in plugin.deps: + if system.find_packages(dep, pkgSet = system.SET_INSTALLED, with_version = False): + self.inst.append(dep) + else: + self.ninst.append(dep) + + self.fill_dep_list(self.inst, self.ninst) + self.depExpander.show() + + self.installBtn.show() + self.installBtn.set_sensitive(bool(self.ninst)) + + else: + self.installBtn.hide() + self.depExpander.hide() + + self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED) + + def cb_install_clicked (self, *args): + if not self.queue: + return False + + for cpv in self.ninst: + + pkg = system.find_best_match(cpv, masked = False, only_cpv = True) + if not pkg: + pkg = system.find_best_match(cpv, masked = True, only_cpv = True) + + try: + try: + self.queue.append(pkg, type = "install") + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES: + self.queue.append(pkg, type = "install", unmask = True) + except BlockedException, e: + blocked_dialog(e[0], e[1]) + + return True + + def get_actual (self): + store, it = self.view.get_selection().get_selected() + + if it: + return self.plugins[int(store.get_path(it)[0])] + else: + return None diff --git a/portato/gui/windows/preference.py b/portato/gui/windows/preference.py index dba28b1..94d7ade 100644 --- a/portato/gui/windows/preference.py +++ b/portato/gui/windows/preference.py @@ -22,197 +22,197 @@ from ..utils import get_color from ...helper import debug class PreferenceWindow (AbstractDialog): - """Window displaying some preferences.""" - - # all checkboxes in the window - # widget name -> option name - checkboxes = { - "collapseCatCheck" : ("collapseCats", "GUI"), - "consoleUpdateCheck" : ("updateConsole", "GUI"), - "debugCheck" : "debug", - "deepCheck" : "deep", - "newUseCheck" : "newuse", - "maskPerVersionCheck" : "maskPerVersion", - "minimizeCheck" : ("hideOnMinimize", "GUI"), - "searchOnTypeCheck" : ("searchOnType", "GUI"), - "showSlotsCheck" : ("showSlots", "GUI"), - "systrayCheck" : ("showSystray", "GUI"), - "testPerVersionCheck" : "keywordPerVersion", - "titleUpdateCheck" : ("updateTitle", "GUI"), - "usePerVersionCheck" : "usePerVersion" - } - - # all edits in the window - # widget name -> option name - edits = { - "maskFileEdit" : "maskFile", - "testFileEdit" : "keywordFile", - "useFileEdit" : "useFile", - "syncCommandEdit" : "syncCommand", - "browserEdit" : ("browserCmd", "GUI") - } - - # the mappings for the tabpos combos - tabpos = { - 1 : gtk.POS_TOP, - 2 : gtk.POS_BOTTOM, - 3 : gtk.POS_LEFT, - 4 : gtk.POS_RIGHT - } - - def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn, catmodel_fn): - """Constructor. - - @param parent: parent window - @type parent: gtk.Window - @param cfg: configuration object - @type cfg: gui_helper.Config - @param console_fn: function to call to set the console font - @type console_fn: function(string) - @param linkbtn_fn: function to call to set the linkbutton behavior - @type linkbtn_fn: function(string) - @param tabpos_fn: function to call to set the tabposition of the notebooks - @type tabpos_fn: function(gtk.ComboBox,int) - @param catmodel_fn: function to call to set the model of the cat list (collapsed/not collapsed) - @type catmodel_fn: function()""" - - AbstractDialog.__init__(self, parent) - - # our config - self.cfg = cfg - - # the setter functions - self.console_fn = console_fn - self.linkbtn_fn = linkbtn_fn - self.tabpos_fn = tabpos_fn - self.catmodel_fn = catmodel_fn - - # set the bg-color of the hint - hintEB = self.tree.get_widget("hintEB") - hintEB.modify_bg(gtk.STATE_NORMAL, get_color(self.cfg, "prefhint")) - - # the checkboxes - for box, val in self.checkboxes.iteritems(): - if isinstance(val, tuple): - self.tree.get_widget(box).\ - set_active(self.cfg.get_boolean(val[0], section = val[1])) - else: - self.tree.get_widget(box).\ - set_active(self.cfg.get_boolean(val)) - - # the edits - for edit, val in self.edits.iteritems(): - if isinstance(val,tuple): - self.tree.get_widget(edit).\ - set_text(self.cfg.get(val[0], section = val[1])) - else: - self.tree.get_widget(edit).\ - set_text(self.cfg.get(val)) - - # the set list - self.setList = self.tree.get_widget("setList") - if system.has_set_support(): - self.fill_setlist() - self.tree.get_widget("setFrame").show() - - # the console font button - self.consoleFontBtn = self.tree.get_widget("consoleFontBtn") - self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GUI")) - - # the console title length spin button - self.titleLengthSpinBtn = self.tree.get_widget("titleLengthSpinBtn") - self.titleLengthSpinBtn.set_value(int(self.cfg.get("titlelength", section = "GUI"))) - - # the comboboxes - self.systemTabCombo = self.tree.get_widget("systemTabCombo") - self.pkgTabCombo = self.tree.get_widget("packageTabCombo") - - for c in (self.systemTabCombo, self.pkgTabCombo): - m = c.get_model() - m.clear() - for i in (_("Top"), _("Bottom"), _("Left"), _("Right")): - m.append((i,)) - - self.systemTabCombo.set_active(int(self.cfg.get("systemTabPos", section = "GUI"))-1) - self.pkgTabCombo.set_active(int(self.cfg.get("packageTabPos", section = "GUI"))-1) - - self.window.show_all() - - def _save(self): - """Sets all options in the Config-instance.""" - - for box, val in self.checkboxes.iteritems(): - if isinstance(val, tuple): - self.cfg.set_boolean(val[0], self.tree.get_widget(box).get_active(), section = val[1]) - else: - self.cfg.set_boolean(val, self.tree.get_widget(box).get_active()) - - for edit, val in self.edits.iteritems(): - if isinstance(val,tuple): - self.cfg.set(val[0], self.tree.get_widget(edit).get_text(), section = val[1]) - else: - self.cfg.set(val,self.tree.get_widget(edit).get_text()) - - if system.has_set_support(): - self.cfg.set("updatesets", ", ".join(sorted(name for enabled, markup, descr, name in self.setList.get_model() if enabled))) - - font = self.consoleFontBtn.get_font_name() - self.cfg.set("consolefont", font, section = "GUI") - self.console_fn(font) - - self.cfg.set("titlelength", str(self.titleLengthSpinBtn.get_value_as_int()), section = "GUI") - - pkgPos = self.pkgTabCombo.get_active()+1 - sysPos = self.systemTabCombo.get_active()+1 - - self.cfg.set("packageTabPos", str(pkgPos), section = "GUI") - self.cfg.set("systemTabPos", str(sysPos), section = "GUI") - - self.tabpos_fn(map(self.tabpos.get, (pkgPos, sysPos))) - - self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI")) - - self.catmodel_fn() - - def fill_setlist (self): - store = gtk.ListStore(bool, str, str, str) - - enabled = [x.strip() for x in self.cfg.get("updatesets").split(",")] - - for set, descr in system.get_sets(description = True): - store.append([set in enabled, "<i>%s</i>" % set, descr, set]) - - tCell = gtk.CellRendererToggle() - tCell.set_property("activatable", True) - tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ... - - sCell = gtk.CellRendererText() - - col = gtk.TreeViewColumn(_("Package Set"), tCell, active = 0) - col.pack_start(sCell) - col.add_attribute(sCell, "markup", 1) - self.setList.append_column(col) - - self.setList.append_column(gtk.TreeViewColumn(_("Description"), sCell, text = 2)) - - self.setList.set_model(store) - - def cb_ok_clicked(self, button): - """Saves, writes to config-file and closes the window.""" - self._save() - try: - self.cfg.write() - except IOError, e: - io_ex_dialog(e) - - self.window.destroy() + """Window displaying some preferences.""" + + # all checkboxes in the window + # widget name -> option name + checkboxes = { + "collapseCatCheck" : ("collapseCats", "GUI"), + "consoleUpdateCheck" : ("updateConsole", "GUI"), + "debugCheck" : "debug", + "deepCheck" : "deep", + "newUseCheck" : "newuse", + "maskPerVersionCheck" : "maskPerVersion", + "minimizeCheck" : ("hideOnMinimize", "GUI"), + "searchOnTypeCheck" : ("searchOnType", "GUI"), + "showSlotsCheck" : ("showSlots", "GUI"), + "systrayCheck" : ("showSystray", "GUI"), + "testPerVersionCheck" : "keywordPerVersion", + "titleUpdateCheck" : ("updateTitle", "GUI"), + "usePerVersionCheck" : "usePerVersion" + } + + # all edits in the window + # widget name -> option name + edits = { + "maskFileEdit" : "maskFile", + "testFileEdit" : "keywordFile", + "useFileEdit" : "useFile", + "syncCommandEdit" : "syncCommand", + "browserEdit" : ("browserCmd", "GUI") + } + + # the mappings for the tabpos combos + tabpos = { + 1 : gtk.POS_TOP, + 2 : gtk.POS_BOTTOM, + 3 : gtk.POS_LEFT, + 4 : gtk.POS_RIGHT + } + + def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn, catmodel_fn): + """Constructor. + + @param parent: parent window + @type parent: gtk.Window + @param cfg: configuration object + @type cfg: gui_helper.Config + @param console_fn: function to call to set the console font + @type console_fn: function(string) + @param linkbtn_fn: function to call to set the linkbutton behavior + @type linkbtn_fn: function(string) + @param tabpos_fn: function to call to set the tabposition of the notebooks + @type tabpos_fn: function(gtk.ComboBox,int) + @param catmodel_fn: function to call to set the model of the cat list (collapsed/not collapsed) + @type catmodel_fn: function()""" + + AbstractDialog.__init__(self, parent) + + # our config + self.cfg = cfg + + # the setter functions + self.console_fn = console_fn + self.linkbtn_fn = linkbtn_fn + self.tabpos_fn = tabpos_fn + self.catmodel_fn = catmodel_fn + + # set the bg-color of the hint + hintEB = self.tree.get_widget("hintEB") + hintEB.modify_bg(gtk.STATE_NORMAL, get_color(self.cfg, "prefhint")) + + # the checkboxes + for box, val in self.checkboxes.iteritems(): + if isinstance(val, tuple): + self.tree.get_widget(box).\ + set_active(self.cfg.get_boolean(val[0], section = val[1])) + else: + self.tree.get_widget(box).\ + set_active(self.cfg.get_boolean(val)) + + # the edits + for edit, val in self.edits.iteritems(): + if isinstance(val,tuple): + self.tree.get_widget(edit).\ + set_text(self.cfg.get(val[0], section = val[1])) + else: + self.tree.get_widget(edit).\ + set_text(self.cfg.get(val)) + + # the set list + self.setList = self.tree.get_widget("setList") + if system.has_set_support(): + self.fill_setlist() + self.tree.get_widget("setFrame").show() + + # the console font button + self.consoleFontBtn = self.tree.get_widget("consoleFontBtn") + self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GUI")) + + # the console title length spin button + self.titleLengthSpinBtn = self.tree.get_widget("titleLengthSpinBtn") + self.titleLengthSpinBtn.set_value(int(self.cfg.get("titlelength", section = "GUI"))) + + # the comboboxes + self.systemTabCombo = self.tree.get_widget("systemTabCombo") + self.pkgTabCombo = self.tree.get_widget("packageTabCombo") + + for c in (self.systemTabCombo, self.pkgTabCombo): + m = c.get_model() + m.clear() + for i in (_("Top"), _("Bottom"), _("Left"), _("Right")): + m.append((i,)) + + self.systemTabCombo.set_active(int(self.cfg.get("systemTabPos", section = "GUI"))-1) + self.pkgTabCombo.set_active(int(self.cfg.get("packageTabPos", section = "GUI"))-1) + + self.window.show_all() + + def _save(self): + """Sets all options in the Config-instance.""" + + for box, val in self.checkboxes.iteritems(): + if isinstance(val, tuple): + self.cfg.set_boolean(val[0], self.tree.get_widget(box).get_active(), section = val[1]) + else: + self.cfg.set_boolean(val, self.tree.get_widget(box).get_active()) + + for edit, val in self.edits.iteritems(): + if isinstance(val,tuple): + self.cfg.set(val[0], self.tree.get_widget(edit).get_text(), section = val[1]) + else: + self.cfg.set(val,self.tree.get_widget(edit).get_text()) + + if system.has_set_support(): + self.cfg.set("updatesets", ", ".join(sorted(name for enabled, markup, descr, name in self.setList.get_model() if enabled))) + + font = self.consoleFontBtn.get_font_name() + self.cfg.set("consolefont", font, section = "GUI") + self.console_fn(font) + + self.cfg.set("titlelength", str(self.titleLengthSpinBtn.get_value_as_int()), section = "GUI") + + pkgPos = self.pkgTabCombo.get_active()+1 + sysPos = self.systemTabCombo.get_active()+1 + + self.cfg.set("packageTabPos", str(pkgPos), section = "GUI") + self.cfg.set("systemTabPos", str(sysPos), section = "GUI") + + self.tabpos_fn(map(self.tabpos.get, (pkgPos, sysPos))) + + self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI")) + + self.catmodel_fn() + + def fill_setlist (self): + store = gtk.ListStore(bool, str, str, str) + + enabled = [x.strip() for x in self.cfg.get("updatesets").split(",")] + + for set, descr in system.get_sets(description = True): + store.append([set in enabled, "<i>%s</i>" % set, descr, set]) + + tCell = gtk.CellRendererToggle() + tCell.set_property("activatable", True) + tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ... + + sCell = gtk.CellRendererText() + + col = gtk.TreeViewColumn(_("Package Set"), tCell, active = 0) + col.pack_start(sCell) + col.add_attribute(sCell, "markup", 1) + self.setList.append_column(col) + + self.setList.append_column(gtk.TreeViewColumn(_("Description"), sCell, text = 2)) + + self.setList.set_model(store) + + def cb_ok_clicked(self, button): + """Saves, writes to config-file and closes the window.""" + self._save() + try: + self.cfg.write() + except IOError, e: + io_ex_dialog(e) + + self.window.destroy() - def cb_cancel_clicked (self, button): - """Just closes - w/o saving.""" - self.window.destroy() + def cb_cancel_clicked (self, button): + """Just closes - w/o saving.""" + self.window.destroy() - def cb_check_toggled (self, cell, path): - # for whatever reason we have to define normal toggle behavior explicitly - store = self.setList.get_model() - store[path][0] = not store[path][0] - return True + def cb_check_toggled (self, cell, path): + # for whatever reason we have to define normal toggle behavior explicitly + store = self.setList.get_model() + store[path][0] = not store[path][0] + return True diff --git a/portato/gui/windows/search.py b/portato/gui/windows/search.py index e776dd1..415cbfe 100644 --- a/portato/gui/windows/search.py +++ b/portato/gui/windows/search.py @@ -17,59 +17,59 @@ from .basic import AbstractDialog from ...helper import debug class SearchWindow (AbstractDialog): - """A window showing the results of a search process.""" - - def __init__ (self, parent, list, jump_to): - """Constructor. + """A window showing the results of a search process.""" + + def __init__ (self, parent, list, jump_to): + """Constructor. - @param parent: parent-window - @type parent: gtk.Window - @param list: list of results to show - @type list: string[] - @param jump_to: function to call if "OK"-Button is hit - @type jump_to: function(string)""" - - AbstractDialog.__init__(self, parent) - - self.jump_to = jump_to # function to call for jumping - self.list = list - self.list.sort() - - # combo box - self.searchList = self.tree.get_widget("searchList") - self.build_sort_list() - self.searchList.get_selection().select_path(0) + @param parent: parent-window + @type parent: gtk.Window + @param list: list of results to show + @type list: string[] + @param jump_to: function to call if "OK"-Button is hit + @type jump_to: function(string)""" + + AbstractDialog.__init__(self, parent) + + self.jump_to = jump_to # function to call for jumping + self.list = list + self.list.sort() + + # combo box + self.searchList = self.tree.get_widget("searchList") + self.build_sort_list() + self.searchList.get_selection().select_path(0) - # finished --> show - self.window.show_all() + # finished --> show + self.window.show_all() - def build_sort_list (self): - """Builds the sort list.""" - - store = gtk.ListStore(str) - self.searchList.set_model(store) + def build_sort_list (self): + """Builds the sort list.""" + + store = gtk.ListStore(str) + self.searchList.set_model(store) - # build categories - for p in self.list: - store.append(["%s/<b>%s</b>" % tuple(p.split("/"))]) + # build categories + for p in self.list: + store.append(["%s/<b>%s</b>" % tuple(p.split("/"))]) - cell = gtk.CellRendererText() - col = gtk.TreeViewColumn(_("Results"), cell, markup = 0) - self.searchList.append_column(col) + cell = gtk.CellRendererText() + col = gtk.TreeViewColumn(_("Results"), cell, markup = 0) + self.searchList.append_column(col) - def ok (self, *args): - self.jump() - self.close() - - def jump (self, *args): - model, iter = self.searchList.get_selection().get_selected() - self.jump_to(self.list[model.get_path(iter)[0]]) + def ok (self, *args): + self.jump() + self.close() + + def jump (self, *args): + model, iter = self.searchList.get_selection().get_selected() + self.jump_to(self.list[model.get_path(iter)[0]]) - def cb_key_pressed_combo (self, widget, event): - """Emulates a ok-button-click.""" - keyname = gtk.gdk.keyval_name(event.keyval) - if keyname == "Return": # take it as an "OK" if Enter is pressed - self.jump() - return True - else: - return False + def cb_key_pressed_combo (self, widget, event): + """Emulates a ok-button-click.""" + keyname = gtk.gdk.keyval_name(event.keyval) + if keyname == "Return": # take it as an "OK" if Enter is pressed + self.jump() + return True + else: + return False diff --git a/portato/gui/windows/splash.py b/portato/gui/windows/splash.py index df19a9b..c27f74f 100644 --- a/portato/gui/windows/splash.py +++ b/portato/gui/windows/splash.py @@ -18,33 +18,33 @@ from .basic import Window from ...constants import VERSION, APP_ICON class SplashScreen (Window): - - def __init__ (self, startStr = ""): - Window.__init__(self) - - self.image = self.tree.get_widget("image") - self.genLabel = self.tree.get_widget("generalLabel") - self.descrLabel = self.tree.get_widget("descrLabel") - - self.image.set_from_file(APP_ICON) - self.genLabel.set_label("<b><big>Portato %s ...</big></b>" % VERSION) - - self.set_descr(startStr) - - def set_descr (self, string): - self.descrLabel.set_label(_("... is starting up: %s") % string) - self.do_iteration() - - def do_iteration (self): - while gtk.events_pending(): - gtk.main_iteration() - - def show (self): - self.window.show_all() - self.do_iteration() - - def hide (self): - self.window.hide() - self.do_iteration() - - __call__ = set_descr + + def __init__ (self, startStr = ""): + Window.__init__(self) + + self.image = self.tree.get_widget("image") + self.genLabel = self.tree.get_widget("generalLabel") + self.descrLabel = self.tree.get_widget("descrLabel") + + self.image.set_from_file(APP_ICON) + self.genLabel.set_label("<b><big>Portato %s ...</big></b>" % VERSION) + + self.set_descr(startStr) + + def set_descr (self, string): + self.descrLabel.set_label(_("... is starting up: %s") % string) + self.do_iteration() + + def do_iteration (self): + while gtk.events_pending(): + gtk.main_iteration() + + def show (self): + self.window.show_all() + self.do_iteration() + + def hide (self): + self.window.hide() + self.do_iteration() + + __call__ = set_descr diff --git a/portato/gui/windows/update.py b/portato/gui/windows/update.py index e369c49..297f666 100644 --- a/portato/gui/windows/update.py +++ b/portato/gui/windows/update.py @@ -21,97 +21,97 @@ from ...helper import debug class UpdateWindow (AbstractDialog): - def __init__ (self, parent, packages, queue, jump_to): - AbstractDialog.__init__(self, parent) - - self.queue = queue - self.jump = jump_to - - self.packages = system.sort_package_list(packages) - - self.build_list() - - self.window.show_all() - - def build_list (self): - - store = gtk.ListStore(bool, str) - self.view = self.tree.get_widget("packageList") - self.view.set_model(store) - - cell = gtk.CellRendererText() - tCell = gtk.CellRendererToggle() - tCell.set_property("activatable", True) - tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ... - - self.view.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0)) - self.view.append_column(gtk.TreeViewColumn(_("Package"), cell, text = 1)) - - for p in self.packages: - store.append([False, p.get_cpv()]) - - def cb_set_size (self, *args): - """ - This callback is called shortly before drawing. - It calculates the optimal size of the window. - The optimum is defined as: as large as possible w/o scrollbars - """ - - bb = self.tree.get_widget("updateBB") - vals = (self.view.get_vadjustment().upper+bb.size_request()[1]+10, # max size of list + size of BB + constant - self.parent.get_size()[1]) # size of the parent -> maximum size - debug("Size values for the list and for the parent: %d / %d", *vals) - val = int(min(vals)) - debug("Minimum value: %d", val) - self.window.set_geometry_hints(self.window, min_height = val) - - def cb_select_all_clicked (self, btn): - model = self.view.get_model() - iter = model.get_iter_first() - - while iter: - model.set_value(iter, 0, True) - iter = model.iter_next(iter) - - return True - - def cb_install_clicked (self, btn): - model = self.view.get_model() - iter = model.get_iter_first() - if iter is None: return - - items = [] - while iter: - if model.get_value(iter, 0): - items.append(model.get_value(iter, 1)) - iter = model.iter_next(iter) - - for item in items: - try: - try: - self.queue.append(item, type = "install", oneshot = True) - except PackageNotFoundException, e: - if unmask_dialog(e[0]) == gtk.RESPONSE_YES : - self.queue.append(item, type = "install", unmask = True, oneshot = True) - - except BlockedException, e: - blocked_dialog(e[0], e[1]) - - self.close() - return True - - def cb_package_selected (self, view): - sel = view.get_selection() - store, it = sel.get_selected() - if it: - package = system.new_package(store.get_value(it, 1)) - - self.jump(package.get_cp(), package.get_version()) - - return True - - def cb_check_toggled (self, cell, path): - # for whatever reason we have to define normal toggle behavior explicitly - store = self.view.get_model() - store[path][0] = not store[path][0] - return True + def __init__ (self, parent, packages, queue, jump_to): + AbstractDialog.__init__(self, parent) + + self.queue = queue + self.jump = jump_to + + self.packages = system.sort_package_list(packages) + + self.build_list() + + self.window.show_all() + + def build_list (self): + + store = gtk.ListStore(bool, str) + self.view = self.tree.get_widget("packageList") + self.view.set_model(store) + + cell = gtk.CellRendererText() + tCell = gtk.CellRendererToggle() + tCell.set_property("activatable", True) + tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ... + + self.view.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0)) + self.view.append_column(gtk.TreeViewColumn(_("Package"), cell, text = 1)) + + for p in self.packages: + store.append([False, p.get_cpv()]) + + def cb_set_size (self, *args): + """ + This callback is called shortly before drawing. + It calculates the optimal size of the window. + The optimum is defined as: as large as possible w/o scrollbars + """ + + bb = self.tree.get_widget("updateBB") + vals = (self.view.get_vadjustment().upper+bb.size_request()[1]+10, # max size of list + size of BB + constant + self.parent.get_size()[1]) # size of the parent -> maximum size + debug("Size values for the list and for the parent: %d / %d", *vals) + val = int(min(vals)) + debug("Minimum value: %d", val) + self.window.set_geometry_hints(self.window, min_height = val) + + def cb_select_all_clicked (self, btn): + model = self.view.get_model() + iter = model.get_iter_first() + + while iter: + model.set_value(iter, 0, True) + iter = model.iter_next(iter) + + return True + + def cb_install_clicked (self, btn): + model = self.view.get_model() + iter = model.get_iter_first() + if iter is None: return + + items = [] + while iter: + if model.get_value(iter, 0): + items.append(model.get_value(iter, 1)) + iter = model.iter_next(iter) + + for item in items: + try: + try: + self.queue.append(item, type = "install", oneshot = True) + except PackageNotFoundException, e: + if unmask_dialog(e[0]) == gtk.RESPONSE_YES : + self.queue.append(item, type = "install", unmask = True, oneshot = True) + + except BlockedException, e: + blocked_dialog(e[0], e[1]) + + self.close() + return True + + def cb_package_selected (self, view): + sel = view.get_selection() + store, it = sel.get_selected() + if it: + package = system.new_package(store.get_value(it, 1)) + + self.jump(package.get_cp(), package.get_version()) + + return True + + def cb_check_toggled (self, cell, path): + # for whatever reason we have to define normal toggle behavior explicitly + store = self.view.get_model() + store[path][0] = not store[path][0] + return True diff --git a/portato/gui/wrapper.py b/portato/gui/wrapper.py index 2c492d4..a160e2f 100644 --- a/portato/gui/wrapper.py +++ b/portato/gui/wrapper.py @@ -16,316 +16,316 @@ import vte from ..helper import debug class GtkTree (object): - """The implementation of the abstract tree.""" - - def __init__ (self, tree, col = 0): - """Constructor. - - @param tree: original tree - @type tree: gtk.TreeStore - @param col: the column where the cpv is stored - @type col: int""" - - self.tree = tree - self.cpv_col = col - self.emergeIt = None - self.unmergeIt = None - self.updateIt = None - - def build_append_value (self, cpv, oneshot = False, update = False, downgrade = False, version = None, useChange = []): - """ - Builds the list, which is going to be passed to append. - - @param cpv: the cpv - @type cpv: string (cpv) - @param oneshot: True if oneshot - @type oneshot: boolean - @param update: True if this is an update - @type update: boolean - @param downgrade: True if this is a downgrade - @type downgrade: boolean - @param version: the version we update from - @type version: string - @param useChange: list of changed useflags; use "-use" for removed and "+use" for added flags - @type useChange: string[] - - @returns: the created list - @rtype: list - """ - - string = "" - - if oneshot: - string += "<i>%s</i>" % _("oneshot") - - if update: - if oneshot: string += "; " - if version is not None: - string += "<i>%s</i>" % (_("updating from version %s") % version) - else: - string += "<i>%s</i>" % _("updating") - - elif downgrade: - if oneshot: string += "; " - if version is not None: - string += "<i>%s</i>" % (_("downgrading from version %s") % version) - else: - string += "<i>%s</i>" % _("downgrading") - - if useChange: - if update or downgrade or oneshot: string += "; " - string += "<i><b>%s </b></i>" % _("IUSE changes:") - useChange.sort() - string += "<i>%s</i>" % " ".join(useChange) - - return [cpv, string, False] - - def set_in_progress (self, it, to = True): - """ - Marks the queue where the given iterator belongs as being in progress. - - @param it: one iterator of the queue to mark to - @type it: Iterator - @param to: whether to enable or disable - @type to: boolean - """ - - iter = self.first_iter(it) - if to: - self.tree.set_value(iter, 1, "<b>%s</b>" % _("(In Progress)")) - else: - self.tree.set_value(iter, 1, "") - - self.tree.set_value(iter, 2, to) - - def is_in_progress (self, it): - """ - Returns whether the queue where the given iterator belongs to, is marked as "being in progress". - - @param it: the iterator - @type it: Iterator - @returns: whether the queue is marked "in progress" - @rtype: boolean - """ - return self.tree.get_value(it, 2) - - def get_emerge_it (self): - """ - Returns an iterator signaling the top of the emerge section. - - @returns: emerge-iterator - @rtype: Iterator - """ - if self.emergeIt is None: - self.emergeIt = self.append(None, ["<b>%s</b>" % _("Install"), "", False]) - return self.emergeIt - - def get_unmerge_it (self): - """ - Returns an iterator signaling the top of the unmerge section. - - @returns: unmerge-iterator - @rtype: Iterator - """ - if self.unmergeIt is None: - self.unmergeIt = self.append(None, ["<b>%s</b>" % _("Uninstall"), "", False]) - - return self.unmergeIt - - def get_update_it (self): - """ - Returns an iterator signaling the top of the update section. - - @returns: unmerge-iterator - @rtype: Iterator - """ - if self.updateIt is None: - self.updateIt = self.append(None, ["<b>%s</b>" % _("Update"), "", False]) - - return self.updateIt - - def first_iter (self, it): - """ - Returns the iterator at the top. - - @param it: the iterator - @type it: Iterator - @returns: the top iterator - @rtype: Iterator - """ - return self.tree.get_iter_from_string(self.tree.get_string_from_iter(it).split(":")[0]) - - def is_in (self, it, in_it): - return in_it and self.iter_equal(self.first_iter(it), in_it) - - def is_in_emerge (self, it): - """ - Checks whether an iterator is part of the "Emerge" section. - - @param it: the iterator to check - @type it: Iterator - @returns: True if the iter is part; False otherwise - @rtype: boolean - """ - return self.is_in(it, self.emergeIt) - - def is_in_unmerge (self, it): - """ - Checks whether an iterator is part of the "Unmerge" section. - - @param it: the iterator to check - @type it: Iterator - @returns: True if the iter is part; False otherwise - @rtype: boolean - """ - return self.is_in(it, self.unmergeIt) - - def is_in_update (self, it): - """ - Checks whether an iterator is part of the "Update" section. - - @param it: the iterator to check - @type it: Iterator - @returns: True if the iter is part; False otherwise - @rtype: boolean - """ - return self.is_in(it, self.updateIt) - - def iter_has_parent (self, it): - """ - Returns whether the actual iterator has a parent. - @param it: the iterator - @type it: Iterator - @returns: True if it has a parent it, else False. - @rtype: boolean - """ - return (self.tree.iter_parent(it) != None) - - def parent_iter (self, it): - """ - Returns the parent iter. - - @param it: the iterator - @type it: Iterator - @returns: Parent iterator or None if the current it has no parent. - @rtype: Iterator; None - """ - return self.tree.iter_parent(it) - - def first_child_iter (self, it): - """ - Returns the first child iter. - - @param it: the iterator - @type it: Iterator - @returns: First child iterator or None if the current it has no children. - @rtype: Iterator; None - """ - - return self.tree.iter_children(it) - - def iter_has_children (self, it): - """ - Returns whether the actual iterator has children. - - @param it: the iterator - @type it: Iterator - @returns: True if it has children, else False. - @rtype: boolean - """ - - return self.tree.iter_has_child(it) - - def next_iter (self, it): - """ - Returns the next iter. - - @param it: the iterator - @type it: Iterator - @returns: Next iterator or None if the current iter is the last one. - @rtype: Iterator; None - """ - return self.tree.iter_next(it) - - def get_value (self, it, column): - """ - Returns the value of the specific column at the given iterator. - - @param it: the iterator - @type it: Iterator - @param column: the column of the iterator from where to get the value - @type column: int - @returns: the value - @rtype: anything - """ - - return self.tree.get_value(it, column) - - def iter_equal (self, it, other_it): - """ - Checks whether to iterators are equal. - - @param it: the one iterator to compare - @type it: Iterator - @param other_it: the other iterator to compare - @type other_it: Iterator - @returns: True if both iterators are equal; False otherwise - @rtype boolean - """ - return self.tree.get_string_from_iter(it) == self.tree.get_string_from_iter(other_it) - - def append (self, parent = None, values = None): - """ - Appends some values right after the given parent. If parent is None, it is appended as the first element. - - @param parent: the iterator to append the values right after; if None it symbolizes the top - @type parent: Iterator - @param values: a list of values which are going to be appended to the tree - @type values: list - @returns: Iterator pointing to the newly appended stuff - @rtype: Iterator - """ - - return self.tree.append(parent, values) - - def remove (self, it): - """ - Removes an iterator out of the tree. - @attention: The iterator can point to anything hereafter. Do not reuse! - - @param it: iterator to remove - @type it: Iterator - """ - - if self.emergeIt and self.iter_equal(it, self.emergeIt) : self.emergeIt = None - elif self.unmergeIt and self.iter_equal(it, self.unmergeIt) : self.unmergeIt = None - elif self.updateIt and self.iter_equal(it, self.updateIt) : self.updateIt = None - - self.tree.remove(it) - - def get_original (self): - """ - Returns the original tree-object. - - @returns: original tree-object - @rtype: tree-object - """ - return self.tree - - def get_cpv_column (self): - """ - Returns the number of the column where the cpv's are stored. - - @returns: column with cpv's - @rtype: int - """ - return self.cpv_col + """The implementation of the abstract tree.""" + + def __init__ (self, tree, col = 0): + """Constructor. + + @param tree: original tree + @type tree: gtk.TreeStore + @param col: the column where the cpv is stored + @type col: int""" + + self.tree = tree + self.cpv_col = col + self.emergeIt = None + self.unmergeIt = None + self.updateIt = None + + def build_append_value (self, cpv, oneshot = False, update = False, downgrade = False, version = None, useChange = []): + """ + Builds the list, which is going to be passed to append. + + @param cpv: the cpv + @type cpv: string (cpv) + @param oneshot: True if oneshot + @type oneshot: boolean + @param update: True if this is an update + @type update: boolean + @param downgrade: True if this is a downgrade + @type downgrade: boolean + @param version: the version we update from + @type version: string + @param useChange: list of changed useflags; use "-use" for removed and "+use" for added flags + @type useChange: string[] + + @returns: the created list + @rtype: list + """ + + string = "" + + if oneshot: + string += "<i>%s</i>" % _("oneshot") + + if update: + if oneshot: string += "; " + if version is not None: + string += "<i>%s</i>" % (_("updating from version %s") % version) + else: + string += "<i>%s</i>" % _("updating") + + elif downgrade: + if oneshot: string += "; " + if version is not None: + string += "<i>%s</i>" % (_("downgrading from version %s") % version) + else: + string += "<i>%s</i>" % _("downgrading") + + if useChange: + if update or downgrade or oneshot: string += "; " + string += "<i><b>%s </b></i>" % _("IUSE changes:") + useChange.sort() + string += "<i>%s</i>" % " ".join(useChange) + + return [cpv, string, False] + + def set_in_progress (self, it, to = True): + """ + Marks the queue where the given iterator belongs as being in progress. + + @param it: one iterator of the queue to mark to + @type it: Iterator + @param to: whether to enable or disable + @type to: boolean + """ + + iter = self.first_iter(it) + if to: + self.tree.set_value(iter, 1, "<b>%s</b>" % _("(In Progress)")) + else: + self.tree.set_value(iter, 1, "") + + self.tree.set_value(iter, 2, to) + + def is_in_progress (self, it): + """ + Returns whether the queue where the given iterator belongs to, is marked as "being in progress". + + @param it: the iterator + @type it: Iterator + @returns: whether the queue is marked "in progress" + @rtype: boolean + """ + return self.tree.get_value(it, 2) + + def get_emerge_it (self): + """ + Returns an iterator signaling the top of the emerge section. + + @returns: emerge-iterator + @rtype: Iterator + """ + if self.emergeIt is None: + self.emergeIt = self.append(None, ["<b>%s</b>" % _("Install"), "", False]) + return self.emergeIt + + def get_unmerge_it (self): + """ + Returns an iterator signaling the top of the unmerge section. + + @returns: unmerge-iterator + @rtype: Iterator + """ + if self.unmergeIt is None: + self.unmergeIt = self.append(None, ["<b>%s</b>" % _("Uninstall"), "", False]) + + return self.unmergeIt + + def get_update_it (self): + """ + Returns an iterator signaling the top of the update section. + + @returns: unmerge-iterator + @rtype: Iterator + """ + if self.updateIt is None: + self.updateIt = self.append(None, ["<b>%s</b>" % _("Update"), "", False]) + + return self.updateIt + + def first_iter (self, it): + """ + Returns the iterator at the top. + + @param it: the iterator + @type it: Iterator + @returns: the top iterator + @rtype: Iterator + """ + return self.tree.get_iter_from_string(self.tree.get_string_from_iter(it).split(":")[0]) + + def is_in (self, it, in_it): + return in_it and self.iter_equal(self.first_iter(it), in_it) + + def is_in_emerge (self, it): + """ + Checks whether an iterator is part of the "Emerge" section. + + @param it: the iterator to check + @type it: Iterator + @returns: True if the iter is part; False otherwise + @rtype: boolean + """ + return self.is_in(it, self.emergeIt) + + def is_in_unmerge (self, it): + """ + Checks whether an iterator is part of the "Unmerge" section. + + @param it: the iterator to check + @type it: Iterator + @returns: True if the iter is part; False otherwise + @rtype: boolean + """ + return self.is_in(it, self.unmergeIt) + + def is_in_update (self, it): + """ + Checks whether an iterator is part of the "Update" section. + + @param it: the iterator to check + @type it: Iterator + @returns: True if the iter is part; False otherwise + @rtype: boolean + """ + return self.is_in(it, self.updateIt) + + def iter_has_parent (self, it): + """ + Returns whether the actual iterator has a parent. + @param it: the iterator + @type it: Iterator + @returns: True if it has a parent it, else False. + @rtype: boolean + """ + return (self.tree.iter_parent(it) != None) + + def parent_iter (self, it): + """ + Returns the parent iter. + + @param it: the iterator + @type it: Iterator + @returns: Parent iterator or None if the current it has no parent. + @rtype: Iterator; None + """ + return self.tree.iter_parent(it) + + def first_child_iter (self, it): + """ + Returns the first child iter. + + @param it: the iterator + @type it: Iterator + @returns: First child iterator or None if the current it has no children. + @rtype: Iterator; None + """ + + return self.tree.iter_children(it) + + def iter_has_children (self, it): + """ + Returns whether the actual iterator has children. + + @param it: the iterator + @type it: Iterator + @returns: True if it has children, else False. + @rtype: boolean + """ + + return self.tree.iter_has_child(it) + + def next_iter (self, it): + """ + Returns the next iter. + + @param it: the iterator + @type it: Iterator + @returns: Next iterator or None if the current iter is the last one. + @rtype: Iterator; None + """ + return self.tree.iter_next(it) + + def get_value (self, it, column): + """ + Returns the value of the specific column at the given iterator. + + @param it: the iterator + @type it: Iterator + @param column: the column of the iterator from where to get the value + @type column: int + @returns: the value + @rtype: anything + """ + + return self.tree.get_value(it, column) + + def iter_equal (self, it, other_it): + """ + Checks whether to iterators are equal. + + @param it: the one iterator to compare + @type it: Iterator + @param other_it: the other iterator to compare + @type other_it: Iterator + @returns: True if both iterators are equal; False otherwise + @rtype boolean + """ + return self.tree.get_string_from_iter(it) == self.tree.get_string_from_iter(other_it) + + def append (self, parent = None, values = None): + """ + Appends some values right after the given parent. If parent is None, it is appended as the first element. + + @param parent: the iterator to append the values right after; if None it symbolizes the top + @type parent: Iterator + @param values: a list of values which are going to be appended to the tree + @type values: list + @returns: Iterator pointing to the newly appended stuff + @rtype: Iterator + """ + + return self.tree.append(parent, values) + + def remove (self, it): + """ + Removes an iterator out of the tree. + @attention: The iterator can point to anything hereafter. Do not reuse! + + @param it: iterator to remove + @type it: Iterator + """ + + if self.emergeIt and self.iter_equal(it, self.emergeIt) : self.emergeIt = None + elif self.unmergeIt and self.iter_equal(it, self.unmergeIt) : self.unmergeIt = None + elif self.updateIt and self.iter_equal(it, self.updateIt) : self.updateIt = None + + self.tree.remove(it) + + def get_original (self): + """ + Returns the original tree-object. + + @returns: original tree-object + @rtype: tree-object + """ + return self.tree + + def get_cpv_column (self): + """ + Returns the number of the column where the cpv's are stored. + + @returns: column with cpv's + @rtype: int + """ + return self.cpv_col class GtkConsole (vte.Terminal): - """The implementation of the abstract Console for GTK.""" - - def reset (self): - """ - Resets the terminal. - """ - vte.Terminal.reset(self, True, True) + """The implementation of the abstract Console for GTK.""" + + def reset (self): + """ + Resets the terminal. + """ + vte.Terminal.reset(self, True, True) |