summaryrefslogtreecommitdiff
path: root/portato/gui/windows/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'portato/gui/windows/main.py')
-rw-r--r--portato/gui/windows/main.py1661
1 files changed, 1661 insertions, 0 deletions
diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py
new file mode 100644
index 0000000..2dbecde
--- /dev/null
+++ b/portato/gui/windows/main.py
@@ -0,0 +1,1661 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/gui/windows/main.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2008 René 'Necoro' Neumann
+# This is free software. You may redistribute copies of it under the terms of
+# the GNU General Public License version 2.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by René 'Necoro' Neumann <necoro@necoro.net>
+
+from __future__ import absolute_import, with_statement
+
+# gtk stuff
+import gtk
+import gobject
+
+# other
+import os.path
+import itertools as itt
+
+# our backend stuff
+from ...backend import flags, system # must be the first to avoid circular deps
+from ... import get_listener, plugin, dependency
+from ...helper import debug, warning, error, info, unique_array, N_, _
+from ...session import Session
+from ...constants import CONFIG_LOCATION, VERSION, APP_ICON
+from ...backend.exceptions import PackageNotFoundException, BlockedException
+
+# more GUI stuff
+from ..utils import Database, Config
+from ..queue import EmergeQueue
+from ..session import SESSION_VERSION, SessionException, OldSessionException, NewSessionException
+from ..wrapper import GtkTree, GtkConsole
+from ..exception_handling import GtkThread
+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)
+
+# even more GUI stuff
+from .basic import Window, Popup
+from .about import AboutWindow
+from .plugin import PluginWindow
+from .preference import PreferenceWindow
+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")
+
+ # the version combo
+ self.versionCombo = self.tree.get_widget("versionCombo")
+ self.build_version_combo()
+
+ # 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, gtk.gdk.color_parse("#FFFFFF"))
+
+ 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, cp, queue = None, version = None, doEmerge = True, instantChange = False, type = None):
+ """Updates the table to show the contents for the package.
+
+ @param cp: the selected package
+ @type cp: string (cp)
+ @param queue: emerge-queue (if None the emerge-buttons are disabled)
+ @type queue: EmergeQueue
+ @param version: if not None, specifies the version to select
+ @type version: string
+ @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.cp = cp # category/package
+ self.version = version # version - if not None this is used
+ self.queue = queue
+ self.doEmerge = doEmerge
+ self.instantChange = instantChange
+ self.type = type
+
+ # packages and installed packages
+ if not self.doEmerge:
+ self.instPackages = self.packages = system.find_packages("=%s-%s" % (cp, version), masked = True)
+ else:
+ self.packages = system.sort_package_list(system.find_packages(cp, masked = True))
+ self.instPackages = system.sort_package_list(system.find_installed_packages(cp, masked = True))
+
+ # version-combo-box
+ self.versionCombo.handler_block(self.versionCombo.changeHandler) # block change handler, because it would be called several times
+ self.versionCombo.get_model().clear()
+ self.fill_version_combo()
+ self.versionCombo.handler_unblock(self.versionCombo.changeHandler) # unblock handler again
+
+ if not self.queue or not self.doEmerge:
+ self.emergeBtn.set_sensitive(False)
+ self.unmergeBtn.set_sensitive(False)
+
+ # current status
+ self.cb_version_combo_changed()
+ self.vb.show_all()
+
+ def hide (self):
+ self.vb.hide_all()
+
+ def set_labels (self):
+ pkg = self.actual_package()
+
+ # name
+ self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cp())
+
+ # description
+ desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>")
+ self.descLabel.set_label(desc)
+
+ # overlay
+ if pkg.is_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):
+
+ deptree = self.actual_package().get_dependencies()
+ 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:
+ 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])
+
+ add (deptree, None)
+
+ def fill_use_list(self):
+
+ pkg = self.actual_package()
+ 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.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 build_version_combo (self):
+ store = gtk.ListStore(gtk.gdk.Pixbuf, str)
+
+ # build view
+ self.versionCombo.set_model(store)
+ col = gtk.TreeViewColumn("Versions")
+
+ # adding the pixbuf
+ cell = gtk.CellRendererPixbuf()
+ self.versionCombo.pack_start(cell, False)
+ self.versionCombo.add_attribute(cell, "pixbuf", 0)
+
+ # adding the package name
+ cell = gtk.CellRendererText()
+ self.versionCombo.pack_start(cell, True)
+ self.versionCombo.add_attribute(cell, "text", 1)
+
+ # connect
+ self.versionCombo.changeHandler = self.versionCombo.connect("changed", self.cb_version_combo_changed)
+
+ def fill_version_combo (self):
+
+ store = self.versionCombo.get_model()
+
+ # append versions
+ for vers, inst in ((x.get_version(), x.is_installed()) for x in self.packages):
+ if inst:
+ icon = self.main.instPixbuf
+ else:
+ icon = None
+ store.append([icon, vers])
+
+ # activate the first one
+ try:
+ best_version = ""
+ if self.version:
+ best_version = self.version
+ else:
+ best_version = system.find_best_match(self.packages[0].get_cp(), only_installed = (self.instPackages != [])).get_version()
+ for i in range(len(self.packages)):
+ if self.packages[i].get_version() == best_version:
+ self.versionCombo.set_active(i)
+ break
+ except AttributeError: # no package found
+ self.versionCombo.set_active(0)
+
+ def actual_package (self):
+ """Returns the actual selected package.
+
+ @returns: the actual selected package
+ @rtype: backend.Package"""
+
+ return self.packages[self.versionCombo.get_active()]
+
+ def _update_keywords (self, emerge, update = False):
+ if emerge:
+ type = "install" if not self.type else self.type
+ try:
+ try:
+ self.queue.append(self.actual_package().get_cpv(), type = type, update = update)
+ except PackageNotFoundException, e:
+ if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
+ self.queue.append(self.actual_package().get_cpv(), type = type, unmask = True, update = update)
+ except BlockedException, e:
+ blocked_dialog(e[0], e[1])
+ else:
+ try:
+ self.queue.append(self.actual_package().get_cpv(), type = "uninstall")
+ except PackageNotFoundException, e:
+ error(_("Package could not be found: %s"), e[0])
+ #masked_dialog(e[0])
+
+ def cb_version_combo_changed (self, *args):
+
+ pkg = self.actual_package()
+
+ # 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 self.actual_package().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.actual_package().remove_new_use_flags()
+ self.actual_package().remove_new_masked()
+ self.actual_package().remove_new_testing()
+ self.versionCombo.get_model().clear()
+ self.fill_version_combo()
+ self.cb_version_combo_changed()
+ 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.actual_package().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.actual_package().is_testing(use_keywords = False):
+ button.set_active(False)
+ return True
+
+ # re-set to testing status
+ if not self.actual_package().is_testing(use_keywords = True):
+ self.actual_package().set_testing(False)
+ button.set_label(_("Testing"))
+ button.set_active(True)
+ else: # disable testing
+ self.actual_package().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.actual_package()
+
+ 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.actual_package()
+
+ 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("gtk")
+ menus = plugin.get_plugin_queue().get_plugin_menus()
+ if menus:
+ self.tree.get_widget("pluginMenuItem").set_no_show_all(False)
+ pluginMenu = self.tree.get_widget("pluginMenu")
+
+ for m in 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))
+
+ # cat and pkg list
+ self.sortPkgListByName = True
+ self.catList = self.tree.get_widget("catList")
+ self.pkgList = self.tree.get_widget("pkgList")
+ self.build_cat_list()
+ self.build_pkg_list()
+
+ # search entry
+ self.searchEntry = self.tree.get_widget("searchEntry")
+
+ # queue list
+ 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", "GTK"), self.cfg.get("systemTabPos", "GTK")))))
+
+ # 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.queuePopup = Popup("queuePopup", self, self.__file__)
+ 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)
+
+ self.catList.get_selection().select_path(1)
+ self.pkgList.get_selection().select_path(0)
+
+ # 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, *args, **kwargs):
+ self.packageTable.update(*args, **kwargs)
+
+ def build_terminal (self):
+ """
+ Builds the terminal.
+ """
+
+ self.console.set_scrollback_lines(1024)
+ self.console.set_scroll_on_output(True)
+ self.console.set_font_from_string(self.cfg.get("consolefont", "GTK"))
+ 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)
+
+ def build_cat_list (self):
+ """
+ Builds the category list.
+ """
+
+ store = gtk.ListStore(str)
+
+ self.catList.set_model(store)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(_("Categories"), cell, text = 0)
+ self.catList.append_column(col)
+
+ self.fill_cat_store(store)
+ self.catList.get_selection().connect("changed", self.cb_cat_list_selection)
+
+ def fill_cat_store (self, store):
+ """
+ Fills the category store with data.
+
+ @param store: the store to fill
+ @type store: gtk.ListStore
+ """
+
+ cats = self.db.get_categories(installed = not self.showAll)
+
+ for p in cats:
+ store.append([p])
+
+ # 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, 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 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 refresh_stores (self):
+ """
+ Refreshes the category and package stores.
+ """
+ store = self.catList.get_model()
+ store.clear()
+ self.fill_cat_store(store)
+
+ store = self.pkgList.get_model()
+ store.clear()
+ try:
+ self.fill_pkg_store(store, self.selCatName)
+ except AttributeError: # 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,)
+
+ 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
+
+ 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 ():
+ stat_on = p.status >= p.STAT_ENABLED
+ hard_on = not p.get_option("disabled")
+
+ if stat_on != hard_on:
+ return int(stat_on)
+ else:
+ return ""
+ 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)
+
+ # set the simple ones :)
+ map(self.session.add_handler,[
+ ([("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),
+ ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection)
+ #([("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, self.queue, version = version)
+
+ 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:
+ title = _("Console")
+ else:
+ title = ("%s (%s)") % (_("Console"), title)
+
+ 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