home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / python2.4 / site-packages / GDebi / GDebi.py < prev    next >
Encoding:
Python Source  |  2006-08-04  |  29.1 KB  |  702 lines

  1. import sys
  2. import os
  3. import string
  4. import apt
  5. import apt_pkg
  6.  
  7. import pygtk
  8. pygtk.require("2.0")
  9. import gtk, gtk.glade
  10. import pango
  11. import gobject
  12. import vte
  13. import urllib
  14. import fcntl
  15. import posix
  16. import time
  17. import thread
  18. import re
  19.  
  20. from DebPackage import DebPackage, MyCache
  21. from SimpleGladeApp import SimpleGladeApp
  22. from apt.progress import InstallProgress
  23.  
  24. from gettext import gettext as _
  25.  
  26. def utf8(str):
  27.   return unicode(str, 'latin1').encode('utf-8')
  28.  
  29. class GDebi(SimpleGladeApp):
  30.  
  31.     def __init__(self, datadir, options, file=""):
  32.         localesApp="gdebi"
  33.         localesDir="/usr/share/locale"
  34.         gtk.glade.bindtextdomain(localesApp, localesDir)
  35.         gtk.glade.textdomain(localesApp)
  36.  
  37.         SimpleGladeApp.__init__(self, domain="gdebi",
  38.                                 path=datadir+"/gdebi.glade")
  39.  
  40.         # use a nicer default icon
  41.         icons = gtk.icon_theme_get_default()
  42.         logo=icons.load_icon("gnome-mime-application-x-deb", 48, 0)
  43.         if logo != "":
  44.             gtk.window_set_default_icon_list(logo)
  45.  
  46.         # set image of button "install"  manually, since it is overriden 
  47.         #by set_label otherwise
  48.         img = gtk.Image()
  49.         img.set_from_stock(gtk.STOCK_APPLY,gtk.ICON_SIZE_BUTTON)
  50.         self.button_install.set_image(img)
  51.  
  52.         # setup status
  53.         self.context=self.statusbar_main.get_context_id("context_main_window")
  54.         self.statusbar_main.push(self.context,_("Loading..."))
  55.  
  56.         # setup drag'n'drop
  57.         self.window_main.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
  58.                                        gtk.DEST_DEFAULT_HIGHLIGHT |
  59.                                        gtk.DEST_DEFAULT_DROP,
  60.                                        [('text/uri-list',0,0)],
  61.                                        gtk.gdk.ACTION_COPY)
  62.  
  63.         self.window_main.set_sensitive(False)
  64.         self.notebook_details.set_sensitive(False)
  65.         self.hbox_main.set_sensitive(False)
  66.  
  67.         # show what we have
  68.         self.window_main.show()
  69.  
  70.         self.cprogress = self.CacheProgressAdapter(self.progressbar_cache)
  71.         self._cache = MyCache(self.cprogress)
  72.         if self._cache._depcache.BrokenCount > 0:
  73.                 err_header = _("Broken dependencies")
  74.                 err_body = _("Your system has broken dependencies. "
  75.                              "This application can not continue until "
  76.                              "this is fixed. "
  77.                              "To fix it run 'sudo synaptic' or "
  78.                              "'sudo apt-get install -f' "
  79.                              "in a terminal window.")
  80.                 self.show_alert(gtk.MESSAGE_ERROR, err_header, err_body)
  81.                 sys.exit(1)
  82.         self.statusbar_main.push(self.context, "")
  83.         self._options = options
  84.         
  85.         # setup the details treeview
  86.         self.details_list = gtk.ListStore(gobject.TYPE_STRING)
  87.         column = gtk.TreeViewColumn("")
  88.         render = gtk.CellRendererText()
  89.         column.pack_start(render, True)
  90.         column.add_attribute(render, "markup", 0)
  91.         self.treeview_details.append_column(column)
  92.         self.treeview_details.set_model(self.details_list)
  93.         
  94.         if file != "" and os.path.exists(file):
  95.             self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  96.             while gtk.events_pending(): gtk.main_iteration()        
  97.             self.open(file)
  98.             self.window_main.window.set_cursor(None)
  99.         
  100.         self.window_main.set_sensitive(True)
  101.  
  102.  
  103.     def _get_file_path_from_dnd_dropped_uri(self, uri):
  104.         """ helper to get a useful path from a drop uri"""
  105.         path = urllib.url2pathname(uri) # escape special chars
  106.         path = path.strip('\r\n\x00') # remove \r\n and NULL
  107.         # get the path to file
  108.         if path.startswith('file:\\\\\\'): # windows
  109.             path = path[8:] # 8 is len('file:///')
  110.         elif path.startswith('file://'): # nautilus, rox
  111.             path = path[7:] # 7 is len('file://')
  112.         elif path.startswith('file:'): # xffm
  113.             path = path[5:] # 5 is len('file:')
  114.         return path
  115.     
  116.     def on_window_main_drag_data_received(self, widget, context, x, y,
  117.                                           selection, target_type, timestamp):
  118.         """ call when we got a drop event """
  119.         uri = selection.data.strip()
  120.         uri_splitted = uri.split() # we may have more than one file dropped
  121.         for uri in uri_splitted:
  122.             path = self._get_file_path_from_dnd_dropped_uri(uri)
  123.             #print 'path to open', path
  124.             if path.endswith(".deb"):
  125.                 self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  126.                 while gtk.events_pending(): gtk.main_iteration()        
  127.                 self.open(path)
  128.                 self.window_main.window.set_cursor(None)
  129.  
  130.     def open(self, file):
  131.         try:
  132.             self._deb = DebPackage(self._cache, file)
  133.         except (IOError,SystemError),e:
  134.             err_header = _("Could not open '%s'" % os.path.basename(file))
  135.             err_body = _("The package might be corrupted or you are not "
  136.                          "allowed to open the file. Check the permissions "
  137.                          "of the file.")
  138.             self.show_alert(gtk.MESSAGE_ERROR, err_header, err_body)
  139.  
  140.             return False
  141.             
  142.         self.statusbar_main.push(self.context, "")
  143.  
  144.         # grey in since we are ready for user input now
  145.         self.window_main.set_sensitive(True)
  146.  
  147.         # set window title
  148.         self.window_main.set_title(_("Package Installer - %s") % 
  149.                                    self._deb.pkgName)
  150.  
  151.         # set name
  152.         self.label_name.set_markup(self._deb.pkgName)
  153.         
  154.         self.notebook_details.set_sensitive(True)
  155.         self.hbox_main.set_sensitive(True)
  156.  
  157.  
  158.         # set description
  159.         buf = self.textview_description.get_buffer()
  160.         try:
  161.             long_desc = ""
  162.             raw_desc = string.split(utf8(self._deb["Description"]), "\n")
  163.             # append a newline to the summary in the first line
  164.             summary = raw_desc[0]
  165.             raw_desc[0] = ""
  166.             long_desc = "%s\n" % summary
  167.             for line in raw_desc:
  168.                 tmp = string.strip(line)
  169.                 if tmp == ".":
  170.                     long_desc += "\n"
  171.                 else:
  172.                     long_desc += tmp + "\n"
  173.             #print long_desc
  174.             # do some regular expression magic on the description
  175.             # Add a newline before each bullet
  176.             p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE)
  177.             long_desc = p.sub('\n*', long_desc)
  178.             # replace all newlines by spaces
  179.             p = re.compile(r'\n', re.MULTILINE)
  180.             long_desc = p.sub(" ", long_desc)
  181.             # replace all multiple spaces by
  182.             # newlines
  183.             p = re.compile(r'\s\s+', re.MULTILINE)
  184.             long_desc = p.sub("\n", long_desc)
  185.             # write the descr string to the buffer
  186.             buf.set_text(long_desc)
  187.             # tag the first line with a bold font
  188.             tag = buf.create_tag(None, weight=pango.WEIGHT_BOLD)
  189.             iter = buf.get_iter_at_offset(0)
  190.             (start, end) = iter.forward_search("\n",
  191.                                                gtk.TEXT_SEARCH_TEXT_ONLY,
  192.                                                None)
  193.             buf.apply_tag(tag , iter, end)
  194.         except KeyError:
  195.             buf.set_text("No description is available")
  196.  
  197.         # set various status bits
  198.         self.label_version.set_text(self._deb["Version"])
  199.         self.label_maintainer.set_text(utf8(self._deb["Maintainer"]))
  200.         self.label_priority.set_text(self._deb["Priority"])
  201.         self.label_section.set_text(utf8(self._deb["Section"]))
  202.         self.label_size.set_text(self._deb["Installed-Size"] + " KB")
  203.  
  204.         # set filelist
  205.         buf = self.textview_filelist.get_buffer()
  206.         buf.set_text("\n".join(self._deb.filelist))
  207.  
  208.         # check the deps
  209.         if not self._deb.checkDeb():
  210.             self.label_status.set_markup("<span foreground=\"red\" weight=\"bold\">"+
  211.                                          "Error: " +
  212.                                          self._deb._failureString +
  213.                                          "</span>")
  214.         self.button_install.set_label(_("_Install Package"))
  215.  
  216.             self.button_install.set_sensitive(False)
  217.             self.button_details.hide()
  218.             return
  219.  
  220.         if self._deb.compareToVersionInCache() == DebPackage.VERSION_SAME:
  221.             self.label_status.set_text(_("Same version is already installed"))
  222.             self.button_install.set_label(_("_Reinstall Package"))
  223.             self.button_install.grab_default()
  224.             self.button_install.set_sensitive(True)
  225.             self.button_details.hide()
  226.             return
  227.  
  228.         # check if the package is available in the normal sources as well
  229.         res = self._deb.compareToVersionInCache(useInstalled=False)
  230.         if not self._options.non_interactive and res != DebPackage.NO_VERSION:
  231.             pkg = self._cache[self._deb.pkgName]
  232.             title = msg = ""
  233.             
  234.             # FIXME: make this strs better, improve the dialog by
  235.             # providing a option to install from repository directly
  236.             # (when possible)
  237.             if res == DebPackage.VERSION_SAME:
  238.                 if self._cache.downloadable(pkg,useCandidate=True):
  239.                     title = _("Same version is available in a software channel")
  240.                     msg = _("You are recommended to install the software "
  241.                             "from the channel instead.")
  242.             elif res == DebPackage.VERSION_IS_NEWER:
  243.                 if self._cache.downloadable(pkg,useCandidate=True):
  244.                     title = _("An older version is available in a software channel")
  245.                     msg = _("Generally you are recommended to install "
  246.                             "the version from the software channel, since "
  247.                             "it is usually better supported.")
  248.             elif res == DebPackage.VERSION_OUTDATED:
  249.                 if self._cache.downloadable(pkg,useCandidate=True):
  250.                     title = _("A later version is available in a software "
  251.                               "channel")
  252.                     msg = _("You are strongly advised to install "
  253.                             "the version from the software channel, since "
  254.                             "it is usually better supported.")
  255.  
  256.             if title != "" and msg != "":
  257.                 msg = "<big><b>%s</b></big>\n\n%s" % (title,msg)
  258.                 dialog = gtk.MessageDialog(parent=self.window_main,
  259.                                            flags=gtk.DIALOG_MODAL,
  260.                                            type=gtk.MESSAGE_INFO,
  261.                                            buttons=gtk.BUTTONS_CLOSE)
  262.                 dialog.set_markup(msg)
  263.                 dialog.run()
  264.                 dialog.destroy()
  265.  
  266.         (install, remove, unauthenticated) = self._deb.requiredChanges
  267.         deps = ""
  268.         if len(remove) == len(install) == 0:
  269.             deps = _("All dependencies are satisfied")
  270.             self.button_details.hide()
  271.         else:
  272.             self.button_details.show()
  273.         if len(remove) > 0:
  274.             # FIXME: use ngettext here
  275.             deps += _("Requires the <b>removal</b> of %s packages\n") % len(remove)
  276.         if len(install) > 0:
  277.             deps += _("Requires the installation of %s packages") % len(install)
  278.         self.label_status.set_markup(deps)
  279.         img = gtk.Image()
  280.         img.set_from_stock(gtk.STOCK_APPLY,gtk.ICON_SIZE_BUTTON)
  281.         self.button_install.set_image(img)
  282.         self.button_install.set_label(_("_Install Package"))
  283.         self.button_install.set_sensitive(True)
  284.         self.button_install.grab_default()
  285.  
  286.     def on_button_details_clicked(self, widget):
  287.         #print "on_button_details_clicked"
  288.         (install, remove, unauthenticated) = self._deb.requiredChanges
  289.         self.details_list.clear()
  290.         for rm in remove:
  291.             self.details_list.append([_("<b>To be removed: %s</b>") % rm])
  292.         for inst in install:
  293.             self.details_list.append([_("To be installed: %s") % inst])
  294.         self.dialog_details.set_transient_for(self.window_main)
  295.         self.dialog_details.run()
  296.         self.dialog_details.hide()
  297.  
  298.     def on_open_activate(self, widget):
  299.         #print "open"
  300.         # build dialog
  301.         fs = gtk.FileChooserDialog(parent=self.window_main,
  302.                                    buttons=(gtk.STOCK_CANCEL, 
  303.                                             gtk.RESPONSE_CANCEL, 
  304.                                             gtk.STOCK_OPEN, 
  305.                                             gtk.RESPONSE_OK),
  306.                                    action=gtk.FILE_CHOOSER_ACTION_OPEN,
  307.                                    title=_("Open Software Package"))
  308.         fs.set_default_response(gtk.RESPONSE_OK)
  309.         # set filter
  310.         filter = gtk.FileFilter()
  311.         filter.add_pattern("*.deb")
  312.         filter.set_name(_("Software packages"))
  313.         #fs.add_filter(filter)
  314.         fs.set_filter(filter)
  315.         # run it!
  316.         if fs.run() == gtk.RESPONSE_OK:
  317.             #print fs.get_filename()
  318.             self.open(fs.get_filename())
  319.         fs.destroy()
  320.  
  321.     def on_about_activate(self, widget):
  322.         #print "about"
  323.         from Version import VERSION
  324.         self.dialog_about.set_version(VERSION)
  325.         self.dialog_about.run()
  326.         self.dialog_about.hide()
  327.  
  328.     def on_button_install_clicked(self, widget):
  329.         #print "install"
  330.         self.statusbar_main.push(self.context,_("Installing package file..."))
  331.         (install, remove, unauthenticated) = self._deb.requiredChanges
  332.         if widget != None and len(unauthenticated) > 0:
  333.             primary = _("Install unauthenticated software?")
  334.             secondary = _("Malicous software can damage your data "
  335.                           "and take control of your system.\n\n"
  336.                           "The packages below are not authenticated and "
  337.                           "could therefor be of malicous nature.")
  338.             msg = "<big><b>%s</b></big>\n\n%s" % (primary, secondary)
  339.             dialog = gtk.MessageDialog(parent=self.dialog_deb_install,
  340.                                        flags=gtk.DIALOG_MODAL,
  341.                                        type=gtk.MESSAGE_WARNING,
  342.                                        buttons=gtk.BUTTONS_YES_NO)
  343.             dialog.set_markup(msg)
  344.             dialog.set_border_width(6)
  345.             scrolled = gtk.ScrolledWindow()
  346.             textview = gtk.TextView()
  347.             textview.set_cursor_visible(False)
  348.             textview.set_editable(False) 
  349.             buf = textview.get_buffer()
  350.             buf.set_text("\n".join(unauthenticated))
  351.             scrolled.add(textview)
  352.             scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  353.             scrolled.show()
  354.             dialog.vbox.pack_start(scrolled)
  355.             textview.show()
  356.             res = dialog.run()
  357.             dialog.destroy()
  358.             if res != gtk.RESPONSE_YES:
  359.                 return
  360.  
  361.         if os.getuid() != 0:
  362.             os.execl("/usr/bin/gksu", "gksu", "--desktop",
  363.                      "/usr/share/applications/gdebi.desktop",
  364.                      "--", "gdebi-gtk", "--non-interactive",
  365.                      self._deb.file)
  366.             return
  367.  
  368.         # check if we can lock the apt database
  369.         try:
  370.             apt_pkg.PkgSystemLock()
  371.         except SystemError:
  372.             header = _("Only one software management tool is allowed to"
  373.                        " run at the same time")
  374.             body = _("Please close the other application e.g. 'Update "
  375.                      "Manager', 'aptitude' or 'Synaptic' first.")
  376.             self.show_alert(gtk.MESSAGE_ERROR, header, body)
  377.             return
  378.         apt_pkg.PkgSystemUnLock()
  379.  
  380.         # lock for install
  381.         self.window_main.set_sensitive(False)
  382.         self.button_deb_install_close.set_sensitive(False)
  383.         # clear terminal
  384.         #self._term.feed(str(0x1b)+"[2J")
  385.         self.dialog_deb_install.set_transient_for(self.window_main)
  386.         self.dialog_deb_install.show_all()
  387.  
  388.         # install the dependecnies
  389.         if len(install) > 0 or len(remove) > 0:
  390.             # check if we can lock the apt database
  391.             try:
  392.                 apt_pkg.PkgSystemLock()
  393.             except SystemError:
  394.                 header = _("Only one software management tool is allowed to"
  395.                            " run at the same time")
  396.                 body = _("Please close the other application e.g. 'Update "
  397.                          "Manager', 'aptitude' or 'Synaptic' first.")
  398.                 self.show_alert(gtk.MESSAGE_ERROR, header, body)
  399.                 self.dialog_deb_install.hide()
  400.                 self.window_main.set_sensitive(True)
  401.                 return
  402.             # FIXME: use the new python-apt acquire interface here,
  403.             # or rather use it in the apt module and raise exception
  404.             # when stuff goes wrong!
  405.             fprogress = self.FetchProgressAdapter(self.progressbar_install,
  406.                                                   self.label_action,
  407.                                                   self.dialog_deb_install)
  408.             iprogress = self.InstallProgressAdapter(self.progressbar_install,
  409.                                                     self._term,
  410.                                                     self.label_action,
  411.                                                     self.expander_install)
  412.             errMsg = None
  413.             try:
  414.                 res = self._cache.commit(fprogress,iprogress)
  415.             except IOError, msg:
  416.                 res = False
  417.                 errMsg = "%s" % msg
  418.                 header = _("Could not download all required files")
  419.                 body = _("Please check your internet connection or "
  420.                          "installation medium.")
  421.             except SystemError, msg:
  422.                 res = False
  423.                 header = _("Could not install all dependencies"),
  424.                 body = _("Usually this is related to an error of the "
  425.                          "software distributor. See the terminal window for "
  426.                          "more details.")
  427.             if not res:
  428.                 self.show_alert(gtk.MESSAGE_ERROR, header, body, msg)
  429.                 
  430.                 self.label_install_status.set_markup("<span foreground=\"red\" weight=\"bold\">%s</span>" % primary)
  431.                 self.button_deb_install_close.set_sensitive(True)
  432.                 self.button_deb_install_close.grab_default()
  433.         self.statusbar_main.push(self.context,_("Failed to install package file"))
  434.                 return 
  435.  
  436.         # install the package itself
  437.         self.label_action.set_markup("<b><big>"+_("Installing package file")+"</big></b>")
  438.         dprogress = self.DpkgInstallProgress(self._deb.file,
  439.                                              self.label_install_status,
  440.                                              self.progressbar_install,
  441.                                              self._term,
  442.                                              self.expander_install)
  443.         dprogress.commit()
  444.         #self.label_action.set_markup("<b><big>"+_("Package installed")+"</big></b>")
  445.         # show the button
  446.         self.button_deb_install_close.set_sensitive(True)
  447.         self.button_deb_install_close.grab_default()
  448.         self.label_action.set_markup("<b><big>"+_("Installation finished")+"</big></b>")
  449.         if dprogress.exitstatus == 0:
  450.             self.label_install_status.set_markup("<i>"+_("Package '%s' was installed") % os.path.basename(self._deb.file)+"</i>")
  451.         else:
  452.             self.label_install_status.set_markup("<b>"+_("Failed to install package '%s'") % os.path.basename(self._deb.file)+"</b>")
  453.             self.expander_install.set_expanded(True)
  454.         self.statusbar_main.push(self.context,_("Installation complete"))
  455.         # FIXME: Doesn't stop notifying
  456.         #self.window_main.set_property("urgency-hint", 1)
  457.  
  458.         # reopen the cache, reread the file, FIXME: add progress reporting
  459.         #self._cache = MyCache(self.cprogress)
  460.         self._cache = MyCache()
  461.         if self._cache._depcache.BrokenCount > 0:
  462.             err_header = _("Failed to completely install all dependencies")
  463.             err_body = _("To fix this run 'sudo apt-get install -f' in a "
  464.                          "terminal window.")
  465.             self.show_alert(gtk.MESSAGE_ERROR, err_header, err_body)
  466.  
  467.             #print "Autsch, please report"
  468.         self.open(self._deb.file)
  469.         
  470.     def on_button_deb_install_close_clicked(self, widget):
  471.         # FIXME: doesn't turn it off
  472.         #self.window_main.set_property("urgency-hint", 0)
  473.         self.dialog_deb_install.hide()
  474.         self.window_main.set_sensitive(True)
  475.  
  476.     def on_window_main_delete_event(self, *args):
  477.         if self.window_main.get_property("sensitive"):
  478.             gtk.main_quit()
  479.             return False
  480.         else: 
  481.             return True
  482.  
  483.     def create_vte(self, arg1,arg2,arg3,arg4):
  484.         #print "create_vte (for the custom glade widget)"
  485.         self._term = vte.Terminal()
  486.         self._term.set_font_from_string("monospace 10")
  487.         return self._term
  488.  
  489.  
  490.     def show_alert(self, type, header, body=None, details=None):
  491.         self.dialog_hig.set_transient_for(self.window_main)
  492.  
  493.         message = "<b><big>%s</big></b>" % header
  494.         if not body == None:
  495.             message = "%s\n\n%s" % (message, body)
  496.         self.label_hig.set_markup(message)
  497.   
  498.         if not details == None:
  499.              buffer = self.textview_hig.get_buffer()
  500.              buffer.set_text(details)
  501.              self.expander_hig.set_expanded(False)
  502.              self.expander_hig.show()
  503.              
  504.         if type == gtk.MESSAGE_ERROR:
  505.              self.image_hig.set_property("stock", "gtk-dialog-error")
  506.         elif type == gtk.MESSAGE_WARNING:
  507.              self.image_hig.set_property("stock", "gtk-dialog-warning")
  508.         elif type == gtk.MESSAGE_INFO:
  509.              self.image_hig.set_property("stock", "gtk-dialog-info")
  510.              
  511.         res = self.dialog_hig.run()
  512.         self.dialog_hig.hide()
  513.         if res == gtk.RESPONSE_CLOSE:
  514.             return True
  515.  
  516.         
  517.     # embedded classes
  518.     class DpkgInstallProgress(object):
  519.         def __init__(self, debfile, status, progress, term, expander):
  520.             self.debfile = debfile
  521.             self.status = status
  522.             self.progress = progress
  523.             self.term = term
  524.             self.expander = expander
  525.             self.expander.set_expanded(False)
  526.         def commit(self):
  527.             def finish_dpkg(term, pid, status, lock):
  528.                 " helper "
  529.                 self.exitstatus = posix.WEXITSTATUS(status)
  530.                 #print "dpkg finished %s %s" % (pid,status)
  531.                 #print "exit status: %s" % self.exitstatus
  532.                 #print "was signaled %s" % posix.WIFSIGNALED(status)
  533.                 lock.release()
  534.  
  535.             # get a lock
  536.             lock = thread.allocate_lock()
  537.             lock.acquire()
  538.  
  539.             # ui
  540.             self.status.set_markup("<i>"+_("Installing '%s'...") % \
  541.                                    os.path.basename(self.debfile)+"</i>")
  542.             self.progress.pulse()
  543.             self.progress.set_text("")
  544.  
  545.             # prepare reading the pipe
  546.             (readfd, writefd) = os.pipe()
  547.             fcntl.fcntl(readfd, fcntl.F_SETFL,os.O_NONBLOCK)
  548.             #print "fds (%i,%i)" % (readfd,writefd)
  549.  
  550.             # the command
  551.             cmd = "/usr/bin/dpkg"
  552.             argv = [cmd,"--status-fd", "%s"%writefd, "-i", self.debfile]
  553.             env = ["VTE_PTY_KEEP_FD=%s"% writefd]
  554.             #print cmd
  555.             #print argv
  556.             #print env
  557.             #print self.term
  558.  
  559.  
  560.             # prepare for the fork
  561.             reaper = vte.reaper_get()
  562.             reaper.connect("child-exited", finish_dpkg, lock)
  563.             pid = self.term.fork_command(command=cmd, argv=argv, envv=env)
  564.             read = ""
  565.             while lock.locked():
  566.                 while True:
  567.                     try:
  568.                         read += os.read(readfd,1)
  569.                     except OSError, (errno,errstr):
  570.                         # resource temporarly unavailable is ignored
  571.                         if errno != 11:
  572.                             print errstr
  573.                         break
  574.                     if read.endswith("\n"):
  575.                         statusl = string.split(read, ":")
  576.                         if len(statusl) < 2:
  577.                             print "got garbage from dpkg: '%s'" % read
  578.                             read = ""
  579.                             break
  580.                         status = statusl[2].strip()
  581.                         #print status
  582.                         if status == "error" or status == "conffile-prompt":
  583.                             self.expander.set_expanded(True)
  584.                         read = ""
  585.                 self.progress.pulse()
  586.                 while gtk.events_pending():
  587.                     gtk.main_iteration()
  588.                 time.sleep(0.2)
  589.             self.progress.set_fraction(1.0)
  590.             
  591.     
  592.     class InstallProgressAdapter(InstallProgress):
  593.         def __init__(self,progress,term,label,term_expander):
  594.             InstallProgress.__init__(self)
  595.             self.progress = progress
  596.             self.term = term
  597.             self.term_expander = term_expander
  598.             self.finished = False
  599.             self.action = label
  600.             reaper = vte.reaper_get()
  601.             reaper.connect("child-exited",self.child_exited)
  602.             self.env = ["VTE_PTY_KEEP_FD=%s"% self.writefd,
  603.                         "DEBIAN_FRONTEND=gnome",
  604.                         "APT_LISTCHANGES_FRONTEND=gtk"]
  605.         def child_exited(self,term, pid, status):
  606.             #print "child_exited: %s %s %s %s" % (self,term,pid,status)
  607.             self.apt_status = posix.WEXITSTATUS(status)
  608.             self.finished = True
  609.         def error(self, pkg, errormsg):
  610.             # FIXME: display a msg
  611.             self.term_expander.set_expanded(True)
  612.         def conffile(self, current, new):
  613.             # FIXME: display a msg or expand term
  614.             self.term_expander.set_expanded(True)
  615.         def startUpdate(self):
  616.             #print "startUpdate"
  617.             apt_pkg.PkgSystemUnLock()
  618.             self.action.set_markup("<i>"+_("Installing dependencies...")+"</i>")
  619.             self.progress.set_fraction(0.0)
  620.         def statusChange(self, pkg, percent, status):
  621.             self.progress.set_fraction(percent/100.0)
  622.             self.progress.set_text(status)
  623.         def updateInterface(self):
  624.             InstallProgress.updateInterface(self)
  625.             while gtk.events_pending():
  626.                 gtk.main_iteration()
  627.         def fork(self):
  628.             return self.term.forkpty(envv=self.env)
  629.         def waitChild(self):
  630.             while not self.finished:
  631.                 self.updateInterface()
  632.             return self.apt_status
  633.         
  634.     class FetchProgressAdapter(apt.progress.FetchProgress):
  635.         def __init__(self,progress,action,main):
  636.             #print "FetchProgressAdapter.__init__()"
  637.             self.progress = progress
  638.             self.action = action
  639.             self.main = main
  640.         def start(self):
  641.             #print "start()"
  642.             self.action.set_markup("<i>"+_("Downloading additional package files...")+"</i>")
  643.             self.progress.set_fraction(0)
  644.         def stop(self):
  645.             #print "stop()"
  646.             pass
  647.         def pulse(self):
  648.             self.progress.set_text(_("File %s of %s at %s/s" % (self.currentItems,self.totalItems,apt_pkg.SizeToStr(self.currentCPS))))
  649.             self.progress.set_fraction(self.currentBytes/self.totalBytes)
  650.             while gtk.events_pending():
  651.                 gtk.main_iteration()
  652.             return True
  653.         def mediaChange(self, medium, drive):
  654.             #print "mediaChange %s %s" % (medium, drive)
  655.             msg = _("Please insert '%s' into the drive '%s'" % (medium,drive))
  656.             dialog = gtk.MessageDialog(parent=self.main,
  657.                                        flags=gtk.DIALOG_MODAL,
  658.                                        type=gtk.MESSAGE_QUESTION,
  659.                                        buttons=gtk.BUTTONS_OK_CANCEL)
  660.             dialog.set_markup(msg)
  661.             res = dialog.run()
  662.             #print res
  663.             dialog.destroy()
  664.             if  res == gtk.RESPONSE_OK:
  665.                 return True
  666.             return False
  667.  
  668.     class CacheProgressAdapter(apt.progress.FetchProgress):
  669.         def __init__(self, progressbar):
  670.             self.progressbar = progressbar
  671.         def update(self, percent):
  672.             self.progressbar.show()
  673.             self.progressbar.set_fraction(percent/100.0)
  674.             #self.progressbar.set_text(self.op)
  675.             while gtk.events_pending():
  676.                 gtk.main_iteration()
  677.         def done(self):
  678.             self.progressbar.hide()
  679.         
  680. if __name__ == "__main__":
  681.     app = GDebi("data/",None)
  682.  
  683.     pkgs = ["3ddesktop"]
  684.     for pkg in pkgs:
  685.         print "installing %s" % pkg
  686.         app._cache[pkg].markInstall()
  687.  
  688.     for pkg in app._cache:
  689.         if pkg.markedInstall or pkg.markedUpgrade:
  690.             print pkg.name
  691.  
  692.     apt_pkg.PkgSystemLock()
  693.     app.dialog_deb_install.set_transient_for(app.window_main)
  694.     app.dialog_deb_install.show_all()
  695.  
  696.     # install the dependecnies
  697.     fprogress = app.FetchProgressAdapter(app.progressbar_install)
  698.     iprogress = app.InstallProgressAdapter(app.progressbar_install, app._term)
  699.     res = app._cache.commit(fprogress,iprogress)
  700.     print "commit retured: %s" % res
  701.     
  702.