#!/usr/bin/python2.4
# gnome-software-properties.in - edit /etc/apt/sources.list
#  
#  Copyright (c) 2004 Canonical
#                2004 Michiel Sikkes
#  
#  Author: Michiel Sikkes <michiel@eyesopened.nl>
#          Michael Vogt <mvo@debian.org>
# 
#  This program is free software; you can redistribute it and/or 
#  modify it under the terms of the GNU General Public License as 
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
# 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
# 
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import pygtk
import sys
pygtk.require('2.0')
import gtk
import gtk.glade
import gconf
import gobject
import os
import gettext
import re
import string
import time
import tempfile
import subprocess
from optparse import OptionParser
import apt_pkg
        
import SoftwareProperties.aptsources as aptsources
import SoftwareProperties.dialog_add as dialog_add
import SoftwareProperties.dialog_edit as dialog_edit
import SoftwareProperties.dialog_apt_key as dialog_apt_key
import SoftwareProperties.dialog_settings as dialog_settings 
import shutil


(LIST_MARKUP, LIST_ENABLED, LIST_ENTRY_OBJ) = range(3)

class SoftwareConfigurator:
    
  def on_button_ok_clicked(self, widget, data):
    #self.save_periodic_config(periodicAptConfFile)
    
    location = "/etc/apt/sources.list"
    #backup first
    shutil.copy(location,location+".save")
    # write
    self.sourceslist.save(location)

    # write the source.list first, even if dirty=False, because
    # e.g. CD-ROM add does not set dirty variable even if it adds
    # something to the sources.list (but no reload needed)
    if self.dirty == False:
      gtk.main_quit()
      sys.exit(0)
    
    primary = "<span weight=\"bold\" size=\"larger\">" + _("Repositories "
              "changed") + "</span>"
    secondary = _("The repository information has changes. A backup copy of "
                  "your sources.list is stored in %s.save. "
                  "\n\n"
                  "You need to reload the package list from the servers "
                  "for your changes to take effect. Do you want to do this "
                  "now?") % location
    dialog = gtk.MessageDialog(self.main_window,gtk.DIALOG_MODAL,
                               gtk.MESSAGE_INFO,gtk.BUTTONS_YES_NO,"")
    dialog.set_markup(primary);
    dialog.format_secondary_text(secondary);
    #textview = gtk.TextView()
    #textview.set_editable(gtk.FALSE)
    #textbuffer = textview.get_buffer()
    #f = os.popen("/usr/bin/diff -u %s.save %s" % (location,location))
    #diff = f.read()
    #textbuffer.set_text(diff)
    #if f.close() != None:
    #  win = gtk.ScrolledWindow()
    #  win.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
    #  win.add(textview)
    #  win.set_size_request(400,300)
    #  win.show()
    #  dialog.vbox.pack_start(child = win, padding = 6)
    #  textview.show()
    res = dialog.run()
    dialog.destroy()
    if res == gtk.RESPONSE_YES:
      self.main_window.hide()
      # we are in "no-update" mode, this is used when the calling application
      # wants to deal with this itself
      if options.no_update:
        gtk.main_quit()
        sys.exit(1)
      child = subprocess.Popen(["/usr/sbin/synaptic", "--update-at-startup",
                                "--hide-main-window","--non-interactive"],
                               close_fds=True)
      # wait for the child to finish
      while child.poll() == None:
        time.sleep(0.05)
        while gtk.events_pending():
          gtk.main_iteration()
        gtk.main_quit()
        sys.exit(1)
    gtk.main_quit()
    sys.exit(0)

  def on_button_edit_clicked(self, widget, data):
    selection = self.sourceslist_view.get_selection()
    (model, iter) = selection.get_selected()
    source_entry = model.get_value(iter, LIST_ENTRY_OBJ)
    edit = dialog_edit.dialog_edit(self.main_window,self.sourceslist,
                                   source_entry)
    if edit.run() == gtk.RESPONSE_OK:
      self.reloadsources(self.sourceslist,self.matcher)
      self.dirty = True

  def on_sourceslist_selection_changed(self, selection, data):
    (model, iter) = selection.get_selected()
    if iter != None:
      self.button_del.set_sensitive(True)
      self.button_edit.set_sensitive(True)
    else:
      self.button_del.set_sensitive(False)
      self.button_edit.set_sensitive(False)

  #def on_sourceslist_row_activated(self, treeview, path, column, data):
  #  #print "on_row_activated()"
  #  model = treeview.get_model()
  #  source_entry = model.get_value(model.get_iter(path), LIST_ENTRY_OBJ)
  #  edit = dialog_edit.dialog_edit(self.main_window,self.sourceslist,
  #                                 source_entry)
  #  edit.run()
  #  self.reloadsources(self.sourceslist,self.matcher)

  def on_button_remove_clicked(self, widget, data):
    selection = self.sourceslist_view.get_selection()
    (model,a_iter) = selection.get_selected()
    if a_iter == None:
      return
    source = model.get_value(a_iter, LIST_ENTRY_OBJ)
    self.sourceslist.remove(source)
    self.reloadsources(self.sourceslist,self.matcher)
    self.dirty=True

  def on_button_add_clicked(self, widget, data):
    add = dialog_add.dialog_add(self.main_window,self.sourceslist)
    if add.run() == gtk.RESPONSE_OK:
      self.reloadsources(self.sourceslist,self.matcher)
      self.dirty=True

  def on_button_settings_clicked(self, widget, data):
    settings = dialog_settings.dialog_settings(self.main_window, self.gladexml)
    settings.run()
    self.show_disabled = self.gconfclient.get_bool("/apps/gnome-software-properties/show_disabled")
    self.c_enabled.set_property("visible", self.show_disabled)
    self.reloadsources(self.sourceslist, self.matcher)

  def on_button_add_cdrom_clicked(self, widget, data):
    tmp = tempfile.NamedTemporaryFile()
    cmd = ["/usr/sbin/synaptic", "--hide-main-window",  "--non-interactive",
           "-o","Dir::Etc::sourcelist=%s" % tmp.name,"--ask-cdrom" ]
    self.main_window.set_sensitive(False)
    proc = subprocess.Popen(cmd)
    # wait for process to finish
    while proc.poll() == None:
      while gtk.events_pending():
        gtk.main_iteration()
      time.sleep(0.05)
    self.main_window.set_sensitive(True)
    # read tmp file with source name
    line = ""
    for x in open(tmp.name):
      line = x
    if line != "":
      self.sourceslist.list.append(aptsources.SourceEntry(line))
      self.reloadsources(self.sourceslist,self.matcher)
      #self.dirty=True # no need here

  def on_button_authentication_clicked(self, widget, data):
    auth = dialog_apt_key.dialog_apt_key(self.main_window)
    auth.run()

  def reloadsources(self, sourceslist, matcher):
    #self.meta = meta_data()
    self.sourcesstore.clear()
    for source in sourceslist.list:
      if source.invalid or (source.disabled and not self.show_disabled):
        continue
      (a_type,dist,comps) = matcher.match(source)
      contents = ""
      if source.comment != "":
        contents += "<i>%s</i>\n\n" % (source.comment)
      contents +="<big><b>%s </b></big> (%s) <small>\n%s</small>" % (dist,a_type, comps)
      iter = self.sourcesstore.append([contents, not source.disabled, source])

  def show_help(self, widget, data):
    print "self.show_help() called"

  # toggled on/off a source in the listview
  def toggled_enabled(self, renderer, path_string):
    iter = self.sourcesstore.get_iter_from_string(path_string)
    source = self.sourcesstore.get_value(iter, LIST_ENTRY_OBJ)
    self.dirty=True
    if self.sourcesstore.get_value(iter, LIST_ENABLED):
      self.sourcesstore.set_value(iter, LIST_ENABLED, False)
      source.set_enabled(False)
    else:
      self.sourcesstore.set_value(iter, LIST_ENABLED, True)
      source.set_enabled(True)

  def __init__(self):
    self.gconfclient = gconf.client_get_default()

    if os.path.exists("../data/gnome-software-properties.glade"):
      self.gladexml = gtk.glade.XML("../data/gnome-software-properties.glade")
    else:
      self.gladexml = gtk.glade.XML("/usr/share/update-manager/glade/gnome-software-properties.glade")

    # do we show disabled sources?
    self.show_disabled = self.gconfclient.get_bool("/apps/gnome-software-properties/show_disabled")
    self.main_window = self.gladexml.get_widget("SoftwareConfigurator")
    # button on the right
    self.gladexml.signal_connect("on_button_edit_clicked",
                                 self.on_button_edit_clicked, None)
    self.button_edit = self.gladexml.get_widget("button_edit")
    self.button_del = self.gladexml.get_widget("button_remove")

    # Gets the treeview and creates a store for it.
    self.sourceslist_view = self.gladexml.get_widget("sourceslist")
    self.sourcesstore = gtk.ListStore(str, bool,gobject.TYPE_PYOBJECT)
    self.sourceslist_view.set_model(self.sourcesstore)
    #self.gladexml.signal_connect("on_sourceslist_row_activated",
    #                             self.on_sourceslist_row_activated, None)
    self.sourceslist_view.get_selection().connect("changed", self.on_sourceslist_selection_changed, None)

    # was something modified
    self.dirty=False

    # Create columns and append them.
    cr = gtk.CellRendererToggle()
    cr.set_property("activatable", True)
    cr.set_property("xpad", 10)
    cr.set_property("ypad", 10)
    cr.connect("toggled", self.toggled_enabled)
    self.c_enabled = gtk.TreeViewColumn("Enabled", cr, active=LIST_ENABLED)
    self.sourceslist_view.append_column(self.c_enabled)
    self.c_enabled.set_property("visible", self.show_disabled)

    tr = gtk.CellRendererText()
    tr.set_property("xpad", 10)
    tr.set_property("ypad", 10)
    c0 = gtk.TreeViewColumn("Entry", tr, markup=LIST_MARKUP)
    self.sourceslist_view.append_column(c0)

    self.sourceslist = aptsources.SourcesList("/etc/apt/sources.list")
    self.matcher = aptsources.SourceEntryMatcher()
    # Empty and fill the sources store.
    self.reloadsources(self.sourceslist,self.matcher)

    self.main_window = self.gladexml.get_widget("SoftwareConfigurator")
    self.main_window.connect("delete_event", lambda widget,ev: gtk.main_quit())

    okbutton = self.gladexml.get_widget("button_ok")
    okbutton.connect("clicked", self.on_button_ok_clicked, None)

    cancelbutton = self.gladexml.get_widget("button_cancel")
    cancelbutton.connect("clicked", lambda w,v: gtk.main_quit(), None)

    self.gladexml.signal_connect("on_button_add_clicked", self.on_button_add_clicked, None)
    self.gladexml.signal_connect("on_button_add_cdrom_clicked", self.on_button_add_cdrom_clicked, None)
    self.gladexml.signal_connect("on_button_remove_clicked", self.on_button_remove_clicked, None)
    self.gladexml.signal_connect("on_button_authentication_clicked", self.on_button_authentication_clicked, None)

    # settings
    self.gladexml.signal_connect("on_button_settings_clicked", self.on_button_settings_clicked, None)

    self.main_window.show()
    if options.toplevel != None:
      # don't show the add-cdrom button for now
      # FIXME: on the long run interface with apt-pkg/cdrom.h
      b = self.gladexml.get_widget("button_add_cdrom")
      b.hide()
      toplevel = gtk.gdk.window_foreign_new(int(options.toplevel))
      self.main_window.window.set_transient_for(toplevel)

      
    # init the config
    apt_pkg.InitConfig()

  def main(self):
    gtk.main()


if __name__ == "__main__":
  APP="update-manager"
  DIR="/usr/share/locale"
  gettext.bindtextdomain(APP, DIR)
  gettext.textdomain(APP)
  gtk.glade.bindtextdomain(APP, DIR)
  gtk.glade.textdomain(APP)
  _ = gettext.gettext
  #print "Software Configurator started..."

  # add option parser
  parser = OptionParser()
  parser.add_option("-n", "--no-update", action="store_true",
                    dest="no_update", default=False,
                    help="No update on repository change (usefull if called "\
                         "from a external program).")
  parser.add_option("-t", "--toplevel", 
                    action="store", type="string", dest="toplevel",
                    help="Set x-window-id of the toplevel parent for the "\
                         "dialog (usefull for embedding)")
  

  (options, args) = parser.parse_args()
  ui = SoftwareConfigurator()
  ui.main()
  #print "Software Configurator ended..."
