Source

vlmaddon / view / message.py

#!/usr/bin/env python

#    This file is part of the vectorlinux multimedia bonus disc.
#
#    This file is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License v2 as published by
#    the Free Software Foundation.
#
#    This file is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this file.  If not, see <http://www.gnu.org/licenses/>.

import gtk
import threading
import time
import os
import sys
try:
    from .utils import vl
except:
    sys.path.append(os.path.join(os.getcwd(), '..'))
    from utils import vl


__author__ = 'Moises Henriquez'
__author_email__ = 'moc.liamg@xnl.E0M'[::-1]


def _(str):
    return str

class UninstallDialog(gtk.Dialog):
    """ Dialog shown when uninstalling package
    Arguments:
    pkglist - a list of packages to be removed.
    parent - A gtk.Window object. Defaults to None"""
    def __init__(self, parent = None, pkglist = [],
            categories = None, view = None):
        gtk.Dialog.__init__(self, parent = parent)
        self.pkglist = pkglist
        self.set_title(_('Uninstalling Packages'))
        if parent is None:
            self.set_position(gtk.WIN_POS_CENTER)
        content = self.get_content_area()
        action = self.get_action_area()
        banner = gtk.Label(
                _('Please wait while packages are being uninstalled'))
        self.banner = banner
        blank = gtk.Label()
        self.progress = gtk.ProgressBar()
        self.status = gtk.Label()

        for item in (banner, blank, self.status, self.progress):
            content.pack_start(item, False, False, 2)

        btnCancel = gtk.Button(stock = gtk.STOCK_CANCEL)
        btnCancel.connect('clicked', self.exit)
        self.btnCancel = btnCancel

        action.pack_start(btnCancel, False, False, 2)
        self.stopbar = False
        self.monitor = threading.Thread(target = self._pulse_bar)
        self.resize(400, 150)
        self.current = ""
        self.failed = []

        self._icurrent = 0.0
        self.pkgmanager = vl.PackageManager()
        self._catpanel = categories
        self._view = view

        self.show_all()

    def exit(self, widget = None):
        self.stopbar = True
        self._catpanel.refresh_view()
        return self.destroy()

    def _pulse_bar(self):
        val = 0.0
        while not self.stopbar:
            time.sleep(0.1)
            gtk.gdk.threads_enter()
            self.progress.pulse()
            gtk.gdk.threads_leave()

        # After the bar is done moving.

        time.sleep(0.5)
        self._show_postproc_labels()

    def _show_postproc_labels(self):
        if len(self.failed) > 0:
            state = _('WARNING: Some packages failed to uninstall')
        else:
            state = _('The selected packages have been successfully removed')
        gtk.gdk.threads_enter()
        self.status.set_property('label', state)
        self.progress.set_fraction(1.0)
        gtk.gdk.threads_leave()

    def change_cancel_button(self):
        wid = self.btnCancel
        wid.set_use_stock(False)
        img = gtk.Image()
        img.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON)
        wid.set_property('label', _('Close'))
        wid.set_image(img)
        return

    def run(self):
        """ Start our custom run process rather than the original"""
        return self._do_run()

    def _do_run(self):
        """ Actual package removal process"""
        self.stopbar = False
        self.monitor.start()
        removal = threading.Thread(
                target= self._do_remove_pkgs,
                args  = (self.pkglist,))
        removal.start()

    def _do_remove_pkgs(self, pkgs):
        """ This is the removal function. Not to be called
        directly, but rather to be parsed as a target to
        a threading object"""
        self.failed = []

        # Update cache first.
        self._icurrent = -1.0
        gtk.gdk.threads_enter()
        self.status.set_property('label',
                _('Updating package cache')
                )
        gtk.gdk.threads_leave()
        i = self.pkgmanager.update_cache()
        if i[1] > 0:
            #FIXME: cache failed to update... error out here???
            pass

        self._icurrent = 0.0
        for pkg in pkgs:
            self.current = pkg
            gtk.gdk.threads_enter()
            self.status.set_property('label','%s %s'% (
                _('Uninstalling'), pkg))
            gtk.gdk.threads_leave()

            ret = self.pkgmanager.remove_package(pkg)
            # ==== error check ===
            if ret[1] > 0:
                if len(ret[0][1].strip()) == 0:
                    msg = ret[0][0].strip()
                else:
                    msg = ret[0][1].strip()
                self.failed.append((pkg, msg))

            self._icurrent += 1

        # When done, stop the monitor thread
        self.stopbar = True

        # Check if any packages failed to uninstall, and
        # present the appropriate dialog message.
        if len(self.failed) > 0:
            ## FIXME: Add the correct failed dialog with
            ## list.
            dia = FailedPackagesDialog(list_reason = self.failed)
            dia.banner.set_property('label',
                    _('Some packages failed to uninstall.') + ' ' +\
                    _('See details below.'))


#            dia = Error(
#                    text = _('Some packages failed to uninstall.'))
        else:
            dia = Info(
                    text = _('The selected packages have been uninstalled'))
        gtk.gdk.threads_enter()
        if dia.run():
            dia.destroy()
        self.change_cancel_button()
        gtk.gdk.threads_leave()

class InstallDialog(UninstallDialog):
    """ A dialog shown while installing packages
    Arguments:
    parent = a gtk.Window object, defaults to None
    pkglist = a list of packages to be installed"""
    def __init__(self, parent = None, pkglist = [], slaptconfig = None, 
            categories = None, view = None):
        UninstallDialog.__init__(self, parent = parent, pkglist = pkglist,
                categories = categories,
                view = view)
        self.slaptconfig = slaptconfig
        self.pkglist = pkglist
        self.set_title(_('Install Packages'))
        self.banner.set_property('label',
                _('Please wait while the following operations are completed'))
        if slaptconfig is None:
            self.slaptconfig = '/etc/slapt-getrc'
        else:
            self.slaptconfig = slaptconfig
#        self.slaptconfig = '/etc/slapt-getrc'

        self.pkgmanager = vl.PackageManager(configfile = self.slaptconfig)

    def _show_postproc_labels(self):
        if len(self.failed) > 0:
            state = _('WARNING: Some packages failed to install')
        else:
            state = _('All selected packages were successfully installed')
        gtk.gdk.threads_enter()
        self.status.set_property('label', state)
        self.banner.set_property('label',
                _('All operations complete'))
        self.progress.set_fraction(1.0)
        self.change_cancel_button()
        gtk.gdk.threads_leave()

    def _do_run(self):
        """ Fork off the package installation process"""
        self.stopbar = False
        self.monitor.start()
        inst = threading.Thread(
                target = self._do_install_pkgs,
                args = (self.pkglist,))
        inst.start()

    def _do_install_pkgs(self, pkgs):
        """ This method should be started in a thread ONLY"""
        # Update cache first.
        self._icurrent = -1.0
        gtk.gdk.threads_enter()
        self.status.set_property('label', _('Updating package cache'))
        gtk.gdk.threads_leave()
        i = self.pkgmanager.update_cache()
        if i[1] > 0:
            # FIXME: Cache failed to update... Error out or continue?
            pass
        self._icurrent = 0.0
        for pkg in pkgs:
            self.current = pkg
            gtk.gdk.threads_enter()
            self.status.set_property('label','%s %s'% (
                _('Installing'), pkg))
            gtk.gdk.threads_leave()

            ret = self.pkgmanager.install_package(pkg)

            # error check -----
            # WARNING: slapt-get returns 0 even when the package did not
            # install due to missing deps.
            if ret[1] > 0 or 'unmet dependencies' in ret[0][1]:
                if len(ret[0][1].strip()) == 0:
                    msg = ret[0][0].strip()
                else:
                    msg = ret[0][1].strip()
                self.failed.append((pkg, msg.strip()))
            self._icurrent += 1
        self.stopbar = True
        self._show_postproc_labels()
        if len(self.failed) > 0:
            dia = FailedPackagesDialog(self.failed)
            gtk.gdk.threads_enter()
            if dia.run():
                dia.destroy()
            self.change_cancel_button()
            gtk.gdk.threads_leave()

class FailedPackagesDialog(gtk.Dialog):
    """Dialog used to show that some packages failed to install
    """
    def __init__(self, list_reason = []):
        gtk.Dialog.__init__(self)
        self.set_title(_('Errors during installation'))
        carea = self.get_content_area()
        aarea = self.get_action_area()
        bannerc = gtk.HBox()
        img = gtk.Image()
        img.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG)
        bannerc.pack_start(img, False, False, 8)
        banner = gtk.Label()
        banner.set_property('label',
                _('The following packages failed to install.') + '\n\n' + \
                        _('Please report this error to the developer'))
        self.banner = banner
        bannerc.pack_start(banner, False, False, 8)
        carea.pack_start(bannerc, False, False, 0)
        scv = gtk.ScrolledWindow()
#        carea.pack_start(scv, True, True, 0)
        store = gtk.ListStore(str, str)
        for item in list_reason:
            store.append([item[0], item[1].strip().replace('\\n ','\n')])
        tree = gtk.TreeView(model = store)
        cr0 = gtk.CellRendererText()
        cr1 = gtk.CellRendererText()
        col0 = gtk.TreeViewColumn('Application', cr0)
        col0.add_attribute(cr0, 'markup', 0)
        col1 = gtk.TreeViewColumn('Returned Error', cr1)
        col1.add_attribute(cr1, 'markup', 1)
        tree = gtk.TreeView()
        tree.set_model(store)
        tree.set_headers_visible(True)
        scv.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        tree.append_column(col0)
        tree.append_column(col1)
        scv.add(tree)
        carea.pack_start(scv, True, True, 0)

        btClose = gtk.Button(stock = gtk.STOCK_CLOSE)
        btClose.connect('clicked', self._close)
        aarea.pack_start(btClose, False, False, 0)
        self.resize(480, 300)

        self.show_all()

    def _close(self, widget=None):
        self.destroy()

def Error(parent = None, text = None, title = 'Error'):
    """Generic error message dialog"""
    dia = gtk.MessageDialog(
            parent = parent,
            type = gtk.MESSAGE_ERROR,
            buttons = gtk.BUTTONS_OK,
            )
    dia.set_markup(text)
    dia.set_title(title)
    return dia

def Info(parent = None, text = None, title = 'Info'):
    dia = gtk.MessageDialog(
            parent = parent,
            type = gtk.MESSAGE_INFO,
            buttons = gtk.BUTTONS_OK,
            )
    dia.set_markup(text)
    dia.set_title(title)
    return dia