home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeViewGtk.py < prev    next >
Encoding:
Python Source  |  2009-04-27  |  26.8 KB  |  680 lines

  1. # DistUpgradeViewGtk.py 
  2. #  
  3. #  Copyright (c) 2004-2006 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19. import pygtk
  20. pygtk.require('2.0')
  21. import gtk
  22. import gtk.gdk
  23. import gtk.glade
  24. import vte
  25. import gobject
  26. import pango
  27. import sys
  28. import logging
  29. import time
  30. import subprocess
  31.  
  32. import apt
  33. import apt_pkg
  34. import os
  35.  
  36. from DistUpgradeApport import *
  37.  
  38. from DistUpgradeView import DistUpgradeView, FuzzyTimeToStr, InstallProgress, FetchProgress
  39. from SimpleGladeApp import SimpleGladeApp, bindtextdomain
  40.  
  41. import gettext
  42. from DistUpgradeGettext import gettext as _
  43.  
  44. def utf8(str):
  45.   return unicode(str, 'latin1').encode('utf-8')
  46.  
  47.  
  48. class GtkCdromProgressAdapter(apt.progress.CdromProgress):
  49.     """ Report the cdrom add progress
  50.         Subclass this class to implement cdrom add progress reporting
  51.     """
  52.     def __init__(self, parent):
  53.         self.status = parent.label_status
  54.         self.progress = parent.progressbar_cache
  55.         self.parent = parent
  56.     def update(self, text, step):
  57.         """ update is called regularly so that the gui can be redrawn """
  58.         if text:
  59.           self.status.set_text(text)
  60.         self.progress.set_fraction(step/float(self.totalSteps))
  61.         while gtk.events_pending():
  62.           gtk.main_iteration()
  63.     def askCdromName(self):
  64.         return (False, "")
  65.     def changeCdrom(self):
  66.         return False
  67.  
  68. class GtkOpProgress(apt.progress.OpProgress):
  69.   def __init__(self, progressbar):
  70.       self.progressbar = progressbar
  71.       #self.progressbar.set_pulse_step(0.01)
  72.       #self.progressbar.pulse()
  73.  
  74.   def update(self, percent):
  75.       #if percent > 99:
  76.       #    self.progressbar.set_fraction(1)
  77.       #else:
  78.       #    self.progressbar.pulse()
  79.       self.progressbar.set_fraction(percent/100.0)
  80.       while gtk.events_pending():
  81.           gtk.main_iteration()
  82.  
  83.   def done(self):
  84.       self.progressbar.set_text(" ")
  85.  
  86.  
  87. class GtkFetchProgressAdapter(FetchProgress):
  88.     # FIXME: we really should have some sort of "we are at step"
  89.     # xy in the gui
  90.     # FIXME2: we need to thing about mediaCheck here too
  91.     def __init__(self, parent):
  92.         FetchProgress.__init__(self)
  93.         # if this is set to false the download will cancel
  94.         self.status = parent.label_status
  95.         self.progress = parent.progressbar_cache
  96.         self.parent = parent
  97.         self.canceled = False
  98.         self.button_cancel = parent.button_fetch_cancel
  99.         self.button_cancel.connect('clicked', self.cancelClicked)
  100.     def cancelClicked(self, widget):
  101.         logging.debug("cancelClicked")
  102.         self.canceled = True
  103.     def mediaChange(self, medium, drive):
  104.         #print "mediaChange %s %s" % (medium, drive)
  105.         msg = _("Please insert '%s' into the drive '%s'") % (medium,drive)
  106.         dialog = gtk.MessageDialog(parent=self.parent.window_main,
  107.                                    flags=gtk.DIALOG_MODAL,
  108.                                    type=gtk.MESSAGE_QUESTION,
  109.                                    buttons=gtk.BUTTONS_OK_CANCEL)
  110.         dialog.set_markup(msg)
  111.         res = dialog.run()
  112.         dialog.destroy()
  113.         if res == gtk.RESPONSE_OK:
  114.             return True
  115.         return False
  116.     def start(self):
  117.         #logging.debug("start")
  118.         self.progress.set_fraction(0)
  119.         self.status.show()
  120.         self.button_cancel.show()
  121.     def stop(self):
  122.         #logging.debug("stop")
  123.         self.progress.set_text(" ")
  124.         self.status.set_text(_("Fetching is complete"))
  125.         self.button_cancel.hide()
  126.     def pulse(self):
  127.         # FIXME: move the status_str and progress_str into python-apt
  128.         # (python-apt need i18n first for this)
  129.         FetchProgress.pulse(self)
  130.         self.progress.set_fraction(self.percent/100.0)
  131.         currentItem = self.currentItems + 1
  132.         if currentItem > self.totalItems:
  133.             currentItem = self.totalItems
  134.  
  135.         if self.currentCPS > 0:
  136.             self.status.set_text(_("Fetching file %li of %li at %sB/s") % (currentItem, self.totalItems, apt_pkg.SizeToStr(self.currentCPS)))
  137.             self.progress.set_text(_("About %s remaining") % FuzzyTimeToStr(self.eta))
  138.         else:
  139.             self.status.set_text(_("Fetching file %li of %li") % (currentItem, self.totalItems))
  140.             self.progress.set_text("  ")
  141.  
  142.         while gtk.events_pending():
  143.             gtk.main_iteration()
  144.         return (not self.canceled)
  145.  
  146. class GtkInstallProgressAdapter(InstallProgress):
  147.     # timeout with no status change when the terminal is expanded
  148.     # automatically
  149.     TIMEOUT_TERMINAL_ACTIVITY = 240
  150.     
  151.     def __init__(self,parent):
  152.         InstallProgress.__init__(self)
  153.         self._cache = None
  154.         self.label_status = parent.label_status
  155.         self.progress = parent.progressbar_cache
  156.         self.expander = parent.expander_terminal
  157.         self.term = parent._term
  158.         self.parent = parent
  159.         # setup the child waiting
  160.         reaper = vte.reaper_get()
  161.         reaper.connect("child-exited", self.child_exited)
  162.         # some options for dpkg to make it die less easily
  163.         apt_pkg.Config.Set("DPkg::StopOnError","False")
  164.  
  165.     def startUpdate(self):
  166.         InstallProgress.startUpdate(self)
  167.         self.finished = False
  168.         # FIXME: add support for the timeout
  169.         # of the terminal (to display something useful then)
  170.         # -> longer term, move this code into python-apt 
  171.         self.label_status.set_text(_("Applying changes"))
  172.         self.progress.set_fraction(0.0)
  173.         self.progress.set_text(" ")
  174.         self.expander.set_sensitive(True)
  175.         self.term.show()
  176.         # if no libgnome2-perl is installed show the terminal
  177.         frontend= os.environ.get("DEBIAN_FRONTEND") or "gnome"
  178.         if frontend == "gnome" and self._cache:
  179.           if (not self._cache.has_key("libgnome2-perl") or 
  180.               not self._cache["libgnome2-perl"].isInstalled):
  181.             frontend = "dialog"
  182.             self.expander.set_expanded(True)
  183.         self.env = ["VTE_PTY_KEEP_FD=%s"% self.writefd,
  184.                     "APT_LISTCHANGES_FRONTEND=none"]
  185.         if not os.environ.has_key("DEBIAN_FRONTEND"):
  186.           self.env.append("DEBIAN_FRONTEND=%s" % frontend)
  187.         # do a bit of time-keeping
  188.         self.start_time = 0.0
  189.         self.time_ui = 0.0
  190.         self.last_activity = 0.0
  191.         
  192.     def error(self, pkg, errormsg):
  193.         InstallProgress.error(self, pkg, errormsg)
  194.         logging.error("got an error from dpkg for pkg: '%s': '%s'" % (pkg, errormsg))
  195.     # we do not report followup errors from earlier failures
  196.         if gettext.dgettext('dpkg', "dependency problems - leaving unconfigured") in errormsg:
  197.       return False
  198.  
  199.         #self.expander_terminal.set_expanded(True)
  200.         self.parent.dialog_error.set_transient_for(self.parent.window_main)
  201.         summary = _("Could not install '%s'") % pkg
  202.         msg = _("The upgrade will continue but the '%s' package may be "
  203.                 "in a not working state. Please consider submitting a "
  204.                 "bug report about it.") % pkg
  205.         markup="<big><b>%s</b></big>\n\n%s" % (summary, msg)
  206.         self.parent.dialog_error.realize()
  207.         self.parent.dialog_error.window.set_functions(gtk.gdk.FUNC_MOVE)
  208.         self.parent.label_error.set_markup(markup)
  209.         self.parent.textview_error.get_buffer().set_text(utf8(errormsg))
  210.         self.parent.scroll_error.show()
  211.         self.parent.dialog_error.run()
  212.         self.parent.dialog_error.hide()
  213.  
  214.     def conffile(self, current, new):
  215.         logging.debug("got a conffile-prompt from dpkg for file: '%s'" % current)
  216.         start = time.time()
  217.         #self.expander.set_expanded(True)
  218.         prim = _("Replace the customized configuration file\n'%s'?") % current
  219.         sec = _("You will lose any changes you have made to this "
  220.                 "configuration file if you choose to replace it with "
  221.                 "a newer version.")
  222.         markup = "<span weight=\"bold\" size=\"larger\">%s </span> \n\n%s" % (prim, sec)
  223.         self.parent.label_conffile.set_markup(markup)
  224.         self.parent.dialog_conffile.set_transient_for(self.parent.window_main)
  225.  
  226.         # now get the diff
  227.         if os.path.exists("/usr/bin/diff"):
  228.           cmd = ["/usr/bin/diff", "-u", current, new]
  229.           diff = utf8(subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0])
  230.           self.parent.textview_conffile.get_buffer().set_text(diff)
  231.         else:
  232.           self.parent.textview_conffile.get_buffer().set_text(_("The 'diff' command was not found"))
  233.         res = self.parent.dialog_conffile.run()
  234.         self.parent.dialog_conffile.hide()
  235.         self.time_ui += time.time() - start
  236.         # if replace, send this to the terminal
  237.         if res == gtk.RESPONSE_YES:
  238.           self.term.feed_child("y\n")
  239.         else:
  240.           self.term.feed_child("n\n")
  241.         
  242.     def fork(self):
  243.         pid = self.term.forkpty(envv=self.env)
  244.         if pid == 0:
  245.           # WORKAROUND for broken feisty vte where envv does not work)
  246.           for env in self.env:
  247.             (key, value) = env.split("=")
  248.             os.environ[key] = value
  249.           # HACK to work around bug in python/vte and unregister the logging
  250.           #      atexit func in the child
  251.           sys.exitfunc = lambda: True
  252.         return pid
  253.  
  254.     def statusChange(self, pkg, percent, status):
  255.         # start the timer when the first package changes its status
  256.         if self.start_time == 0.0:
  257.           #print "setting start time to %s" % self.start_time
  258.           self.start_time = time.time()
  259.         self.progress.set_fraction(float(percent)/100.0)
  260.         self.label_status.set_text(status.strip())
  261.         # start showing when we gathered some data
  262.         if percent > 1.0:
  263.           self.last_activity = time.time()
  264.           self.activity_timeout_reported = False
  265.           delta = self.last_activity - self.start_time
  266.           # time wasted in conffile questions (or other ui activity)
  267.           delta -= self.time_ui
  268.           time_per_percent = (float(delta)/percent)
  269.           eta = (100.0 - percent) * time_per_percent
  270.           # only show if we have some sensible data (60sec < eta < 2days)
  271.           if eta > 61.0 and eta < (60*60*24*2):
  272.             self.progress.set_text(_("About %s remaining") % FuzzyTimeToStr(eta))
  273.           else:
  274.             self.progress.set_text(" ")
  275.  
  276.     def child_exited(self, term, pid, status):
  277.         self.apt_status = os.WEXITSTATUS(status)
  278.         self.finished = True
  279.  
  280.     def waitChild(self):
  281.         while not self.finished:
  282.             self.updateInterface()
  283.         return self.apt_status
  284.  
  285.     def finishUpdate(self):
  286.         self.label_status.set_text("")
  287.     
  288.     def updateInterface(self):
  289.         try:
  290.           InstallProgress.updateInterface(self)
  291.         except ValueError, e:
  292.           logging.error("got ValueError from InstallProgress.updateInterface. Line was '%s' (%s)" % (self.read, e))
  293.           # reset self.read so that it can continue reading and does not loop
  294.       self.read = ""
  295.         # check if we haven't started yet with packages, pulse then
  296.         if self.start_time == 0.0:
  297.           self.progress.pulse()
  298.           time.sleep(0.2)
  299.         # check about terminal activity
  300.         if self.last_activity > 0 and \
  301.            (self.last_activity + self.TIMEOUT_TERMINAL_ACTIVITY) < time.time():
  302.           if not self.activity_timeout_reported:
  303.             logging.warning("no activity on terminal for %s seconds (%s)" % (self.TIMEOUT_TERMINAL_ACTIVITY, self.label_status.get_text()))
  304.             self.activity_timeout_reported = True
  305.           self.parent.expander_terminal.set_expanded(True)
  306.         while gtk.events_pending():
  307.             gtk.main_iteration()
  308.     time.sleep(0.005)
  309.  
  310. class DistUpgradeVteTerminal(object):
  311.   def __init__(self, parent, term):
  312.     self.term = term
  313.     self.parent = parent
  314.   def call(self, cmd, hidden=False):
  315.     def wait_for_child(widget):
  316.       #print "wait for child finished"
  317.       self.finished=True
  318.     self.term.show()
  319.     self.term.connect("child-exited", wait_for_child)
  320.     self.parent.expander_terminal.set_sensitive(True)
  321.     if hidden==False:
  322.       self.parent.expander_terminal.set_expanded(True)
  323.     self.finished = False
  324.     pid = self.term.fork_command(command=cmd[0],argv=cmd)
  325.     if pid < 0:
  326.       # error
  327.       return 
  328.     while not self.finished:
  329.       while gtk.events_pending():
  330.         gtk.main_iteration()
  331.       time.sleep(0.1)
  332.     del self.finished
  333.  
  334. class DistUpgradeViewGtk(DistUpgradeView,SimpleGladeApp):
  335.     " gtk frontend of the distUpgrade tool "
  336.     def __init__(self, datadir=None, logdir=None):
  337.         self.logdir = logdir
  338.         if not datadir:
  339.           localedir=os.path.join(os.getcwd(),"mo")
  340.           gladedir=os.getcwd()
  341.         else:
  342.           localedir="/usr/share/locale/update-manager"
  343.           gladedir=os.path.join(datadir, "glade")
  344.  
  345.         # FIXME: i18n must be somewhere relative do this dir
  346.         try:
  347.           bindtextdomain("update-manager", localedir)
  348.           gettext.textdomain("update-manager")
  349.         except Exception, e:
  350.           logging.warning("Error setting locales (%s)" % e)
  351.         
  352.         icons = gtk.icon_theme_get_default()
  353.         try:
  354.           gtk.window_set_default_icon(icons.load_icon("update-manager", 32, 0))
  355.         except gobject.GError, e:
  356.           logging.debug("error setting default icon, ignoring (%s)" % e)
  357.           pass
  358.         SimpleGladeApp.__init__(self, gladedir+"/DistUpgrade.glade",
  359.                                 None, domain="update-manager")
  360.         self.prev_step = 0 # keep a record of the latest step
  361.         # we don't use this currently
  362.         #self.window_main.set_keep_above(True)
  363.         self.icontheme = gtk.icon_theme_get_default()
  364.         # we keep a reference pngloader around so that its in memory
  365.         # -> this avoid the issue that during the dapper->edgy upgrade
  366.         #    the loaders move from /usr/lib/gtk/2.4.0/loaders to 2.10.0
  367.         self.pngloader = gtk.gdk.PixbufLoader("png")
  368.         try:
  369.           self.svgloader = gtk.gdk.PixbufLoader("svg")
  370.           self.svgloader.close()
  371.         except gobject.GError, e:
  372.           logging.debug("svg pixbuf loader failed (%s)" % e)
  373.           pass
  374.         
  375.         self.window_main.realize()
  376.         self.window_main.window.set_functions(gtk.gdk.FUNC_MOVE)
  377.         self._opCacheProgress = GtkOpProgress(self.progressbar_cache)
  378.         self._fetchProgress = GtkFetchProgressAdapter(self)
  379.         self._cdromProgress = GtkCdromProgressAdapter(self)
  380.         self._installProgress = GtkInstallProgressAdapter(self)
  381.         # details dialog
  382.         self.details_list = gtk.ListStore(gobject.TYPE_STRING)
  383.         column = gtk.TreeViewColumn("")
  384.         render = gtk.CellRendererText()
  385.         column.pack_start(render, True)
  386.         column.add_attribute(render, "markup", 0)
  387.         self.treeview_details.append_column(column)
  388.         self.treeview_details.set_model(self.details_list)
  389.         self.vscrollbar_terminal.set_adjustment(self._term.get_adjustment())
  390.         # work around bug in VteTerminal here
  391.         self._term.realize()
  392.  
  393.         # Use italic style in the status labels
  394.         attrlist=pango.AttrList()
  395.         #attr = pango.AttrStyle(pango.STYLE_ITALIC, 0, -1)
  396.         attr = pango.AttrScale(pango.SCALE_SMALL, 0, -1)
  397.         attrlist.insert(attr)
  398.         self.label_status.set_property("attributes", attrlist)
  399.         # reasonable fault handler
  400.         sys.excepthook = self._handleException
  401.  
  402.     def _handleException(self, type, value, tb):
  403.       # we handle the exception here, hand it to apport and run the
  404.       # apport gui manually after it because we kill u-m during the upgrade
  405.       # to prevent it from poping up for reboot notifications or FF restart
  406.       # notifications or somesuch
  407.       import traceback
  408.       lines = traceback.format_exception(type, value, tb)
  409.       logging.error("not handled expection:\n%s" % "\n".join(lines))
  410.       # we can't be sure that apport will run in the middle of a upgrade
  411.       # so we still show a error message here
  412.       apport_crash(type, value, tb)
  413.       if not run_apport():
  414.         self.error(_("A fatal error occurred"),
  415.                    _("Please report this as a bug (if you haven't already) and include the "
  416.                      "files /var/log/dist-upgrade/main.log and "
  417.                      "/var/log/dist-upgrade/apt.log "
  418.                      "in your report. The upgrade is now aborted.\n"
  419.                      "Your original sources.list was saved in "
  420.                      "/etc/apt/sources.list.distUpgrade."),
  421.                    "\n".join(lines))
  422.       sys.exit(1)
  423.  
  424.     def getTerminal(self):
  425.         return DistUpgradeVteTerminal(self, self._term)
  426.  
  427.     def _key_press_handler(self, widget, keyev):
  428.       # user pressed ctrl-c
  429.       if len(keyev.string) == 1 and ord(keyev.string) == 3:
  430.         summary = _("Ctrl-c pressed")
  431.         msg = _("This will abort the operation and may leave the system "
  432.                 "in a broken state. Are you sure you want to do that?")
  433.         res = self.askYesNoQuestion(summary, msg)
  434.         logging.warning("ctrl-c press detected, user decided to pass it "
  435.                         "on: %s", res)        
  436.         return not res
  437.       return False
  438.  
  439.     def create_terminal(self, arg1,arg2,arg3,arg4):
  440.         " helper to create a vte terminal "
  441.         self._term = vte.Terminal()
  442.         self._term.connect("key-press-event", self._key_press_handler)
  443.         self._term.set_font_from_string("monospace 10")
  444.         self._term.connect("contents-changed", self._term_content_changed)
  445.         self._terminal_lines = []
  446.         try:
  447.           self._terminal_log = open(os.path.join(self.logdir,"term.log"),"w")
  448.         except Exception, e:
  449.           # if something goes wrong (permission denied etc), use stdout
  450.           self._terminal_log = sys.stdout
  451.         return self._term
  452.  
  453.     def _term_content_changed(self, term):
  454.         " called when the *visible* part of the terminal changes "
  455.         # get the current visible text, 
  456.         current_text = self._term.get_text(lambda a,b,c,d: True)
  457.         # see what we have currently and only print stuff that wasn't
  458.         # visible last time
  459.         new_lines = []
  460.         for line in current_text.split("\n"):
  461.           new_lines.append(line)
  462.           if not line in self._terminal_lines:
  463.             self._terminal_log.write(line+"\n")
  464.             self._terminal_log.flush()
  465.         self._terminal_lines = new_lines
  466.     def getFetchProgress(self):
  467.         return self._fetchProgress
  468.     def getInstallProgress(self, cache):
  469.         self._installProgress._cache = cache
  470.         return self._installProgress
  471.     def getOpCacheProgress(self):
  472.         return self._opCacheProgress
  473.     def getCdromProgress(self):
  474.         return self._cdromProgress
  475.     def updateStatus(self, msg):
  476.         self.label_status.set_text("%s" % msg)
  477.     def hideStep(self, step):
  478.         image = getattr(self,"image_step%i" % step)
  479.         label = getattr(self,"label_step%i" % step)
  480.         arrow = getattr(self,"arrow_step%i" % step)
  481.         image.hide()
  482.         label.hide()
  483.     def showStep(self, step):
  484.         image = getattr(self,"image_step%i" % step)
  485.         label = getattr(self,"label_step%i" % step)
  486.         image.show()
  487.         label.show()
  488.     def abort(self):
  489.         size = gtk.ICON_SIZE_MENU
  490.         step = self.prev_step
  491.         if step > 0:
  492.             image = getattr(self,"image_step%i" % step)
  493.             arrow = getattr(self,"arrow_step%i" % step)
  494.             image.set_from_stock(gtk.STOCK_CANCEL, size)
  495.             image.show()
  496.             arrow.hide()
  497.     def setStep(self, step):
  498.         if self.icontheme.rescan_if_needed():
  499.           logging.debug("icon theme changed, re-reading")
  500.         # first update the "previous" step as completed
  501.         size = gtk.ICON_SIZE_MENU
  502.         attrlist=pango.AttrList()
  503.         if self.prev_step:
  504.             image = getattr(self,"image_step%i" % self.prev_step)
  505.             label = getattr(self,"label_step%i" % self.prev_step)
  506.             arrow = getattr(self,"arrow_step%i" % self.prev_step)
  507.             label.set_property("attributes",attrlist)
  508.             image.set_from_stock(gtk.STOCK_APPLY, size)
  509.             image.show()
  510.             arrow.hide()
  511.         self.prev_step = step
  512.         # show the an arrow for the current step and make the label bold
  513.         image = getattr(self,"image_step%i" % step)
  514.         label = getattr(self,"label_step%i" % step)
  515.         arrow = getattr(self,"arrow_step%i" % step)
  516.         # check if that step was not hidden with hideStep()
  517.         if not label.get_property("visible"):
  518.           return
  519.         arrow.show()
  520.         image.hide()
  521.         attr = pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)
  522.         attrlist.insert(attr)
  523.         label.set_property("attributes",attrlist)
  524.  
  525.     def information(self, summary, msg, extended_msg=None):
  526.       self.dialog_information.set_transient_for(self.window_main)
  527.       msg = "<big><b>%s</b></big>\n\n%s" % (summary,msg)
  528.       self.label_information.set_markup(msg)
  529.       if extended_msg != None:
  530.         buffer = self.textview_information.get_buffer()
  531.         buffer.set_text(extended_msg)
  532.         self.scroll_information.show()
  533.       else:
  534.         self.scroll_information.hide()
  535.       self.dialog_information.realize()
  536.       self.dialog_information.window.set_functions(gtk.gdk.FUNC_MOVE)
  537.       self.dialog_information.run()
  538.       self.dialog_information.hide()
  539.       while gtk.events_pending():
  540.         gtk.main_iteration()
  541.  
  542.     def error(self, summary, msg, extended_msg=None):
  543.         self.dialog_error.set_transient_for(self.window_main)
  544.         #self.expander_terminal.set_expanded(True)
  545.         msg="<big><b>%s</b></big>\n\n%s" % (summary, msg)
  546.         self.label_error.set_markup(msg)
  547.         if extended_msg != None:
  548.             buffer = self.textview_error.get_buffer()
  549.             buffer.set_text(extended_msg)
  550.             self.scroll_error.show()
  551.         else:
  552.             self.scroll_error.hide()
  553.         self.dialog_error.realize()
  554.         self.dialog_error.window.set_functions(gtk.gdk.FUNC_MOVE)
  555.         self.dialog_error.run()
  556.         self.dialog_error.hide()
  557.         return False
  558.  
  559.     def confirmChanges(self, summary, changes, downloadSize, 
  560.                        actions=None, removal_bold=True):
  561.         # FIXME: add a whitelist here for packages that we expect to be
  562.         # removed (how to calc this automatically?)
  563.         if not DistUpgradeView.confirmChanges(self, summary, changes, downloadSize):
  564.           return False
  565.         # append warning
  566.         self.confirmChangesMessage +=  "\n\n<b>%s</b>" %  \
  567.             _("To prevent data loss close all open "
  568.               "applications and documents.")
  569.  
  570.         if actions != None:
  571.             self.button_cancel_changes.set_use_stock(False)
  572.             self.button_cancel_changes.set_use_underline(True)
  573.             self.button_cancel_changes.set_label(actions[0])
  574.             self.button_confirm_changes.set_label(actions[1])
  575.  
  576.         self.label_summary.set_markup("<big><b>%s</b></big>" % summary)
  577.         self.label_changes.set_markup(self.confirmChangesMessage)
  578.         # fill in the details
  579.         self.details_list.clear()
  580.         for dg in self.toDowngrade:
  581.             self.details_list.append([_("<b>Downgrade %s</b>") % dg])
  582.         for rm in self.toRemove:
  583.             s = _("Remove %s") % rm
  584.             if removal_bold:
  585.               s = "<b>%s</b>" % s
  586.             self.details_list.append([s])
  587.         for inst in self.toInstall:
  588.             self.details_list.append([_("Install %s") % inst])
  589.         for up in self.toUpgrade:
  590.             self.details_list.append([_("Upgrade %s") % up])
  591.         self.dialog_changes.set_transient_for(self.window_main)
  592.         # work around problem that scroll_to() does not 
  593.         # work when it is not realized
  594.         self.dialog_changes.realize()
  595.         self.treeview_details.realize()
  596.         self.treeview_details.set_cursor((0,))
  597.         self.treeview_details.scroll_to_point(0,0)
  598.         self.dialog_changes.window.set_functions(gtk.gdk.FUNC_MOVE)
  599.         res = self.dialog_changes.run()
  600.         self.dialog_changes.hide()
  601.         if res == gtk.RESPONSE_YES:
  602.             return True
  603.         return False
  604.  
  605.     def askYesNoQuestion(self, summary, msg, default='No'):
  606.         msg = "<big><b>%s</b></big>\n\n%s" % (summary,msg)
  607.         dialog = gtk.MessageDialog(parent=self.window_main,
  608.                                    flags=gtk.DIALOG_MODAL,
  609.                                    type=gtk.MESSAGE_QUESTION,
  610.                                    buttons=gtk.BUTTONS_YES_NO)
  611.         if default == 'No':
  612.           dialog.set_default_response(gtk.RESPONSE_NO)
  613.         else:
  614.           dialog.set_default_response(gtk.RESPONSE_YES)
  615.         dialog.set_markup(msg)
  616.         res = dialog.run()
  617.         dialog.destroy()
  618.         if res == gtk.RESPONSE_YES:
  619.             return True
  620.         return False
  621.     
  622.     def confirmRestart(self):
  623.         self.dialog_restart.set_transient_for(self.window_main)
  624.         self.dialog_restart.realize()
  625.         self.dialog_restart.window.set_functions(gtk.gdk.FUNC_MOVE)
  626.         res = self.dialog_restart.run()
  627.         self.dialog_restart.hide()
  628.         if res == gtk.RESPONSE_YES:
  629.             return True
  630.         return False
  631.  
  632.     def processEvents(self):
  633.         while gtk.events_pending():
  634.             gtk.main_iteration()
  635.  
  636.     def on_window_main_delete_event(self, widget, event):
  637.         self.dialog_cancel.set_transient_for(self.window_main)
  638.         self.dialog_cancel.realize()
  639.         self.dialog_cancel.window.set_functions(gtk.gdk.FUNC_MOVE)
  640.         res = self.dialog_cancel.run()
  641.         self.dialog_cancel.hide()
  642.         if res == gtk.RESPONSE_CANCEL:
  643.             sys.exit(1)
  644.         return True
  645.  
  646. if __name__ == "__main__":
  647.   
  648.   view = DistUpgradeViewGtk()
  649.   fp = GtkFetchProgressAdapter(view)
  650.   ip = GtkInstallProgressAdapter(view)
  651.  
  652.   cache = apt.Cache()
  653.   for pkg in sys.argv[1:]:
  654.     if cache[pkg].isInstalled:
  655.       cache[pkg].markDelete()
  656.     else:
  657.       cache[pkg].markInstall()
  658.   cache.commit(fp,ip)
  659.   gtk.main()
  660.   sys.exit(0)
  661.   
  662.   #sys.exit(0)
  663.   ip.conffile("TODO","TODO~")
  664.   view.getTerminal().call(["dpkg","--configure","-a"])
  665.   #view.getTerminal().call(["ls","-R","/usr"])
  666.   view.error("short","long",
  667.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  668.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  669.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  670.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  671.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  672.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  673.              "asfds afsdj af asdf asdf asf dsa fadsf asdf as fasf sextended\n"
  674.              )
  675.   view.confirmChanges("xx",[], 100)
  676.   
  677.