#!/usr/bin/python

## system-config-printer
## Queue tree window implementation

## Copyright (C) 2001-2003 Red Hat, Inc.
## Copyright (C) 2002-2003 Tim Waugh <twaugh@redhat.com>

## 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., 675 Mass Ave, Cambridge, MA 02139, USA.

import gettext
import sys
import time
if not "/usr/share/printconf/util" in sys.path:
    sys.path.append ("/usr/share/printconf/util")

domain = 'printconf'
from rhpl.translate import _, N_
import rhpl.translate as translate
translate.textdomain (domain)

def help_message ():
    print """usage: system-config-printer [OPTIONS]

Options:
 --add-with-url URL  Add a new printer described by URL
 --help              Display this usage message"""

import getopt
try:
    options, args = getopt.getopt (sys.argv[1:], '', ['add-with-url=', 'help'])
except getopt.error:
    help_message ()
    sys.exit (1)

add_with_url = None
for each in options:
    if each[0] == '--help':
        help_message ()
        sys.exit (0)

    if each[0] == '--add-with-url':
        add_with_url = each[1]

import printconf_conf

try:
    printconf_conf.init_queue_edit_or_die ()
except IOError:
    sys.stderr.write(_("You must be root to run this program.\n"))
    sys.exit (1)

import errno
import os
import signal

# GUI imports
import gnome.ui
import gtk
import gtk.glade
import gtkhtml2
import gobject

# Other modules
import cups_import
import addQueue
import editQueue
import shareQueue
import printconf_version
import pycups

gtk.glade.bindtextdomain (domain)
gnome.program_init ('system-config-printer', printconf_version.version)
pkgdata = "/usr/share/printconf/"

try:
    xml = gtk.glade.XML (pkgdata + "gui/system-config-printer.glade",
                         domain = domain)
except:
    xml = gtk.glade.XML ("system-config-printer.glade", domain = domain)

busy_cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
ready_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)

def complain (window, msg):
    """Put up an error dialog."""
    d = gtk.MessageDialog (window, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
    d.set_transient_for (window)
    d.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
    d.run ()
    d.destroy ()

class queueTree:
    """The main queue tree window."""

    def __init__ (self, hidden = 0):
        self.hidden = hidden
        self.queue_types = [ printconf_conf.queue_types.local,
                             printconf_conf.queue_types.ipp,
                             printconf_conf.queue_types.lpd,
                             printconf_conf.queue_types.smb,
                             printconf_conf.queue_types.ncp,
                             printconf_conf.queue_types.jetdirect ]
        self.smb_page_index = self.queue_types.\
                              index (printconf_conf.queue_types.smb)
        self.queue_type_help_pages = [ "printconf-local-printer.html",
                                       "printconf-ipp-printer.html",
                                       "printconf-lpd-printer.html",
                                       "printconf-smb-printer.html",
                                       "printconf-ncp-printer.html",
                                       "printconf-jetdirect-printer.html" ]
        self.queue_type_names = map (lambda x: x.type_name, self.queue_types)

        # Initializing foomatic is expensive, so just do it on demand.
        self.foomatic_ready = 0

	self.default_iter = None
        self.conf = printconf_conf
        self.toplevel = xml.get_widget ('queueTreeWindow')
        self.toplevel.set_title (_("Printer configuration - %s") %
                                 (os.uname()[1]))
        if not hidden:
            self.toplevel.show_all ()

        imported = gtk.FALSE
        if cups_import.import_needed ():
            self.use_foomatic ()
            cups_import.import_cups_queues ()
            imported = gtk.TRUE

        # Widgets.
        self.local_queue_widgets = [ xml.get_widget ('edit_button'),
                                     xml.get_widget ('delete_button'),
                                     xml.get_widget ('edit_queue'),
                                     xml.get_widget ('delete_queue'),
                                     xml.get_widget ('pop_edit'),
                                     xml.get_widget ('pop_delete'),
                                     xml.get_widget ('pop_sharing') ]
        self.existing_queue_widgets = [ xml.get_widget ('default_button'),
                                        xml.get_widget ('set_as_default'),
                                        xml.get_widget ('pop_set_as_default') ]

        self.cups_test = xml.get_widget ('cups_test')
        if not os.access ("/usr/share/cups/data/testprint.ps", os.R_OK):
            self.cups_test.set_sensitive (gtk.FALSE)

        self.jpeg_test = xml.get_widget ('jpeg_test')
        if not os.access ("/usr/share/gimp-print/samples/profile.jpg",os.R_OK):
            self.jpeg_test.set_sensitive (gtk.FALSE)

        # Grey out these buttons until a queue is chosen.
        map (lambda x: x.set_sensitive (gtk.FALSE),
             self.local_queue_widgets)
        map (lambda x: x.set_sensitive (gtk.FALSE),
             self.existing_queue_widgets)

        # Signals.
        self.toplevel.connect ('destroy', self.destroy)
        xml.signal_connect ('on_quit_activate', self.quit)
        xml.signal_connect ('on_new_button_clicked', self.new_button_clicked)
        xml.signal_connect ('on_new_queue_activate', self.new_button_clicked)
        xml.signal_connect ('on_queue_view_cursor_changed', self.queue_chosen)
        xml.signal_connect ('on_queue_view_button_press_event',
                            self.button_pressed)
        xml.signal_connect ('on_edit_button_clicked', self.edit_button_clicked)
        xml.signal_connect ('on_edit_queue_activate', self.edit_button_clicked)
        xml.signal_connect ('on_pop_edit_activate', self.edit_button_clicked)
        xml.signal_connect ('on_queue_view_row_activated', self.row_activated)
        xml.signal_connect ('on_queue_view_row_expanded', self.row_expanded)
        xml.signal_connect ('on_queue_view_row_collapsed', self.row_collapsed)
        xml.signal_connect ('on_default_button_clicked',
                            self.default_button_clicked)
        xml.signal_connect ('on_set_as_default_activate',
                            self.default_button_clicked)
        xml.signal_connect ('on_pop_set_as_default_activate',
                            self.default_button_clicked)
        xml.signal_connect ('on_delete_button_clicked',
                            self.delete_button_clicked)
        xml.signal_connect ('on_delete_queue_activate',
                            self.delete_button_clicked)
        xml.signal_connect ('on_pop_delete_activate',
                            self.delete_button_clicked)
        xml.signal_connect ('on_apply_activate', self.apply)
        xml.signal_connect ('on_apply_button_clicked', self.apply)
        xml.signal_connect ('on_sharing_activate', self.sharing_button_clicked)
        xml.signal_connect ('on_pop_sharing_activate',
                            self.sharing_button_clicked)
        xml.signal_connect ('on_import_ppd_activate',
                            self.import_ppd_activate)
        xml.signal_connect ('on_queueTreeWindow_delete_event', self.delete)
        xml.signal_connect ('on_browse_help_activate', self.browse_help)
        for test in [ 'on_cups_test_activate',
                      'on_letter_ps_test_activate',
                      'on_a4_ps_test_activate',
                      'on_ascii_test_activate',
                      'on_euc_test_activate',
                      'on_jis_test_activate',
                      'on_sjis_test_activate',
                      'on_euc_ps_test_activate',
                      'on_jis_ps_test_activate',
                      'on_sjis_ps_test_activate',
                      'on_duplex_test_activate',
                      'on_jpeg_test_activate' ]:
            xml.signal_connect (test, self.test)

        # TreeView columns.
        self.queue_store = gtk.TreeStore (gtk.gdk.Pixbuf, str, str,
                                          gobject.TYPE_PYOBJECT,
                                          gtk.gdk.Pixbuf)
        self.queue_store.set_sort_column_id (1, gtk.SORT_ASCENDING)
        self.queue_view = xml.get_widget ('queue_view')
        self.queue_view.set_model (self.queue_store)

        col = gtk.TreeViewColumn (_("Queue name"), gtk.CellRendererText (),
                                  text=1)
        col.set_resizable (gtk.TRUE)
        col.set_sort_column_id (1)
        self.queue_view.append_column (col)

        col = gtk.TreeViewColumn (_("Shared"), gtk.CellRendererPixbuf (),
                                  pixbuf=4)
        self.queue_view.append_column (col)

        col = gtk.TreeViewColumn (_("Default"), gtk.CellRendererPixbuf (),
                                  pixbuf=0)
        self.queue_view.append_column (col)

        col = gtk.TreeViewColumn (_("Description"), gtk.CellRendererText (),
                                  text=2)
        col.set_resizable (gtk.TRUE)
        col.set_sort_column_id (2)
        self.queue_view.append_column (col)

        # Queue status pixbufs
        self.pixbuf = {}
        for (test, name) in [ ("printer-D.png", "printer-default"),
                              ("printer-DS.png", "printer-default-static"),
                              ("printer-DO.png", "printer-default-overridden"),
                              ("printer-S.png", "printer-static"),
                              ("printer-O.png", "printer-overridden"),
                              ("printer-shared.png", "printer-shared"),
                              ("printer-unshared.png", "printer-unshared")]:
            self.pixbuf[name] = gtk.gdk.pixbuf_new_from_file ("%s/gui/%s" %
                                                              (pkgdata, test))
        self.pixbuf["empty"] = self.pixbuf["printer-default"].copy ()
        self.pixbuf["empty"].fill (0)

        # Other modules.
        self.addQueue = addQueue.addQueue (self, xml)
        self.editQueue = editQueue.editQueue (self, xml)
        self.shareQueue = shareQueue.shareQueue (self, xml)

        # Find out what print spooler is currently active, so that
        # we can warn if the user is changing an option that has
        # no effect.
        self.active_spooler = self.get_active_spooler ()

        # Put the queues in the tree view.
        self.populate_tree ()

        # Whether we need to run the backend.
        self.need_to_apply_changes (imported)

        if not hidden:
            # Let GTK take over.
            gtk.mainloop ()

    def get_active_spooler (self):
        """Find out which print spooler is currently active."""
        signal.signal (signal.SIGCHLD, signal.SIG_DFL)
        f = os.popen ('/usr/sbin/alternatives --display print')
        for l in f.readlines ():
            if l.startswith (" link currently points to"):
                which = l.split ('.')[1].strip ()
                break

        f.close ()
        return which

#--------------------------------
# Handle the window being deleted
#--------------------------------
    def delete (self, *args):
        """Callback for the window being deleted.  Ask for confirmation."""
        if self.need_apply:
            sure = gtk.MessageDialog \
                   (self.toplevel, 0, gtk.MESSAGE_QUESTION,
                    gtk.BUTTONS_NONE,
                    _("Do you want to save the changes\n"
                      "you made to your printer configuration?"))
            sure.add_button (_("_Don't save"), gtk.RESPONSE_NO)
            sure.add_button (_("_Cancel"), gtk.RESPONSE_CANCEL)
            sure.add_button (_("_Save"), gtk.RESPONSE_YES)
            response = sure.run ()
            sure.destroy ()

            if (response == gtk.RESPONSE_CANCEL or
                response == gtk.RESPONSE_DELETE_EVENT):
                return gtk.TRUE

            if response == gtk.RESPONSE_YES:
                self.apply ()

        return gtk.FALSE

    def destroy (self, *args):
        gtk.mainquit ()

    def quit (self, *args):
        if not self.delete ():
            self.destroy ()

#----------------------------
# Populate the queue treeview
#----------------------------
    def populate_tree (self):
        """Fill in the queue treeview from the existing configuration."""
        self.name_dict, self.alias_dict = self.conf.get_queues ()
        self.default_queue_name = self.conf.get_default_queue_name ()

        store = self.queue_store
        store.clear ()
        for name in self.name_dict.keys ():
            queue_dict = self.name_dict[name]
            queue = queue_dict["queue"]
            iter = store.append (None)
            store.set_value (iter, 1, name)

            try:
                store.set_value (iter, 2, queue["queue_description"].value)
            except:
                pass

            store.set_value (iter, 3, queue)

            shared = "printer-unshared"
            try:
                for allowed in queue["sharing"]:
                    shared = "printer-shared"
                    break
            except:
                pass

            self.queue_store.set_value (iter, 4, self.pixbuf[shared])

            if name == self.default_queue_name:
                pixbuf = "printer-default"
                if not queue_dict["editable"]:
                    pixbuf = "printer-default-static"
                elif queue_dict["override"]:
                    pixbuf = "printer-default-overridden"

                self.queue_store.set_value (iter, 0, self.pixbuf[pixbuf])
                self.default_iter = iter
            else:
                if not queue_dict["editable"]:
                    self.queue_store.set_value (iter, 0,
                                                self.pixbuf["printer-static"])
                elif queue_dict["override"]:
                    self.queue_store.set_value (iter, 0,
                                                self.pixbuf["printer-overridden"])

        self.browsed_iter = None
        self.populate_browsed_queues ()

    def populate_browsed_queues (self):
        """Update the list of browsed queues.  Also displays error messages
        for any queue that has them."""
        if self.active_spooler != "cups":
            return

        try:
            cups_queues = pycups.get_queues ()
        except:
            return

	if not cups_queues:
		return

        store = self.queue_store
        if self.browsed_iter:
            while store.iter_has_child (self.browsed_iter):
                iter = store.iter_nth_child (self.browsed_iter, 0)
                store.remove (iter)

        errors = {}
        for each in cups_queues.keys ():
            try:
                if cups_queues[each]['printer-state-message']:
                    err = cups_queues[each]['printer-state-message']

                    if self.name_dict.has_key (each):
                        for row in store:
                            i = store.get_iter (row.path)
                            name = store.get_value (i, 1)
                            if name == each:
                                # Ought to be a nicer way to do this.
                                if err and err != "Ready to print.":
                                    errors[each] = err
                                    description = err
                                else:
                                    description = self.name_dict[name]\
                                                  ["queue"]\
                                                  ["queue_description"].value

                                store.set_value (i, 2, description)
                                break

                # If this is one of our local queues, skip it.
                if each in self.name_dict.keys ():
                    continue
            except:
                continue

            if self.name_dict.has_key (each):
                continue

            if not self.browsed_iter:
                self.browsed_iter = store.append (None)
                store.set_value (self.browsed_iter, 0, self.pixbuf["empty"])
                store.set_value (self.browsed_iter, 1, _("Browsed queues"))
                store.set_value (self.browsed_iter, 2, "")

            iter = store.append (self.browsed_iter)
            pixbuf = "empty"
            if each == self.default_queue_name:
                pixbuf = "printer-default"
            store.set_value (iter, 0, self.pixbuf[pixbuf])
            store.set_value (iter, 1, each)
            store.set_value (iter, 2, "")
            try:
                store.set_value (iter, 2,
                                 cups_queues[each]['printer-location'])
            except:
                pass

            try:
		if errors[each]:
                    store.set_value (iter, 2, errors[each])
	    except:
		pass

        self.errors = errors

#----------------------------
# Greying-out the main window
#----------------------------
    def busy (self):
        """Set the main window insensitive."""
	try:
            self.toplevel.set_sensitive (gtk.FALSE)
            self.toplevel.window.set_cursor (busy_cursor)
            while gtk.events_pending ():
                gtk.mainiteration ()
        except:
            pass

    def ready (self):
        """Set the main window sensitive."""
        try:
            self.toplevel.window.set_cursor (ready_cursor)
            self.toplevel.set_sensitive (gtk.TRUE)
        except:
            pass

#-------------------------------------
# Helper functions used by this module
#-------------------------------------
    def use_foomatic (self):
        """Demand-load the foomatic database.  If it is already loaded,
        this does nothing; otherwise it is loaded."""
        if not self.foomatic_ready:
            wait = xml.get_widget ('waitDialog')
            wait_label = xml.get_widget ('wait_label')
            wait_label.set_text (_("Loading printer information.\n"
                                   "Please wait..."))
            wait.set_transient_for (self.toplevel)
            if self.hidden:
                wait.set_position (gtk.WIN_POS_CENTER)
            else:
                wait.set_position (gtk.WIN_POS_CENTER_ON_PARENT)

            wait.show_now ()
            gtk.gdk.window_process_all_updates ()
            while gtk.events_pending ():
                gtk.mainiteration ()
            # Should get a thread to repaint this?

            self.conf.foomatic_init_overview ()
            wait.hide ()
            self.foomatic_ready = 1

    def need_to_apply_changes (self, true):
        """Called to set button sensitivity.  If true is zero everything
        is up to date, otherwise changes have been made that have not been
        applied yet."""
        if true:
            self.need_apply = 1
        else:
            self.need_apply = 0

        xml.get_widget ('apply_button').set_sensitive (true)
        xml.get_widget ('apply').set_sensitive (true)

        store, iter = self.queue_view.get_selection ().get_selected ()
        tests_available = not true
        if not iter or store.iter_has_child (iter):
            tests_available = gtk.FALSE

        xml.get_widget ('test').set_sensitive (tests_available)

    def queue_chosen (self, treeview):
        """Called when a queue has been chosen.  All of the widgets that
        act on an existing queue are made sensitive again."""
        store, iter = treeview.get_selection ().get_selected ()
	is_normal = self.is_normal_queue (iter)
        map (lambda x: x.set_sensitive (is_normal),
             self.existing_queue_widgets)
        is_local = self.is_local_queue (iter)
        map (lambda x: x.set_sensitive (is_local),
             self.local_queue_widgets)

        xml.get_widget ('test').set_sensitive (not self.need_apply and
                                               not store.iter_has_child (iter))

	if is_local:
            self.populate_browsed_queues ()

    def make_queue_editable (self, name):
        """Called to make an imported queue editable.  Returns zero
        on success."""
        override = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_INFO,
                                      gtk.BUTTONS_YES_NO,
                                      _("This is an imported printer. "
                                        "Do you want to create a local "
                                        "override?"))
        response = override.run ()
        override.destroy ()
        if response != gtk.RESPONSE_YES:
            return 1

        self.conf.override_queue (name)
        pixbuf = "printer-overridden"
        if name == self.default_queue_name:
            pixbuf = "printer-default-overridden"

        store.set_value (iter, 0, self.pixbuf[pixbuf])
        return 0

    def is_local_queue (self, iter):
        store = self.queue_store
        if store.get_value (iter, 3):
            return 1
        return 0

    def is_normal_queue (self, iter):
        store = self.queue_store
        return not store.iter_has_child (iter)

#-------------------
# Create a new queue
#-------------------
    def new_button_clicked (self, *args):
        """New button handler."""
        self.busy ()
        self.use_foomatic ()
        self.addQueue.addQueueDruid ()
        self.populate_browsed_queues ()

#-------------
# Edit a queue
#-------------
    def edit_button_clicked (self, *args):
        """Edit button handler."""
        store, iter = self.queue_view.get_selection ().get_selected ()
        if not self.is_normal_queue (iter):
            return

        name = store.get_value (iter, 1)
        queue_dict = self.name_dict[name]
        if not queue_dict["editable"]:
            if self.make_queue_editable (name):
                return

        self.busy ()
        self.use_foomatic ()
        if self.editQueue.editQueueDialog (iter):
            self.need_to_apply_changes (gtk.TRUE)
            self.name_dict, self.alias_dict = self.conf.get_queues ()

        self.populate_browsed_queues ()

#---------------------
# Double-click handler
#---------------------
    def row_activated (self, view, path, column):
        """Handle double-clicks in the tree view.  In the status column
        treat a double-click as 'set as default'; otherwise edit the
        queue."""
        store = self.queue_store
        iter = store.get_iter (path)
        if not self.is_local_queue (iter) and not store.iter_depth (iter):
            if view.row_expanded (path):
                view.collapse_row (path)
            else:
                self.populate_browsed_queues ()
                view.expand_row (path, 0)

            return

        if column == view.get_column (1):
            self.sharing_button_clicked ()
        elif column == view.get_column (2):
            self.default_button_clicked ()
        else:
            if store.iter_depth (iter) > 0:
                # Double-clicking on a browsed queue could bring
                # up a box with information about it in I suppose.
                return

            self.edit_button_clicked ()

    def row_expanded (self, view, iter, path):
        try:
            if self.expanding_row:
                return
        except:
            self.expanding_row = 1

        self.populate_browsed_queues ()
        view.expand_row (path, 0)
        del self.expanding_row

    def row_collapsed (self, view, iter, path):
        # Set button sensitivity
        self.need_to_apply_changes (self.need_apply)

#---------------------------
# Set a queue as the default
#---------------------------
    def default_button_clicked (self, *args):
        """Default button callback."""
        store, iter = self.queue_view.get_selection ().get_selected ()
        name = store.get_value (iter, 1)
        if name == self.default_queue_name:
            # Nothing to do.
            return

        self.conf.set_default_queue_name (name)
        store.set_value (iter, 0, self.pixbuf["printer-default"])
        if self.default_iter:
            store.set_value (self.default_iter, 0, self.pixbuf["empty"])
        self.default_queue_name = name
        self.default_iter = iter
        self.need_to_apply_changes (gtk.TRUE)
        if self.is_local_queue (iter):
            self.populate_browsed_queues ()

#---------------------
# Queue sharing dialog
#---------------------
    def sharing_button_clicked (self, *args):
        """Sharing button callback."""
        self.spooler_warn_unless ("cups", self.toplevel)

        store, iter = self.queue_view.get_selection ().get_selected ()
        if iter:
            if self.is_normal_queue (iter):
                name = store.get_value (iter, 1)
                queue_dict = self.name_dict[name]
                if not queue_dict["editable"]:
                    if self.make_queue_editable (name):
                        return
            else:
                iter = None

        if self.shareQueue.shareQueueDialog (iter):
            self.need_to_apply_changes (gtk.TRUE)

        self.populate_browsed_queues ()

        # Fix up the sharing icon.
        shared = "printer-unshared"
        try:
            for allowed in self.name_dict[name]["queue"]["sharing"]:
                shared = "printer-shared"
                break
        except:
            pass

        self.queue_store.set_value (iter, 4, self.pixbuf[shared])

#---------------
# Delete a queue
#---------------
    def delete_button_clicked (self, *args):
        """Delete button callback."""
        store, iter = self.queue_view.get_selection ().get_selected ()
        if not self.is_normal_queue (iter):
            return

        name = store.get_value (iter, 1)
        self.conf.delete_queue_and_fix_default (self.name_dict[name]["queue"])
        self.name_dict, self.alias_dict = self.conf.get_queues ()
        self.default_queue_name = self.conf.get_default_queue_name ()
        store.remove (iter)

        # If it was an override queue, put in the non-editable queue.
        if self.name_dict.has_key (name):
            queue_dict = self.name_dict[name]
            iter = store.append (None)
            store.set_value (iter, 1, name)
            if name != self.default_queue_name:
                if not queue_dict["editable"]:
                    store.set_value (iter, 0, self.pixbuf["printer-static"])
                elif queue_dict["override"]:
                    store_set_value (iter, 0,
                                     self.pixbuf["printer-overridden"])

        self.fix_default_marker ()

        # Since the selected queue is deleted, nothing is selected now.
        # Grey out the buttons that act on an existing queue.
        map (lambda x: x.set_sensitive (gtk.FALSE),
             self.existing_queue_widgets)

        self.need_to_apply_changes (gtk.TRUE)
        self.populate_browsed_queues ()

    def fix_default_marker (self):
        """Fix up 'default' marker."""
        store = self.queue_store
        iter = store.get_iter_first ()
        while iter:
            if store.get_value (iter, 1) == self.default_queue_name:
                self.default_iter = iter
                store.set_value (iter, 0, self.pixbuf["printer-default"])
                break

            iter = store.iter_next (iter)

#---------------------------------------
# Helper functions used by other modules
#---------------------------------------
    def show_notes (self, id, driver, caller):
        """Show the printer/driver notes.  This is a helper function,
           used by the other modules."""
        caller.busy ()
        notes = self.conf.foomatic_printer_driver_lookup (id, driver)
        caller.ready ()

        message = _("Notes from the Linux Printing Database")
        source1 = '<HTML><HEAD><META CONTENT="text/html; charset=UTF-8" HTTP-EQUIV="Content-Type"><BODY BGCOLOR="white">\n'
        source1 += '<H3>' + _("Printer notes:") + '</H3>\n'
        source1 += notes.printer_comments_dict.get ("en", "")
        source1 += "</BODY></HTML>\n"

        source2 = '<HTML><HEAD><META CONTENT="text/html; charset=UTF-8" HTTP-EQUIV="Content-Type"><BODY BGCOLOR="white">\n'
        source2 += '<H3>' + _("Driver notes:") + '</H3>\n'
        source2 += notes.driver_comments_dict.get ("en", "")
        source2 += "</BODY></HTML>\n"

	dialog = gtk.MessageDialog (None, 0, gtk.MESSAGE_INFO,
				    gtk.BUTTONS_OK, message)

	html1 = gtkhtml2.Document ()
	html1.clear ()
	html1.open_stream ('text/html')
	html1.write_stream (source1)
	html1.close_stream ()
	view1 = gtkhtml2.View ()
	view1.set_document (html1)
        view1.set_size_request (400, 200)

        window1 = gtk.ScrolledWindow ()
	dialog.vbox.pack_start (window1)
        window1.add (view1)

	html2 = gtkhtml2.Document ()
	html2.clear ()
	html2.open_stream ('text/html')
	html2.write_stream (source2)
	html2.close_stream ()
	view2 = gtkhtml2.View ()
	view2.set_document (html2)
        view2.set_size_request (600, 200)

        window2 = gtk.ScrolledWindow ()
	dialog.vbox.pack_start (window2)
        window2.add (view2)

	dialog.set_position (gtk.WIN_POS_CENTER)
	dialog.vbox.show_all()
	dialog.run()
	dialog.hide()

    def populate_mfr_optionmenu (self, optionmenu, queue = None):
        """
        Populate optionmenu with a list of manufacturer's names.
        If queue is also given, the history is set to the appropriate
        manufacturer.  This is a helper function, used by other modules.

        Returns a list corresponding to the menu items.
        """

        mfr_list = self.conf.foomatic.make_model_dict_dict.keys ()

        # Put in alphanumeric order, except for Generic which should
        # come first.
        mfr_list.sort ()
        try:
            mfr_list.pop (mfr_list.index ("Generic"))
        except:
            pass
        mfr_list.insert (0, _("Generic (click to select manufacturer)"))

        mfr_menu = gtk.Menu ()
        for mfr in mfr_list:
            menuitem = gtk.MenuItem (mfr)
            mfr_menu.add (menuitem)
            menuitem.show ()
            menuitem.set_sensitive (gtk.TRUE)

        optionmenu.set_sensitive (gtk.TRUE)
        optionmenu.set_menu (mfr_menu)

        mfr_list[0] = "Generic"
        if (queue and queue["filter_type"].value ==
            self.conf.drivers.foomatic.filter_type and
            queue["filter_data"]["mf_type"].value ==
            self.conf.drivers.foomatic.mf_type):
            id = queue["filter_data"]["printer_id"].value
            mfr = self.conf.foomatic.id_dict[id].make
            optionmenu.set_history (mfr_list.index (mfr))    
        else:
            optionmenu.set_history (mfr_list.index ("Generic"))

        return mfr_list

    def populate_model_store (self, store, queue = None, id_dict = {},
                              mfr = None, id = None, window = None):
        """
        Set up the printer model tree.  This is a helper function,
        used by the other modules.

        store:            TreeStore to populate
        queue (optional): Current queue
        id_dict (opt):    Dictionary of foomatic IDs to populate
        mfr (opt):        Manufacturer to list models for
        id (opt):         Foomatic ID to select
        """

        if window:
            window.set_cursor (busy_cursor)
            while gtk.events_pending ():
                gtk.mainiteration ()

        selectable_iter = None
        select_id = id

        store.clear ()

        d = self.conf.drivers.foomatic
        if (queue and queue["filter_type"].value == d.filter_type and
            queue["filter_data"]["mf_type"].value == d.mf_type):
            select_id = queue["filter_data"]["printer_id"].value
            if not mfr:
                mfr = self.conf.foomatic.id_dict[select_id].make

        if id and not mfr:
            mfr = self.conf.foomatic.id_dict[id].make

        if not mfr:
            mfr = "Generic"

        if mfr == "Generic":
            d = self.conf.drivers.text
            iter = store.append (None)
            store.set_value (iter, 0, d.label)
            store.set_value (iter, 1, (d, None))
            if (queue and queue["filter_type"].value == d.filter_type and
                queue["filter_data"]["mf_type"].value == d.mf_type):
                selectable_iter = iter
                    
            d = self.conf.drivers.raw
            iter = store.append (None)
            store.set_value (iter, 0, d.label)
            store.set_value (iter, 1, (d, None))
            if queue and queue["filter_type"].value == d.filter_type:
                selectable_iter = iter

        d = self.conf.drivers.foomatic
        models = self.conf.foomatic.make_model_dict_dict[mfr].keys ()
        models.sort ()
        for model in models:
            iter_model = store.append (None)
            store.set_value (iter_model, 0, model)
            id = self.conf.foomatic.make_model_dict_dict[mfr][model].id
            store.set_value (iter_model, 1, (d, id))
            id_dict[id] = iter_model
            if id == select_id:
                selectable_iter = iter_model

        # Sort the printer models the way a real person would.
        def model_sort_func (store, iter_a, iter_b):
            return pycups.model_sort (store.get_value (iter_a, 0),
                                      store.get_value (iter_b, 0))

        store.set_sort_column_id (0, gtk.SORT_ASCENDING)
        store.set_sort_func (0, model_sort_func)

        if window:
            window.set_cursor (ready_cursor)

        return selectable_iter

    def spooler_warn_unless (self, spooler, window):
        """Warn that the option about to be modified doesn't apply
        to the active spooler."""
        if self.active_spooler == spooler:
            return

        msg = _("You are changing an option\n"
                "that has no effect in the print\n"
                "spooler you are using (%s).") % self.active_spooler
        d = gtk.MessageDialog (window, 0, gtk.MESSAGE_WARNING,
                               gtk.BUTTONS_OK, msg)
        d.set_position (gtk.WIN_POS_CENTER)
        d.run ()
        d.destroy ()

#--------------
# Apply changes
#--------------
    def apply (self, *args):
        """Save changes and restart lpd service."""

        self.busy ()
        wait = xml.get_widget ('waitDialog')
        wait_label = xml.get_widget ('wait_label')
        wait_label.set_text (_("Applying changes.\n"
                               "Please wait..."))
        wait.set_transient_for (self.toplevel)
        wait.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
        wait.show_now ()
        gtk.gdk.window_process_all_updates ()
        while gtk.events_pending ():
            gtk.mainiteration ()

        self.conf.save_queues ()
        success = self.conf.restart_lpd ()
        wait.hide ()
        self.ready ()

        if not success:
            bad = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_ERROR,
                                     gtk.BUTTONS_OK,
                                     _("Failed to write queues."))
            bad.run ()
            bad.destroy ()
        else:
            self.need_to_apply_changes (gtk.FALSE)

#-----------
# Run a test
#-----------
    def test (self, menuitem):
        """Send a test page to the print queue."""

        selection = self.queue_view.get_selection ()
        store, iter = selection.get_selected ()
        if store.iter_has_child (iter):
            return

        name = store.get_value (iter, 1)
        index = menuitem.get_parent ().get_children ().index (menuitem)
        page = [ "/usr/share/cups/data/testprint.ps",
                 "----separator----",
                 "testpage.ps",
                 "testpage-a4.ps",
                 "testpage.asc",
                 "testpage.euc",
                 "testpage.jis",
                 "testpage.sjis",
                 "testpage.big5",
                 "test-euc.ps",
                 "test-jis.ps",
                 "test-sjis.ps",
                 "test-big5.ps",
                 "duplex.asc",
                 "/usr/share/gimp-print/samples/profile.jpg" ][index]
        label = menuitem.get_children ()[0].get_label ().replace ("_", "")
        if page[0] == "/":
            testfile = page
        else:
            testfile = pkgdata + "tests/" + page

        (loglevel, logname, errorlog_start) = self.set_cups_loglevel ("debug")
	self.busy ()
	time.sleep (1)
	self.ready ()
        err = self.conf.print_test_page (name, testfile)
        if err:
            self.set_cups_loglevel (loglevel)
            bad = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_ERROR,
                                     gtk.BUTTONS_OK,
                                     _("There was a problem sending %s\n"
                                       "to '%s' queue:\n\n") % (label, name)
                                     + err)
            bad.run ()
            bad.destroy ()
        else:
            done = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_INFO,
                                      gtk.BUTTONS_YES_NO,
                                      _("Sent %s\n"
                                        "to '%s' queue.  Does it "
                                        "look okay?") % (label, name))
            response = done.run ()
            done.destroy ()
            (x, x, errorlog_end) = self.set_cups_loglevel (loglevel)

            if response == gtk.RESPONSE_NO:
                errorlog_size = errorlog_end - errorlog_start
                errorlog = file (logname)
                errorlog.seek (errorlog_start)
                data = errorlog.read (errorlog_size)
                errorlog.close ()
                dialog = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_INFO,
                                            gtk.BUTTONS_OK,
                                            _("Here are the messages that "
                                              "appeared in the error log:"))
                window = gtk.ScrolledWindow ()
                dialog.vbox.pack_start (window)
                textview = gtk.TextView ()
                buffer = gtk.TextBuffer ()
                buffer.set_text (data)
                textview.set_buffer (buffer)
                textview.set_editable (gtk.FALSE)
                textview.set_cursor_visible (gtk.FALSE)
                window.add (textview)
                dialog.show_all ()
                dialog.run ()
                dialog.destroy ()

	self.populate_browsed_queues ()

    def set_cups_loglevel (self, level):
        """Set the LogLevel setting in cupsd.conf, and return a tuple of
        the old setting, the error log file name, and its size."""
        if self.active_spooler != "cups":
            return ("", "/dev/null", 0)

        loglevel_keyword = "LogLevel "
        oldlevel = "info"
        errorlog_keyword = "ErrorLog "
        errorlog = "/var/log/cups/error_log"

        cupsd_conf_filename = "/etc/cups/cupsd.conf"
        try:
            cupsdconf = file (cupsd_conf_filename).readlines ()
        except:
            cupsdconf = []

        for i in range (len (cupsdconf)):
            line = cupsdconf[i]
            if line.startswith (loglevel_keyword):
                oldlevel = line[len (loglevel_keyword):].strip ()
                cupsdconf[i] = "LogLevel %s\n" % level

            if line.startswith (errorlog_keyword):
                errorlog = line[len (errorlog_keyword):].strip ()

	size = 0
	if level != "debug":
            try:
        	(x, x, x, x, x, x, size, x, x, x) = os.stat (errorlog)
            except:
        	pass

        # Don't adjust LogLevel for the time being; causes problems.
        if 0: #if level != oldlevel:
            tmp = cupsd_conf_filename + ".#system-config-printer"
            out = file (tmp, "w")
            out.writelines (cupsdconf)
            out.close ()
            try:
                os.rename (tmp, cupsd_conf_filename)
                os.system ("/sbin/service cups reload >/dev/null 2>/dev/null")
            except:
                pass

	if level == "debug":
            try:
        	(x, x, x, x, x, x, size, x, x, x) = os.stat (errorlog)
            except:
        	pass

        return (oldlevel, errorlog, size)

#------------
# Browse help
#------------
    def browse_help (self, menuitem):
        gnome.url_show ("file://%s/index.html" %
                        self.conf.conf.printconf_help_dir)

#------------
# Pop-up menu
#------------
    def button_pressed (self, view, event):
        """Activate the pop-up menu on right-click."""

        # Right-click?
        if event.button != 3:
            return

        popup = xml.get_widget ("queuePopup")
        popup.popup (None, None, None, event.button, event.time)

#------------
# Pop-up menu
#------------
    def import_ppd_activate (self, imgmenuitem):
        """Import PPD."""
        selector = gtk.FileSelection ()
        selector.set_select_multiple (gtk.FALSE)
        while gtk.TRUE:
            response = selector.run ()
            if response != gtk.RESPONSE_OK:
                print "Bye!"
                selector.destroy ()
                return

            filename = selector.get_filename ()

            try:
                f = file (filename)
                line1 = f.readline ()
                if not line1.startswith ("*PPD-Adobe:"):
                    complain (self.toplevel,
                              _("'%s' does not look like a PPD file.") %
                              filename)
                    selector.destroy ()
                    return

                break
            except IOError, e:
                if e.errno == errno.EISDIR:
                    d = os.path.dirname (filename + "/") + "/"
                    selector.complete (d)
            except:
                complain (self.toplevel,
                          _("File cannot be opened for reading."))
                selector.destroy ()
                return

        selector.destroy ()

        # Create a template printer XML
        def ppd_val (s):
            offset = s.find (':')
            if offset == -1:
                return ''
            return s[1 + offset:].strip ().strip ("\"'")

        mfr = None
        mdl = None
        for l in f.readlines ():
            if l.startswith ("*Manufacturer:"):
                mfr = ppd_val (l)
            elif l.startswith ("*ShortNickName:"):
                mdl = ppd_val (l) + " (PPD)"

        if not mfr:
            mfr = "[PPD]"

        if not mdl:
            mdl = "[PPD]"

        if mdl.startswith (mfr + ' '):
            mdl = mdl[1 + len (mfr):]

        n = 1
        while n != 0:
            id = "ppd%d" % n
            p = "/usr/share/foomatic/db/source/printer/%s.xml" % id
            if not os.path.exists (p):
                break

            n += 1

        file (p, 'w').writelines ([ '<printer id="printer/%s">' % id,
                                    '  <make>%s</make>' % mfr,
                                    '  <model>%s</model>' % mdl,
                                    '  <functionality>A</functionality>',
                                    '  <driver>ppd</driver>',
                                    '</printer>' ])

        os.environ["PPD"] = filename
        if os.system ('/usr/sbin/foomatic-ppdload "$PPD" %s' % id) == 0:
            d = gtk.MessageDialog (self.toplevel, 0, gtk.MESSAGE_INFO,
                                   gtk.BUTTONS_OK,
                                   _("PPD imported."))
            d.set_transient_for (self.toplevel)
            d.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
            d.run ()
            d.destroy ()
        else:
            complain (self.toplevel, _("PPD import failed."))

if add_with_url:
    if not add_with_url.startswith ("smb:"):
        print "Only smb: URLs are supported by --add-with-url at present."
        sys.exit (1)

    toplevel = queueTree (hidden = 1)
    toplevel.use_foomatic ()
    toplevel.addQueue.addQueueDruid (url = add_with_url)
    if toplevel.addQueue.window.get_property ("visible"):
        gtk.mainloop ()

else:
    queueTree()

# Local Variables:
# py-indent-offset: 4
# End:
