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 / SoftwareProperties / SoftwareProperties.py < prev    next >
Encoding:
Python Source  |  2006-08-24  |  40.8 KB  |  1,059 lines

  1. # gnome-software-properties.in - edit /etc/apt/sources.list
  2. #
  3. #  Copyright (c) 2004,2005 Canonical
  4. #                2004-2005 Michiel Sikkes
  5. #
  6. #  Author: Michiel Sikkes <michiel@eyesopened.nl>
  7. #          Michael Vogt <mvo@debian.org>
  8. #
  9. #  This program is free software; you can redistribute it and/or
  10. #  modify it under the terms of the GNU General Public License as
  11. #  published by the Free Software Foundation; either version 2 of the
  12. #  License, or (at your option) any later version.
  13. #
  14. #  This program is distributed in the hope that it will be useful,
  15. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. #  GNU General Public License for more details.
  18. #
  19. #  You should have received a copy of the GNU General Public License
  20. #  along with this program; if not, write to the Free Software
  21. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. #  USA
  23.  
  24. #import pdb
  25. import sys
  26. import apt
  27. import apt_pkg
  28. import gobject
  29. import shutil
  30. import gettext
  31. import tempfile
  32. from gettext import gettext as _
  33. import os
  34. import string
  35. import re
  36.  
  37. #sys.path.append("@prefix/share/update-manager/python")
  38.  
  39. from UpdateManager.Common.SimpleGladeApp import SimpleGladeApp
  40. from UpdateManager.Common.HelpViewer import HelpViewer
  41. import UpdateManager.Common.aptsources as aptsources
  42. import dialog_add
  43. import dialog_edit
  44. import dialog_cache_outdated
  45. import dialog_add_sources_list
  46. from dialog_apt_key import apt_key
  47. from utils import *
  48.  
  49. (LIST_MARKUP, LIST_ENABLED, LIST_ENTRY_OBJ) = range(3)
  50.  
  51. CONF_MAP = {
  52.   "autoupdate"   : "APT::Periodic::Update-Package-Lists",
  53.   "autodownload" : "APT::Periodic::Download-Upgradeable-Packages",
  54.   "autoclean"    : "APT::Periodic::AutocleanInterval",
  55.   "unattended"   : "APT::Periodic::Unattended-Upgrade",
  56.   "max_size"     : "APT::Archives::MaxSize",
  57.   "max_age"      : "APT::Archives::MaxAge"
  58. }
  59.  
  60. (
  61.     COLUMN_ACTIVE,
  62.     COLUMN_DESC
  63. ) = range(2)
  64.  
  65. RESPONSE_REPLACE = 1
  66. RESPONSE_ADD = 2
  67.  
  68. # columns of the source_store
  69. (
  70.     STORE_ACTIVE, 
  71.     STORE_DESCRIPTION, 
  72.     STORE_SOURCE, 
  73.     STORE_SEPARATOR,
  74.     STORE_VISIBLE
  75. ) = range(5)
  76.  
  77.  
  78. class SoftwareProperties(SimpleGladeApp):
  79.  
  80.   def __init__(self, datadir=None, options=None, parent=None, file=None):
  81.     gtk.window_set_default_icon_name("software-properties")
  82.  
  83.     # FIXME: some saner way is needed here
  84.     if datadir == None:
  85.       datadir = "/usr/share/update-manager/"
  86.     self.datadir = datadir
  87.     SimpleGladeApp.__init__(self, datadir+"glade/SoftwareProperties.glade",
  88.                             None, domain="update-manager")
  89.     self.modified = False
  90.  
  91.     self.file = file
  92.  
  93.     self.distro = aptsources.Distribution()
  94.     cell = gtk.CellRendererText()
  95.     self.combobox_server.pack_start(cell, True)
  96.     self.combobox_server.add_attribute(cell, 'text', 0)
  97.     
  98.     # set up the handler id for the callbacks 
  99.     self.handler_server_changed = self.combobox_server.connect("changed", 
  100.                                   self.on_combobox_server_changed)
  101.     self.handler_source_code_changed = self.checkbutton_source_code.connect(
  102.                                          "toggled",
  103.                                          self.on_checkbutton_source_code_toggled
  104.                                          )
  105.  
  106.     if parent:
  107.       self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
  108.       self.window_main.show()
  109.       self.window_main.set_transient_for(parent)
  110.  
  111.     # If externally called, reparent to external application.
  112.     self.options = options
  113.     if options and options.toplevel != None:
  114.       self.window_main.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
  115.       self.window_main.show()
  116.       toplevel = gtk.gdk.window_foreign_new(int(options.toplevel))
  117.       self.window_main.window.set_transient_for(toplevel)
  118.     
  119.     self.init_sourceslist()
  120.     self.reload_sourceslist()
  121.     self.backup_sourceslist()
  122.  
  123.     self.window_main.show()
  124.  
  125.     # this maps the key (combo-box-index) to the auto-update-interval value
  126.     # where (-1) means, no key
  127.     self.combobox_interval_mapping = { 0 : 1,
  128.                                        1 : 2,
  129.                                        2 : 7,
  130.                                        3 : 14 }
  131.     self.combobox_update_interval.set_active(0)
  132.  
  133.     update_days = apt_pkg.Config.FindI(CONF_MAP["autoupdate"])
  134.  
  135.     self.combobox_update_interval.append_text(_("Daily"))
  136.     self.combobox_update_interval.append_text(_("Every two days"))
  137.     self.combobox_update_interval.append_text(_("Weekly"))
  138.     self.combobox_update_interval.append_text(_("Every two weeks"))
  139.  
  140.     # If a custom period is defined add an corresponding entry
  141.     if not update_days in self.combobox_interval_mapping.values():
  142.         if update_days > 0:
  143.             self.combobox_update_interval.append_text(_("Every %s days") 
  144.                                                       % update_days)
  145.             self.combobox_interval_mapping[4] = update_days
  146.     
  147.     for key in self.combobox_interval_mapping:
  148.       if self.combobox_interval_mapping[key] == update_days:
  149.         self.combobox_update_interval.set_active(key)
  150.         break
  151.  
  152.     if update_days >= 1:
  153.       self.checkbutton_auto_update.set_active(True)
  154.       self.combobox_update_interval.set_sensitive(True)
  155.     else:
  156.       self.checkbutton_auto_update.set_active(False)
  157.       self.combobox_update_interval.set_sensitive(False)
  158.  
  159.     # Automatic removal of cached packages by age
  160.     self.combobox_delete_interval_mapping = { 0 : 7,
  161.                                               1 : 14,
  162.                                               2 : 30 }
  163.  
  164.     delete_days = apt_pkg.Config.FindI(CONF_MAP["max_age"])
  165.  
  166.     self.combobox_delete_interval.append_text(_("After one week"))
  167.     self.combobox_delete_interval.append_text(_("After two weeks"))
  168.     self.combobox_delete_interval.append_text(_("After one month"))
  169.  
  170.     # If a custom period is defined add an corresponding entry
  171.     if not delete_days in self.combobox_delete_interval_mapping.values():
  172.         if delete_days > 0 and CONF_MAP["autoclean"] != 0:
  173.             self.combobox_delete_interval.append_text(_("After %s days") 
  174.                                                       % delete_days)
  175.             self.combobox_delete_interval_mapping[3] = delete_days
  176.     
  177.     for key in self.combobox_delete_interval_mapping:
  178.       if self.combobox_delete_interval_mapping[key] == delete_days:
  179.         self.combobox_delete_interval.set_active(key)
  180.         break
  181.  
  182.     if delete_days >= 1 and apt_pkg.Config.FindI(CONF_MAP["autoclean"]) != 0:
  183.       self.checkbutton_auto_delete.set_active(True)
  184.       self.combobox_delete_interval.set_sensitive(True)
  185.     else:
  186.       self.checkbutton_auto_delete.set_active(False)
  187.       self.combobox_delete_interval.set_sensitive(False)
  188.  
  189.     # Autodownload
  190.     if apt_pkg.Config.FindI(CONF_MAP["autodownload"]) == 1:
  191.       self.checkbutton_auto_download.set_active(True)
  192.     else:
  193.       self.checkbutton_auto_download.set_active(False)
  194.  
  195.     # Unattended updates
  196.     if os.path.exists("/usr/bin/unattended-upgrade"):
  197.         # FIXME: we should always show the option. if unattended-upgrades is
  198.         # not installed a dialog should popup and allow the user to install
  199.         # unattended-upgrade
  200.         #self.checkbutton_unattended.set_sensitive(True)
  201.         self.checkbutton_unattended.show()
  202.     else:
  203.         #self.checkbutton_unattended.set_sensitive(False)
  204.         self.checkbutton_unattended.hide()
  205.     if apt_pkg.Config.FindI(CONF_MAP["unattended"]) == 1:
  206.         self.checkbutton_unattended.set_active(True)
  207.     else:
  208.         self.checkbutton_unattended.set_active(False)
  209.  
  210.     self.help_viewer = HelpViewer("update-manager#setting-preferences")
  211.     if self.help_viewer.check() == False:
  212.         self.button_help.set_sensitive(False)
  213.  
  214.     # apt-key stuff
  215.     self.apt_key = apt_key()
  216.     self.init_keyslist()
  217.     self.reload_keyslist()
  218.  
  219.     # drag and drop support for sources.list
  220.     self.treeview_sources.drag_dest_set(gtk.DEST_DEFAULT_ALL, \
  221.                                         [('text/uri-list',0, 0)], \
  222.                                         gtk.gdk.ACTION_COPY)
  223.     self.treeview_sources.connect("drag_data_received",\
  224.                                   self.on_sources_drag_data_received)
  225.  
  226.     # call the add sources.list dialog if we got a file from the cli
  227.     if self.file != None:
  228.         self.open_file(file)
  229.  
  230.   def distro_to_widgets(self):
  231.     """
  232.     Represent the distro information in the user interface
  233.     """
  234.     # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
  235.     self.label_updates.set_label("<b>%s</b>" % (_("%s updates") %\
  236.                                                 self.distro.id))
  237.     # TRANS: %s stands for the distribution name e.g. Debian or Ubuntu
  238.     self.label_dist_name.set_label("%s" % self.distro.description)
  239.  
  240.     # Setup the checkbuttons for the components
  241.     for checkbutton in self.vbox_dist_comps.get_children():
  242.          self.vbox_dist_comps.remove(checkbutton)
  243.     for comp in self.distro.source_template.components.keys():
  244.         checkbox = gtk.CheckButton(label=self.distro.source_template.components[comp][2])
  245.         # check if the comp is enabled
  246.         # FIXME: use inconsistence if there are main sources with not all comps
  247.         if comp in self.distro.download_comps:
  248.             checkbox.set_active(True)
  249.         # setup the callback and show the checkbutton
  250.         checkbox.connect("toggled", self.on_component_toggled, comp)
  251.         self.vbox_dist_comps.add(checkbox)
  252.         checkbox.show()
  253.  
  254.     # Setup the checkbuttons for the child repos / updates
  255.     for checkbutton in self.vbox_updates.get_children():
  256.          self.vbox_updates.remove(checkbutton)
  257.     for template in self.distro.source_template.children:
  258.         checkbox = gtk.CheckButton(label=template.description)
  259.         comps = []
  260.         for child in self.distro.child_sources:
  261.             if child.template == template:
  262.                 comps.extend(child.comps)
  263.         # check if all comps of the main source are also enabled 
  264.         # for the corresponding child sources
  265.         if len(comps) > 0 and \
  266.              len(self.distro.enabled_comps ^ set(comps)) == 0:
  267.             # the cild source covers all components
  268.             checkbox.set_active(True)
  269.         elif len(comps) > 0 and\
  270.              len(self.distro.enabled_comps ^ set(comps)) != 0:
  271.             # the cild is enabled, but doesn't cover 
  272.             # all components
  273.             checkbox.set_active(False)
  274.             checkbox.set_inconsistent(True)
  275.         else:
  276.             # there is no corresponding child source at all
  277.             checkbox.set_active(False)
  278.         # setup the callback and show the checkbutton
  279.         checkbox.connect("toggled", self.on_checkbutton_child_toggled,
  280.                          template)
  281.         self.vbox_updates.add(checkbox)
  282.         checkbox.show()
  283.  
  284.     if len(self.distro.enabled_comps) < 1:
  285.         self.vbox_updates.set_sensitive(False)
  286.     else:
  287.         self.vbox_updates.set_sensitive(True)
  288.  
  289.     # Intiate the combobox which allows do specify a server for all
  290.     # distro related sources
  291.     self.combobox_server.handler_block(self.handler_server_changed)
  292.     server_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
  293.     self.combobox_server.set_model(server_store)
  294.     server_store.append([_("Main server"),
  295.                         self.distro.main_server])
  296.     if self.distro.country != None:
  297.         # TRANSLATORS: %s is a country
  298.         server_store.append([_("Server for %s") % gettext.dgettext("iso-3166",
  299.                              self.distro.country).rstrip(),
  300.                              self.distro.nearest_server])
  301.     else:
  302.         server_store.append([_("Nearest server"),
  303.                              self.distro.nearest_server])
  304.     if len(self.distro.used_servers) > 0:
  305.         for server in self.distro.used_servers:
  306.             if not re.match(server, self.distro.main_server) and \
  307.                not re.match(server, self.distro.nearest_server):
  308.                 # FIXME: use regexp here instead of this
  309.                 country = ""
  310.                 i = server.find("://")
  311.                 l = server.find(".archive.ubuntu.com")
  312.                 if i != -1 and l != -1:
  313.                   country = server[i+len("://"):l]
  314.                 if self.distro.countries.has_key(country):
  315.                     # TRANSLATORS: %s is a country
  316.                     server_store.append([_("Server for %s") % \
  317.                                         gettext.dgettext("iso-3166",
  318.                                         self.distro.countries[country].rstrip()),
  319.                                         server])
  320.                 else:
  321.                     server_store.append(["%s" % server, server])
  322.         if len(self.distro.used_servers) > 1:
  323.             server_store.append([_("Custom servers"), None])
  324.             self.combobox_server.set_active(2)
  325.         elif self.distro.used_servers[0] == self.distro.main_server:
  326.             self.combobox_server.set_active(0)
  327.         elif self.distro.used_servers[0] == self.distro.nearest_server:
  328.             self.combobox_server.set_active(1)
  329.         elif len(self.distro.used_servers) == 1:
  330.             self.combobox_server.set_active(2)
  331.  
  332.     else:
  333.         self.combobox_server.set_active(0)
  334.  
  335.     self.combobox_server.handler_unblock(self.handler_server_changed)
  336.  
  337.     # Check for source code sources
  338.     self.checkbutton_source_code.handler_block(self.handler_source_code_changed)
  339.     self.checkbutton_source_code.set_inconsistent(False)
  340.     if len(self.distro.source_code_sources) < 1:
  341.         # we don't have any source code sources, so
  342.         # uncheck the button
  343.         self.checkbutton_source_code.set_active(False)
  344.         self.distro.get_source_code = False
  345.     else:
  346.         # there are source code sources, so we check the button
  347.         self.checkbutton_source_code.set_active(True)
  348.         self.distro.get_source_code = True
  349.         # check if there is a corresponding source code source for
  350.         # every binary source. if not set the checkbutton to inconsistent
  351.         templates = {}
  352.         sources = []
  353.         sources.extend(self.distro.main_sources)
  354.         sources.extend(self.distro.child_sources)
  355.         for source in sources:
  356.             if templates.has_key(source.template):
  357.                 for comp in source.comps:
  358.                     templates[source.template].add(comp)
  359.             else:
  360.                 templates[source.template] = set(source.comps)
  361.         # add fake http sources for the cdrom, since the sources
  362.         # for the cdrom are only available in the internet
  363.         if len(self.distro.cdrom_sources) > 0:
  364.             templates[self.distro.source_template] = self.distro.cdrom_comps
  365.         for source in self.distro.source_code_sources:
  366.             if not templates.has_key(source.template) or \
  367.                (templates.has_key(source.template) and not \
  368.                 (len(set(templates[source.template]) ^ set(source.comps)) == 0\
  369.                  or (len(set(source.comps) ^ self.distro.enabled_comps) == 0))):
  370.                 self.checkbutton_source_code.set_inconsistent(True)
  371.                 self.distro.get_source_code = False
  372.                 break
  373.     self.checkbutton_source_code.handler_unblock(self.handler_source_code_changed)
  374.  
  375.     if len(self.cdrom_store) == 0:
  376.         self.treeview_cdroms.set_sensitive(False)
  377.     else:
  378.         self.treeview_cdroms.set_sensitive(True)
  379.  
  380.   def on_combobox_server_changed(self, combobox):
  381.     """
  382.     Replace the servers used by the main and update sources with
  383.     the selected one
  384.     """
  385.     server_store = combobox.get_model()
  386.     iter = combobox.get_active_iter()
  387.     uri_selected = server_store.get_value(iter, 1)
  388.     sources = []
  389.     sources.extend(self.distro.main_sources)
  390.     sources.extend(self.distro.child_sources)
  391.     sources.extend(self.distro.source_code_sources)
  392.     for source in sources:
  393.         # FIXME: ugly
  394.         if not "security.ubuntu.com" in source.uri:
  395.             source.uri = uri_selected
  396.     self.distro.ddefault_server = uri_selected
  397.     self.modified_sourceslist()
  398.  
  399.   def on_component_toggled(self, checkbutton, comp):
  400.     """
  401.     Sync the components of all main sources (excluding cdroms),
  402.     child sources and source code sources
  403.     """
  404.     if checkbutton.get_active() == True:
  405.         self.distro.enable_component(self.sourceslist, comp)
  406.     else:
  407.         self.distro.disable_component(self.sourceslist, comp)
  408.     self.modified_sourceslist()
  409.  
  410.   def massive_debug_output(self):
  411.       """
  412.       do not write our changes yet - just print them to std_out
  413.       """
  414.       print "START SOURCES.LIST:"
  415.       for source in self.sourceslist:
  416.           print source.str()
  417.       print "END SOURCES.LIST\n"
  418.  
  419.   def on_checkbutton_child_toggled(self, checkbutton, template):
  420.     """
  421.     Enable or disable a child repo of the distribution main repository
  422.     """
  423.     if checkbutton.get_active() == False:
  424.         for source in self.distro.child_sources:
  425.             if source.template == template:
  426.                 self.sourceslist.remove(source)
  427.     else:
  428.         self.distro.add_source(self.sourceslist,
  429.                                      uri=template.base_uri,
  430.                                      dist=template.name)
  431.     self.modified_sourceslist()
  432.   
  433.   def on_checkbutton_source_code_toggled(self, checkbutton):
  434.     """
  435.     Disable or enable the source code for all sources
  436.     """
  437.     self.distro.get_source_code = checkbutton.get_active()
  438.     sources = []
  439.     sources.extend(self.distro.main_sources)
  440.     sources.extend(self.distro.child_sources)
  441.  
  442.     # remove all exisiting sources
  443.     for source in self.distro.source_code_sources:
  444.         self.sourceslist.remove(source)
  445.  
  446.     if checkbutton.get_active() == True:
  447.         for source in sources:
  448.             self.sourceslist.add("deb-src",
  449.                                  source.uri,
  450.                                  source.dist,
  451.                                  source.comps,
  452.                                  "Added by software-properties",
  453.                                  self.sourceslist.list.index(source)+1,
  454.                                  source.file)
  455.         for source in self.distro.cdrom_sources:
  456.             self.sourceslist.add("deb-src",
  457.                                  self.distro.source_template.base_uri,
  458.                                  self.distro.source_template.name,
  459.                                  source.comps,
  460.                                  "Added by software-properties",
  461.                                  self.sourceslist.list.index(source)+1,
  462.                                  source.file)
  463.     self.modified_sourceslist()
  464.  
  465.   def on_checkbutton_popcon_toggled(self, widget):
  466.     """ The user clicked on the popcon paritipcation button """
  467.     popcon = "/etc/popularity-contest.conf"
  468.     if widget.get_active():
  469.       new_value = "yes"
  470.     else:
  471.       new_value = "no"
  472.     if os.path.exists(popcon):
  473.       # read it
  474.       lines = open(popcon).read().split("\n")
  475.       for line in lines:
  476.         try:
  477.           (key,value) = line.split("=")
  478.           if key == "PARTICIPATE":
  479.             lines[lines.index(line)] = 'PARTICIPATE=\"%s"' % new_value
  480.         except ValueError:
  481.           continue
  482.       # write it
  483.       open(popcon,"w").write("\n".join(lines))
  484.  
  485.  
  486.   def open_file(self, file):
  487.     """Show an confirmation for adding the channels of the specified file"""
  488.     dialog = dialog_add_sources_list.AddSourcesList(self.window_main,
  489.                                                     self.sourceslist,
  490.                                                     self.render_source,
  491.                                                     self.get_comparable,
  492.                                                     self.datadir,
  493.                                                     file)
  494.     (res, new_sources) = dialog.run()
  495.     if res == RESPONSE_REPLACE:
  496.         self.sourceslist.list = []
  497.     if res in (RESPONSE_ADD, RESPONSE_REPLACE):
  498.         for source in new_sources:
  499.             self.sourceslist.add(source.type,
  500.                                  source.uri,
  501.                                  source.dist,
  502.                                  source.comps,
  503.                                  source.comment)
  504.         self.modified_sourceslist()
  505.  
  506.   def on_sources_drag_data_received(self, widget, context, x, y,
  507.                                      selection, target_type, timestamp):
  508.       """Extract the dropped file pathes and open the first file, only"""
  509.       uri = selection.data.strip()
  510.       uri_splitted = uri.split()
  511.       if len(uri_splitted)>0:
  512.           self.open_file(uri_splitted[0])
  513.  
  514.   def hide(self):
  515.     self.window_main.hide()
  516.     
  517.   def init_sourceslist(self):
  518.     """
  519.     Read all valid sources into our ListStore
  520.     """
  521.     # STORE_ACTIVE - is the source enabled or disabled
  522.     # STORE_DESCRIPTION - description of the source entry
  523.     # STORE_SOURCE - the source entry object
  524.     # STORE_SEPARATOR - if the entry is a separator
  525.     # STORE_VISIBLE - if the entry is shown or hidden
  526.     self.cdrom_store = gtk.ListStore(gobject.TYPE_BOOLEAN, 
  527.                                      gobject.TYPE_STRING,
  528.                                      gobject.TYPE_PYOBJECT,
  529.                                      gobject.TYPE_BOOLEAN,
  530.                                      gobject.TYPE_BOOLEAN)
  531.     self.treeview_cdroms.set_model(self.cdrom_store)
  532.     self.source_store = gtk.ListStore(gobject.TYPE_BOOLEAN, 
  533.                                       gobject.TYPE_STRING,
  534.                                       gobject.TYPE_PYOBJECT,
  535.                                       gobject.TYPE_BOOLEAN,
  536.                                       gobject.TYPE_BOOLEAN)
  537.     self.treeview_sources.set_model(self.source_store)
  538.     self.treeview_sources.set_row_separator_func(self.is_separator,
  539.                                                  STORE_SEPARATOR)
  540.  
  541.     cell_desc = gtk.CellRendererText()
  542.     cell_desc.set_property("xpad", 2)
  543.     cell_desc.set_property("ypad", 2)
  544.     col_desc = gtk.TreeViewColumn(_("Software Channel"), cell_desc,
  545.                                   markup=COLUMN_DESC)
  546.     col_desc.set_max_width(1000)
  547.  
  548.     cell_toggle = gtk.CellRendererToggle()
  549.     cell_toggle.set_property("xpad", 2)
  550.     cell_toggle.set_property("ypad", 2)
  551.     cell_toggle.connect('toggled', self.on_channel_toggled, self.cdrom_store)
  552.     col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
  553.                                     active=COLUMN_ACTIVE)
  554.  
  555.     self.treeview_cdroms.append_column(col_active)
  556.     self.treeview_cdroms.append_column(col_desc)
  557.  
  558.     cell_desc = gtk.CellRendererText()
  559.     cell_desc.set_property("xpad", 2)
  560.     cell_desc.set_property("ypad", 2)
  561.     col_desc = gtk.TreeViewColumn(_("Software Channel"), cell_desc,
  562.                                   markup=COLUMN_DESC)
  563.     col_desc.set_max_width(1000)
  564.  
  565.     cell_toggle = gtk.CellRendererToggle()
  566.     cell_toggle.set_property("xpad", 2)
  567.     cell_toggle.set_property("ypad", 2)
  568.     cell_toggle.connect('toggled', self.on_channel_toggled, self.source_store)
  569.     col_active = gtk.TreeViewColumn(_("Active"), cell_toggle,
  570.                                     active=COLUMN_ACTIVE)
  571.  
  572.     self.treeview_sources.append_column(col_active)
  573.     self.treeview_sources.append_column(col_desc)
  574.  
  575.     self.sourceslist = aptsources.SourcesList()
  576.  
  577.   def backup_sourceslist(self):
  578.     """
  579.     Duplicate the list of sources
  580.     """
  581.     self.sourceslist_backup = []
  582.     for source in self.sourceslist.list:
  583.         source_bkp = aptsources.SourceEntry(line=source.line,file=source.file)
  584.         self.sourceslist_backup.append(source_bkp)
  585.  
  586.   def on_channel_activate(self, treeview, path, column):
  587.     """Open the edit dialog if a channel was double clicked"""
  588.     self.on_edit_clicked(treeview)
  589.  
  590.   def on_treeview_sources_cursor_changed(self, treeview):
  591.     """Enable the buttons remove and edit if a channel is selected"""
  592.     sel = self.treeview_sources.get_selection()
  593.     (model, iter) = sel.get_selected()
  594.     if iter:
  595.         self.button_edit.set_sensitive(True)
  596.         self.button_remove.set_sensitive(True)
  597.     else:
  598.         self.button_edit.set_sensitive(False)
  599.         self.button_remove.set_sensitive(False)
  600.   
  601.   def on_channel_toggled(self, cell_toggle, path, store):
  602.     """Enable or disable the selected channel"""
  603.     #FIXME cdroms need to disable the comps in the childs and sources
  604.     iter = store.get_iter((int(path),))
  605.     source_entry = store.get_value(iter, STORE_SOURCE) 
  606.     source_entry.disabled = not source_entry.disabled
  607.     store.set_value(iter, STORE_ACTIVE, not source_entry.disabled)
  608.     self.modified_sourceslist()
  609.  
  610.   def init_keyslist(self):
  611.     self.keys_store = gtk.ListStore(str)
  612.     self.treeview2.set_model(self.keys_store)
  613.     
  614.     tr = gtk.CellRendererText()
  615.     
  616.     keys_col = gtk.TreeViewColumn("Key", tr, text=0)
  617.     self.treeview2.append_column(keys_col)
  618.     
  619.   def on_button_revert_clicked(self, button):
  620.     """Restore the source list from the startup of the dialog"""
  621.     self.sourceslist.list = []
  622.     for source in self.sourceslist_backup:
  623.         source_reset = aptsources.SourceEntry(line=source.line,file=source.file)
  624.         self.sourceslist.list.append(source_reset)
  625.     self.save_sourceslist()
  626.     self.reload_sourceslist()
  627.     self.button_revert.set_sensitive(False)
  628.     self.modified = False
  629.   
  630.   def modified_sourceslist(self):
  631.     """The sources list was changed and now needs to be saved and reloaded"""
  632.     self.massive_debug_output()
  633.     self.modified = True
  634.     self.button_revert.set_sensitive(True)
  635.     self.save_sourceslist()
  636.     self.reload_sourceslist()
  637.  
  638.   def render_source(self, source):
  639.     """Render a nice output to show the source in a treeview"""
  640.  
  641.     if source.template == None:
  642.         if source.comment:
  643.             contents = "<b>%s</b>" % source.comment
  644.             # Only show the components if there are more than one
  645.             if len(source.comps) > 1:
  646.                 for c in source.comps:
  647.                     contents += " %s" % c
  648.         else:
  649.             contents = "<b>%s %s</b>" % (source.uri, source.dist)
  650.             for c in source.comps:
  651.                 contents += " %s" % c
  652.         if source.type in ("deb-src", "rpm-src"):
  653.             contents += " %s" % _("(Source Code)")
  654.         return contents
  655.     else:
  656.         # try to make use of an corresponding template
  657.         contents = "<b>%s</b>" % source.template.description
  658.         if source.type in ("deb-src", "rpm-src"):
  659.             contents += " (%s)" % _("Source Code")
  660.         if source.comment:
  661.             contents +=" %s" % source.comment
  662.         if source.template.child == False:
  663.             for comp in source.comps:
  664.                 if source.template.components.has_key(comp):
  665.                     print source.template.components[comp]
  666.                     (desc, enabled, desc_long) = source.template.components[comp]
  667.                     contents += "\n%s" % desc
  668.                 else:
  669.                     contents += "\n%s" % comp
  670.         return contents
  671.  
  672.   def get_comparable(self, source):
  673.       """extract attributes to sort the sources"""
  674.       cur_sys = 1
  675.       has_template = 1
  676.       has_comment = 1
  677.       is_source = 1
  678.       revert_numbers = string.maketrans("0123456789", "9876543210")
  679.       if source.template:
  680.         has_template = 0
  681.         desc = source.template.description
  682.         if source.template.distribution == self.distro:
  683.             cur_sys = 0
  684.       else:
  685.           desc = "%s %s %s" % (source.uri, source.dist, source.comps)
  686.           if source.comment:
  687.               has_comment = 0
  688.       if source.type.find("src"):
  689.           is_source = 0
  690.       return (cur_sys, has_template, has_comment, is_source,
  691.               desc.translate(revert_numbers))
  692.  
  693.   def reload_sourceslist(self):
  694.     (path_x, path_y) = self.treeview_sources.get_cursor()
  695.     self.source_store.clear()
  696.     self.cdrom_store.clear()
  697.     self.sourceslist.refresh()
  698.     self.sourceslist_visible=[]
  699.     self.distro.get_sources(self.sourceslist)
  700.     # Only show sources that are no binary or source code repos for
  701.     # the current distribution, but show cdrom based repos
  702.     for source in self.sourceslist.list:
  703.         if not source.invalid and\
  704.            (source not in self.distro.main_sources and\
  705.             source not in self.distro.cdrom_sources and\
  706.             source not in self.distro.child_sources and\
  707.             source not in self.distro.disabled_sources) and\
  708.            source not in self.distro.source_code_sources:
  709.             self.sourceslist_visible.append(source)
  710.         elif not source.invalid and source in self.distro.cdrom_sources:
  711.             contents = self.render_source(source)
  712.             self.cdrom_store.append([not source.disabled, contents,
  713.                                     source, False, True])
  714.  
  715.     # Sort the sources list
  716.     self.sourceslist_visible.sort(key=self.get_comparable)
  717.  
  718.     for source in self.sourceslist_visible:
  719.         contents = self.render_source(source)
  720.  
  721.         self.source_store.append([not source.disabled, contents,
  722.                                   source, False, True])
  723.  
  724.     (path_x, path_y) = self.treeview_sources.get_cursor()
  725.     if len(self.source_store) < 1 or path_x <0:
  726.         self.button_remove.set_sensitive(False)
  727.         self.button_edit.set_sensitive(False)
  728.     self.distro.get_sources(self.sourceslist)
  729.     self.distro_to_widgets()
  730.     
  731.   def is_separator(self, model, iter, column):
  732.     return model.get_value(iter, column) 
  733.       
  734.   def reload_keyslist(self):
  735.     self.keys_store.clear()
  736.     for key in self.apt_key.list():
  737.       self.keys_store.append([key])
  738.  
  739.   def on_combobox_update_interval_changed(self, widget):
  740.     i = self.combobox_update_interval.get_active()
  741.     if i != -1:
  742.         value = self.combobox_interval_mapping[i]
  743.         # Only write the key if it has changed
  744.         if not value == apt_pkg.Config.FindI(CONF_MAP["autoupdate"]):
  745.             apt_pkg.Config.Set(CONF_MAP["autoupdate"], str(value))
  746.             self.write_config()
  747.  
  748.   def on_opt_autoupdate_toggled(self, widget):
  749.     if self.checkbutton_auto_update.get_active():
  750.       self.combobox_update_interval.set_sensitive(True)
  751.       # if no frequency was specified use daily
  752.       i = self.combobox_update_interval.get_active()
  753.       if i == -1:
  754.           i = 0
  755.           self.combobox_update_interval.set_active(i)
  756.       value = self.combobox_interval_mapping[i]
  757.     else:
  758.       self.combobox_update_interval.set_sensitive(False)
  759.       value = 0
  760.     apt_pkg.Config.Set(CONF_MAP["autoupdate"], str(value))
  761.     # FIXME: Write config options, apt_pkg should be able to do this.
  762.     self.write_config()
  763.  
  764.   def on_opt_unattended_toggled(self, widget):  
  765.     if self.checkbutton_unattended.get_active():
  766.         self.checkbutton_unattended.set_active(True)
  767.         apt_pkg.Config.Set(CONF_MAP["unattended"], str(1))
  768.     else:
  769.         self.checkbutton_unattended.set_active(False)
  770.         apt_pkg.Config.Set(CONF_MAP["unattended"], str(0))
  771.     # FIXME: Write config options, apt_pkg should be able to do this.
  772.     self.write_config()
  773.  
  774.   def on_opt_autodownload_toggled(self, widget):  
  775.     if self.checkbutton_auto_download.get_active():
  776.         self.checkbutton_auto_download.set_active(True)
  777.         apt_pkg.Config.Set(CONF_MAP["autodownload"], str(1))
  778.     else:
  779.         self.checkbutton_auto_download.set_active(False)
  780.         apt_pkg.Config.Set(CONF_MAP["autodownload"], str(0))
  781.     # FIXME: Write config options, apt_pkg should be able to do this.
  782.     self.write_config()
  783.  
  784.   def on_combobox_delete_interval_changed(self, widget):
  785.     i = self.combobox_delete_interval.get_active()
  786.     if i != -1:
  787.         value = self.combobox_delete_interval_mapping[i]
  788.         # Only write the key if it has changed
  789.         if not value == apt_pkg.Config.FindI(CONF_MAP["max_age"]):
  790.             apt_pkg.Config.Set(CONF_MAP["max_age"], str(value))
  791.             self.write_config()
  792.       
  793.   def on_opt_autodelete_toggled(self, widget):  
  794.     if self.checkbutton_auto_delete.get_active():
  795.       self.combobox_delete_interval.set_sensitive(True)
  796.       # if no frequency was specified use the first default value
  797.       i = self.combobox_delete_interval.get_active()
  798.       if i == -1:
  799.           i = 0
  800.           self.combobox_delete_interval.set_active(i)
  801.       value_maxage = self.combobox_delete_interval_mapping[i]
  802.       value_clean = 1
  803.       apt_pkg.Config.Set(CONF_MAP["max_age"], str(value_maxage))
  804.     else:
  805.       self.combobox_delete_interval.set_sensitive(False)
  806.       value_clean = 0
  807.     apt_pkg.Config.Set(CONF_MAP["autoclean"], str(value_clean))
  808.     # FIXME: Write config options, apt_pkg should be able to do this.
  809.     self.write_config()
  810.     
  811.   def write_config(self):
  812.     # update the adept file as well if it is there
  813.     conffiles = ["/etc/apt/apt.conf.d/10periodic",
  814.                  "/etc/apt/apt.conf.d/15adept-periodic-update"]
  815.  
  816.     # check (beforehand) if one exists, if not create one
  817.     for f in conffiles:
  818.       if os.path.isfile(f):
  819.         break
  820.     else:
  821.       print "No config found, creating one"
  822.       open(conffiles[0], "w")
  823.  
  824.     # now update them
  825.     for periodic in conffiles:
  826.       # read the old content first
  827.       content = []
  828.       if os.path.isfile(periodic):
  829.         content = open(periodic, "r").readlines()
  830.         cnf = apt_pkg.Config.SubTree("APT::Periodic")
  831.  
  832.         # then write a new file without the updated keys
  833.         f = open(periodic, "w")
  834.         for line in content:
  835.           for key in cnf.List():
  836.             if line.find("APT::Periodic::%s" % (key)) >= 0:
  837.               break
  838.           else:
  839.             f.write(line)
  840.  
  841.         # and append the updated keys
  842.         for i in cnf.List():
  843.           f.write("APT::Periodic::%s \"%s\";\n" % (i, cnf.FindI(i)))
  844.         f.close()    
  845.  
  846.   def save_sourceslist(self):
  847.     #location = "/etc/apt/sources.list"
  848.     #shutil.copy(location, location + ".save")
  849.     self.sourceslist.backup(".save")
  850.     self.sourceslist.save()
  851.  
  852.   def on_add_clicked(self, widget):
  853.     dialog = dialog_add.dialog_add(self.window_main, self.sourceslist,
  854.                                    self.datadir)
  855.     line = dialog.run()
  856.     if line != None:
  857.       self.sourceslist.list.append(aptsources.SourceEntry(line))
  858.       self.modified_sourceslist()
  859.       
  860.   def on_edit_clicked(self, widget):
  861.     sel = self.treeview_sources.get_selection()
  862.     (model, iter) = sel.get_selected()
  863.     if not iter:
  864.       return
  865.     source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
  866.     dialog = dialog_edit.dialog_edit(self.window_main, self.sourceslist,
  867.                                      source_entry, self.datadir)
  868.     if dialog.run() == gtk.RESPONSE_OK:
  869.         self.modified_sourceslist()
  870.  
  871.   # FIXME:outstanding from merge
  872.   def on_channel_activated(self, treeview, path, column):
  873.      """Open the edit dialog if a channel was double clicked"""
  874.      # check if the channel can be edited
  875.      if self.button_edit.get_property("sensitive") == True:
  876.          self.on_edit_clicked(treeview)
  877.  
  878.   # FIXME:outstanding from merge
  879.   def on_treeview_sources_cursor_changed(self, treeview):
  880.     """set the sensitiveness of the edit and remove button
  881.        corresponding to the selected channel"""
  882.     sel = self.treeview_sources.get_selection()
  883.     (model, iter) = sel.get_selected()
  884.     if not iter:
  885.         # No channel is selected, so disable edit and remove
  886.         self.button_edit.set_sensitive(False)
  887.         self.button_remove.set_sensitive(False)
  888.         return
  889.     # allow to remove the selected channel
  890.     self.button_remove.set_sensitive(True)
  891.     # disable editing of cdrom sources
  892.     source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
  893.     if source_entry.uri.startswith("cdrom:"):
  894.         self.button_edit.set_sensitive(False)
  895.     else:
  896.         self.button_edit.set_sensitive(True)
  897.  
  898.   def on_remove_clicked(self, widget):
  899.     model = self.treeview_sources.get_model()
  900.     (path, column) = self.treeview_sources.get_cursor()
  901.     iter = model.get_iter(path)
  902.     if iter:
  903.       source = model.get_value(iter, LIST_ENTRY_OBJ)
  904.       self.sourceslist.remove(source)
  905.       self.modified_sourceslist()
  906.  
  907.   def add_key_clicked(self, widget):
  908.     chooser = gtk.FileChooserDialog(title=_("Import key"),
  909.                                     parent=self.window_main,
  910.                                     buttons=(gtk.STOCK_CANCEL,
  911.                                              gtk.RESPONSE_REJECT,
  912.                                              gtk.STOCK_OK,gtk.RESPONSE_ACCEPT))
  913.     res = chooser.run()
  914.     chooser.hide()
  915.     if res == gtk.RESPONSE_ACCEPT:
  916.       if not self.apt_key.add(chooser.get_filename()):
  917.         error(self.window_main,
  918.               _("Error importing selected file"),
  919.               _("The selected file may not be a GPG key file " \
  920.                 "or it might be corrupt."))
  921.       self.reload_keyslist()
  922.         
  923.   def remove_key_clicked(self, widget):
  924.     selection = self.treeview2.get_selection()
  925.     (model,a_iter) = selection.get_selected()
  926.     if a_iter == None:
  927.         return
  928.     key = model.get_value(a_iter,0)
  929.     if not self.apt_key.rm(key[:8]):
  930.       error(self.main,
  931.         _("Error removing the key"),
  932.         _("The key you selected could not be removed. "
  933.           "Please report this as a bug."))
  934.     self.reload_keyslist()
  935.     
  936.   def on_restore_clicked(self, widget):
  937.     self.apt_key.update()
  938.     self.reload_keyslist()
  939.     
  940.   def on_delete_event(self, widget, args):
  941.     self.on_close_button(widget)
  942.  
  943.   def on_close_button(self, widget):
  944.     # show a dialog that a reload of the channel information is required
  945.     # only if there is no parent defined
  946.     if self.modified == True and \
  947.        self.options.toplevel == None:
  948.         d = dialog_cache_outdated.DialogCacheOutdated(self.window_main,
  949.                                                       self.datadir)
  950.         res = d.run()
  951.     self.quit()
  952.     
  953.   def on_help_button(self, widget):
  954.     self.help_viewer.run()
  955.  
  956.   def on_button_add_cdrom_clicked(self, widget):
  957.     #print "on_button_add_cdrom_clicked()"
  958.  
  959.     # testing
  960.     #apt_pkg.Config.Set("APT::CDROM::Rename","true")
  961.  
  962.     saved_entry = apt_pkg.Config.Find("Dir::Etc::sourcelist")
  963.     tmp = tempfile.NamedTemporaryFile()
  964.     apt_pkg.Config.Set("Dir::Etc::sourcelist",tmp.name)
  965.     progress = GtkCdromProgress(self.datadir,self.window_main)
  966.     cdrom = apt_pkg.GetCdrom()
  967.     # if nothing was found just return
  968.     try:
  969.       res = cdrom.Add(progress)
  970.     except SystemError, msg:
  971.       #print "aiiiieeee, exception from cdrom.Add() [%s]" % msg
  972.       progress.close()
  973.       dialog = gtk.MessageDialog(parent=self.window_main,
  974.                                  flags=gtk.DIALOG_MODAL,
  975.                                  type=gtk.MESSAGE_ERROR,
  976.                                  buttons=gtk.BUTTONS_OK,
  977.                                  message_format=None)
  978.       dialog.set_markup(_("<big><b>Error scaning the CD</b></big>\n\n%s"%msg))
  979.       res = dialog.run()
  980.       dialog.destroy()
  981.       return
  982.     apt_pkg.Config.Set("Dir::Etc::sourcelist",saved_entry)
  983.     if res == False:
  984.       progress.close()
  985.       return
  986.     # read tmp file with source name (read only last line)
  987.     line = ""
  988.     for x in open(tmp.name):
  989.       line = x
  990.     if line != "":
  991.       full_path = "%s%s" % (apt_pkg.Config.FindDir("Dir::Etc"),saved_entry)
  992.       self.sourceslist.list.append(aptsources.SourceEntry(line,full_path))
  993.       self.reload_sourceslist()
  994.       self.modified = True
  995.  
  996.  # def on_channel_toggled(self, cell_toggle, path, store):
  997.  #     """Enable or disable the selected channel"""
  998.  #     iter = store.get_iter((int(path),))
  999.   #    source_entry = store.get_value(iter, LIST_ENTRY_OBJ)
  1000.   #    source_entry.disabled = not source_entry.disabled
  1001.   #    self.reload_sourceslist()
  1002.   #    self.modified = True
  1003.  
  1004. # FIXME: move this into a different file
  1005. class GtkCdromProgress(apt.progress.CdromProgress, SimpleGladeApp):
  1006.   def __init__(self,datadir, parent):
  1007.     SimpleGladeApp.__init__(self,
  1008.                             datadir+"glade/SoftwarePropertiesDialogs.glade",
  1009.                             "dialog_cdrom_progress",
  1010.                             domain="update-manager")
  1011.     self.dialog_cdrom_progress.show()
  1012.     self.dialog_cdrom_progress.set_transient_for(parent)
  1013.     self.parent = parent
  1014.     self.button_cdrom_close.set_sensitive(False)
  1015.   def close(self):
  1016.     self.dialog_cdrom_progress.hide()
  1017.   def on_button_cdrom_close_clicked(self, widget):
  1018.     self.close()
  1019.   def update(self, text, step):
  1020.     """ update is called regularly so that the gui can be redrawn """
  1021.     if step > 0:
  1022.       self.progressbar_cdrom.set_fraction(step/float(self.totalSteps))
  1023.       if step == self.totalSteps:
  1024.         self.button_cdrom_close.set_sensitive(True)
  1025.     if text != "":
  1026.       self.label_cdrom.set_text(text)
  1027.     while gtk.events_pending():
  1028.       gtk.main_iteration()
  1029.   def askCdromName(self):
  1030.     dialog = gtk.MessageDialog(parent=self.dialog_cdrom_progress,
  1031.                                flags=gtk.DIALOG_MODAL,
  1032.                                type=gtk.MESSAGE_QUESTION,
  1033.                                buttons=gtk.BUTTONS_OK_CANCEL,
  1034.                                message_format=None)
  1035.     dialog.set_markup(_("Please enter a name for the disc"))
  1036.     entry = gtk.Entry()
  1037.     entry.show()
  1038.     dialog.vbox.pack_start(entry)
  1039.     res = dialog.run()
  1040.     dialog.destroy()
  1041.     if res == gtk.RESPONSE_OK:
  1042.       name = entry.get_text()
  1043.       return (True,name)
  1044.     return (False,"")
  1045.   def changeCdrom(self):
  1046.     dialog = gtk.MessageDialog(parent=self.dialog_cdrom_progress,
  1047.                                flags=gtk.DIALOG_MODAL,
  1048.                                type=gtk.MESSAGE_QUESTION,
  1049.                                buttons=gtk.BUTTONS_OK_CANCEL,
  1050.                                message_format=None)
  1051.     dialog.set_markup(_("Please insert a disc in the drive:"))
  1052.     res = dialog.run()
  1053.     dialog.destroy()
  1054.     if res == gtk.RESPONSE_OK:
  1055.       return True
  1056.     return False
  1057.   
  1058.     
  1059.