#!/usr/bin/python -O

# Preface
# -------
# This program was written using vim, with python highlighting, at a screen width of 132 characters, and uses tabs
# for indentation.


# General Modules
# ---------------
#
# It is difficult to write anything useful in python that does not use these three modules, and there is not simple
# way to justify them, so we just import them. Strings must be munged, and files must be pathed.
import string
import re
import os


# I18N Setup
# ----------
# 
# Before we begin with the program proper, we must set up I18N translation functions, so that string constants will be
# auto-translated by gettext. Import the 'gettext' module,
import gettext

# bind it to the appropriate domain,
gettext.bindtextdomain ("printconf-gui", "/usr/share/locale")
gettext.textdomain ("printconf-gui")

# and overload the standard '_()' function used by gettext-ized programs.
_=gettext.gettext


# Program Name, License, Version, and Other Details
# ----------------------------------------
#
# This program is copyright:
program_copyright = _("Red Hat, Inc")
# and caries the following license:

	## printconf-gui
	## Copyright (C) 2000 Red Hat, Inc.
	## Copyright (C) 2000 Crutcher Dunnavant <crutcher@redhat.com>,
	##                    Jonathan Blandford <jrb@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.

program_name = 'printconf-gui'
# This program is a graphical configuration utility for the printconf system. It uses the Alchemist data library on
# the backend. The name is an expresion of my belief that magic is bad. If, at some future date, I write a text based
# configuration tool for printconf, I will name it printconf-tui, or some such. I think that having a program present
# different behaviours depending upon whether an X session can be found is foolish and surprising to the user.

program_version = '0.2.15'
# The version pattern follows the linux kernel style, with major.minor.revision, 'stable' branches on the even minor,
# and 'development' branches on the odd minor numbers.

program_authors = [	"Crutcher Dunnavant",		# The primary author (me)
			"The Lumberjacks of the World"] # A bit of humor about printing
			#Jonathan Blandford		# He helped me with my gtk/gnome/glade problems, but didn't
							# want his name on what is ultimatly a sophmoric attempt at a
							# graphical user interface. I can't blame him.

# This is the best comment I could think of to sum up the issue of printing. It is a quote often claimed in jest to be
# the dying words of Henry David Thoreau, and may have actually been. I never checked, but I like the quote.
program_comments = _("\"The Trees! The Trees!\"  -Thoreau")


# Xyzzy
# -----
#
# Hello Interested Reader,
# If you are looking for ways to do magic with your printing configuration, a better read might be printconf-backend,
# which actually applies the data which we construct here.


# Debug State
# -----------
#
# Some features and behaviours of this program are not meant for general use, but exist solely to aid in actually
# working upon the program. Python uses the specail variable '__debug__' to regulate some debuging behaviour, and we
# can hang the figurative hat of the rest of our debuging behaviours upon the same variable. If the python interpreter
# is set to generate optimized code, all blocks of the form:
#
#	if __debug__:
#		... block ...
#
# will be thrown away at /parse time/. This means that if the command line option -O is used on the shebang line
# above, they will never be executed, no matter __debug__'s value. It's value is set to 1 by default, and 0 by the
# optimize switch, and we set it here to be 'sure', but we can never turn debugging back on, if it was turned off by
# the shebang line.
__debug__ = 0

# Through out the program, it is useful to print some status information to the terminal if we are in debug mode.
# This function makes that a simple call.
def debug_print(str):
	if __debug__:
		print str

# This function is a wrapper to make it easier to print out the names of internal function calls.
def called(func):
	debug_print('Called : %s()' % func.__name__)


# System Signals
# --------------
# 
# Since this program runs as a graphical interface, we really want to ignore whatever is happening on the console.
# Importing the signal module makes it possible to register the default handler (ignore) on terminal interupts.
import signal
debug_print('registering signal.SIG_DFL to handle signal.SIGINT')
signal.signal (signal.SIGINT, signal.SIG_DFL)


# Program Data Paths
# ------------------
#
# This being a configuration tool, and a user interface, we need to know where things are.

# The printconf system resides at this location on the file system:
printconf_dir = '/usr/share/printconf'

# printconf keeps its alchemist config file, called a switchboard file, at this location:
printconf_switchboard_file = '/etc/alchemist/switchboard/printconf.switchboard.adl'

# pritnconf's preparsed printer database summary is available at
printer_db_file = '%s/printer_db.pickle' % printconf_dir 

# printconf-gui keeps its private image data and helper programs in the same location that printconf keeps it's data,
# though this is slated for cleanup on the next cycle.
printconf_gui_dir = printconf_dir

# printconf-gui's glade file, which specifies it's user interface layout, is available at:
glade_file = '%s/printconf-gui.glade' % printconf_gui_dir

# and it's images, used in status displays, are stored in the following diorectory:
image_dir = printconf_dir

# Documentation is stored in the following directory.
doc_dir = '/usr/share/doc/printconf-gui-%s/' % program_version


# Glade Display Initialization
# ----------------------------
# 
# This program uses gnome and gtk for its widget set, and glade as the layout template for the interface. Since there
# is a good deal of initialization to be done before the program is active, we go ahead and set up the basics of this
# interface here, and get it up on the screen before initalizing the rest of the program. First, import the modules
# necesary for displaying the interface:
import gtk, gnome.ui, libglade

# now, initalize an instance of the interfaces discribed in the glade context.
program_glade_context = libglade.GladeXML(glade_file, domain="printconf-gui")

# and spin gtk.mainiteration() to draw the inital windows on the screen.
while gtk.events_pending():
	gtk.mainiteration()

# Now that the main widows are keeping the user ammused, we can load the rest of the graphics and widget libraries that
# were not essential for inital display.
# 
# A small collection of status images is used to indicate queue status, and so now we load the GdkImlib module to read
# them in and make them available to gtk. The pixmaps are stored in the global dictionary 'images', with descriptive
# names.
import GdkImlib
images = {}
for (file, 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')]:
	im = GdkImlib.Image ('%s/%s' % (image_dir, file))
	im.render()
	images[name] = im.get_pixmap()


# Gnome Help
# ----------
#
# Gnome (and every other widget set) provide hooks to launch web pages. For some bizare reason, Gnome does this though
# a module named 'help'. They claim it is because it was written to launch their help pages, but it doesn't matter, it
# can be used to spawn of arbitrary web content. We do, however, us it to launch our help pages through out. It
# requires the following modlue:
import gnome.help


# Generic Dialogs
# ---------------
#
# At frequent intervals in the code, the necessity of presenting an informational or inquisative dialog to the user
# presents itself. In these scenarios, it is often necessary to only show the user a single message, a status symbol,
# and then wait for a decisision to be made. The following functions provide small wrappers arround these actions.
def gui_error(message):
	called(gui_error)
        err_dialog = gnome.ui.GnomeErrorDialog(message)
        err_dialog.set_position(gtk.WIN_POS_MOUSE)
        err_dialog.run_and_close()

def gui_warn(message): 
	called(gui_warn)
        warn_dialog = gnome.ui.GnomeWarningDialog(message)
        warn_dialog.set_position(gtk.WIN_POS_MOUSE)
        warn_dialog.run_and_close()

def gui_info(message):
	called(gui_info)
        info_dialog = gnome.ui.GnomeOkDialog(message)
        info_dialog.set_position(gtk.WIN_POS_MOUSE)
        info_dialog.run_and_close()

def gui_ask(message):
	called(gui_ask)
        ask_dialog = gnome.ui.GnomeOkCancelDialog(message, lambda *x: None)
        ask_dialog.set_position(gtk.WIN_POS_MOUSE)
	# This equates to:
	# 	if and only if they click 'Ok', then return true, else return false.
        return not ask_dialog.run_and_close()


# Gtk Signal Handler Registration
# -------------------------------
# 
# gtk is a callback based widget system, and so functions must be wired up to it's callbacks. Instead of collecting
# all such function references at the registration point, I prefer to incrementally collect handler functions and
# signal names as the handlers are defined; because I feel that the code is more readable that way.
gtk_signal_handlers = {}


# Debug Window
# ------------
#
# A small window containing the labels 'Foo', 'Bar', 'Baz', 'Qux', and 'Bob' is included in the glade context. It is
# meant as an anchor for debugging commands during development. This window gets displayed if debuging is turned on.
# Fetch the window from the glade context, and show it if neccessary.
debug_window = program_glade_context.get_widget("debug_window")
if __debug__:
	debug_print('Showing debug window')
	debug_window.show()

# If debugging is turned on, the debugging interface should not go away. This handler registers a function to return 1
# on the gtk delete event for the debugging window, causing the event to be ignored.
gtk_signal_handlers['on_debug_window_delete_event'] = lambda *x: 1

# Initial debugging events are simple printouts of which event was called.
debug_window_functions = {	'foo' : lambda: debug_print('foo'),
				'bar' : lambda: debug_print('bar'),
				'baz' : lambda: debug_print('baz'),
				'qux' : lambda: debug_print('qux'),
				'bob' : lambda: debug_print('bob')}

# A generic function to make it easier to override debuging events dynamically.
def debug_window_button_handler(button, which):
	called(debug_window_button_handler)
	debug_window_functions[which]()

# Wire the debug handler up to the 5 generic functions.
gtk_signal_handlers.update( {	'on_debug_foo' : (debug_window_button_handler, 'foo'),
				'on_debug_bar' : (debug_window_button_handler, 'bar'),
				'on_debug_baz' : (debug_window_button_handler, 'baz'),
				'on_debug_qux' : (debug_window_button_handler, 'qux'),
				'on_debug_bob' : (debug_window_button_handler, 'bob') } )

# Scanning for Printers
# ---------------------
#
# This being a printing system, it's probably a good idea to go and see if there are any printers attached locally.
# There are much more intelligent approaches to this problem, but the rest of the system isn't sophisticated enough
# to care yet, so simply scaning likely devices to see if they can be opened for writing is sufficient.
local_printers = []
for devlpx in [	'/dev/lp0',
		'/dev/lp1',
		'/dev/lp2',
		'/dev/lp3',
		'/dev/usb/lp0',
		'/dev/usb/lp1',
		'/dev/usb/lp2',
		'/dev/usb/lp3']:
	try:
		os.close (os.open (devlpx, os.O_WRONLY | os.O_NONBLOCK))
		local_printers.append({'device':devlpx})
	except: pass


# Initializing the Alchemist
# --------------------------
#
# The alchemist data library is the data handling layer that the printconf system is laid on top of. To use it, we need
# to import the python bindings.
import Alchemist

# The switchboard file is a 'recipie' of sorts for data handling by the alchemist. It specifies how to get and save
# various contexts, and merge order.
switchboard = Alchemist.Switchboard(file_path = printconf_switchboard_file)

# The alchemist views the world in terms of 'BlackBoxes', anonymous data sources with a restricted set of interactions.
# printconf stores it's local configuration in the input set black box 'local', since we will be reading from and
# writting to this source, we need to initalize a direct interface to it, and keep that arround.
dynamic_box_entry = switchboard.readNamespaceCfg('printconf').getChildByName('input_set').getChildByName('local')
dynamic_box = Alchemist.getBox(dynamic_box_entry.getChildByName('box'))

# Since we are dealing in merge space, we need to get the state of the config space just before the context we are
# editing, the 'local' one. The cascade funciton is perfect for this, and we tell it to stop at the appropriate
# position. Since we will not edit the values returned, they are 'static'
static_ctx = switchboard.cascadeNamespace('printconf', dynamic_box_entry.getPos())

# And of course, initalize the 'local' context that we will be editing (hence dynamic)
dynamic_ctx = dynamic_box.read()
if not dynamic_ctx:
	# Oops, no entry available. make one
	dynamic_ctx = Alchemist.Context(name = "local", serial = 0)
	plist = dynamic_ctx.getDataRoot().addChild(Alchemist.Data.ADM_TYPE_LIST, "printconf")
	plist.addChild(Alchemist.Data.ADM_TYPE_LIST, "print_queues")


# Initializing the printer database
# ---------------------------------
#
# There is a massive amount of data on disc, and at the moment, it is not feasible to scan that data at run time. In
# the future, Grant Taylor's work on migrating the Linux Printing Database to XML should ameliorate this problem, but
# for the moment, printconf relies upon an striped printer database that is saved as a python pickle file (because it's
# purpose is to be fast for printconf-gui to read). We use the cPickle module, because it is faster than Pickle.
import cPickle

# Read in the pickled printer database
printer_db = cPickle.Unpickler(open(printer_db_file)).load()

# When we present options to users, it is useful to present printers sorted by vendor. Here, we give the printer
# database a second, vendor index head. It is important that we do this before we add any special case printers, as
# their vendors are not well defined.
vendor_db = {}
for id in printer_db.keys():
	printer = printer_db[id]
	vendor = printer['vendor']
	if not vendor_db.has_key(vendor):
		vendor_db[vendor] = [printer]
	else:
		vendor_db[vendor].append(printer)

# We add three special case printers to the printer database, to simplify code flow. The POSTSCRIPT special case is
# used by the magicfilter filter type, and is for printing to a postscript printer without using mfomatic.
printer_db["POSTSCRIPT"] = {
		"id" : "POSTSCRIPT",
		"model" : _("Postscript Printer"),
		"drivers" : ["Postscript"],
		"vendor": "" }

# The TEXT special case is used by the magicfilter filter type, and is for printing to a text only filter.
printer_db["TEXT"] = {
		"id" : "TEXT",
		"model" : _("Text Only Printer"),
		"drivers" : ["TEXT"],
		"vendor" : "" }

# The NONE special case specifies using the the none filter type, which evalutates to no filtration.
printer_db["NONE"] = {
		"id" : "NONE",
		"model" : _("Raw Print Queue"),
		"drivers" : ["NONE"],
		"vendor" : "" }
		

# Changed?
# --------
#
# One of the most common features of editors is a knowledge of whether anything has been changed since the last save.
# This is really just a boolean flag, but we wrap it in a pair of functions to prevent the need for every state
# transitioning function to list 'changed' as a global variable. Of course, the inital state is unchanged.
changed = None
def set_changed():
	global changed
	changed = 1

def clr_changed():
	global changed
	changed = None


# Foomatic Datafiles
# ------------------
#
# The printer database contains only cursory information about any one printer. When we need more, such as when we wish
# to display the driver options that a printer driver combination offers, we need to read in the foomatic data file for
# that printer driver combination. Since the foomatic files are currently in Perl format (used by data dumper), this is
# most easily accomplished by calling a thin perl wrapper.
#
# Since this is a disk hit, we take a little care to at least keep the last call cached. It is necessary that we not
# modify the returned hash, because that would screw up our latency hiding.
# Fortunately, python has constans, oh, wait, no, it doesn't :(
foomatic_options_key = (None, None)
foomatic_options_hash = {}
def foomatic_options(printer_id, gs_driver):
	called(foomatic_options)
	global foomatic_options_key, foomatic_options_hash

	# Go ahead and return the stored hash if it's one we're looking for.
	if (printer_id, gs_driver) == foomatic_options_key:
		return foomatic_options_hash

	try:
		# Call the perl helper, and eval it's return string.
		perl_cmd = printconf_dir + '/util/scan_foo_options.pl ' + printer_id + ' ' + gs_driver
		perl = os.popen(perl_cmd)
		foo_str = perl.read()
		hash = eval(foo_str)
	except:
		return None
	
	# save the hash for use next time.
	foomatic_options_hash = hash
	foomatic_options_key = (printer_id, gs_driver)
	return hash


# Queue Validation
# ----------------
#
# Queue validation is a simple boolean check. If any of the things we would expect to find are not in a queue, then it
# is 'INVALID', we return None, and this means that the program is not smart enough to edit that particular queue.
def validate_queue(q_obj):
	called(validate_queue)

	if not valid_queue_name(q_obj.getName()):
		return None

	try:
		a_list = q_obj.getChildByName('alias_list')
		a_list.isAnonymous()
		for i in range(a_list.getNumChildren()):
			if not valid_queue_name(a_list.getChildByIndex(i).getValue()):
				return None

		q_type = q_obj.getChildByName('queue_type').getValue()
		q_data = q_obj.getChildByName('queue_data')

		if q_type == 'LOCAL':
			q_data.getChildByName('local_printer_device')

		elif q_type == 'LPD':
			q_data.getChildByName('lpd_server')
			q_data.getChildByName('lpd_queue')
			q_data.getChildByName('lpd_strict_rfc1179')
		
		elif q_type == 'SMB':
			q_data.getChildByName('smb_share')
			q_data.getChildByName('smb_ip')
			q_data.getChildByName('smb_user')
			q_data.getChildByName('smb_password')
			q_data.getChildByName('smb_workgroup')
			q_data.getChildByName('smb_translate')
		
		elif q_type == 'NCP':
			q_data.getChildByName('ncp_server')
			q_data.getChildByName('ncp_queue')
			q_data.getChildByName('ncp_user')
			q_data.getChildByName('ncp_password')
		
		elif q_type == 'JETDIRECT':
			q_data.getChildByName('jetdirect_ip')
			q_data.getChildByName('jetdirect_port')
		
		else:
			return None

		f_type = q_obj.getChildByName('filter_type').getValue()
		f_data = q_obj.getChildByName('filter_data')
		if f_type == 'NONE':
			None

		elif f_type == 'MAGICFILTER':
			f_data.getChildByName('flags')

			mf_type = f_data.getChildByName('mf_type').getValue()
			if mf_type == 'POSTSCRIPT':
				f_data.getChildByName('page_size').getValue()

			elif mf_type == 'TEXT':
				None

			elif mf_type == 'MFOMATIC':
				f_data.getChildByName('printer_id').getValue()
				f_data.getChildByName('gs_driver').getValue()

				foo_ops = f_data.getChildByName('foomatic_defaults')
				for i in range(foo_ops.getNumChildren()):
					op = foo_ops.getChildByIndex(i)
					op.getChildByName('name')
					op.getChildByName('type')
					op.getChildByName('default')
			else:
				return None

		else:
			return None
			
	except Exception, e:
		return None

	return 1


# Description
# -----------
#
# Given a queue, this function comes up with a little blurb to describe the queue. It is all special cases, and
# any additional types or subtypes will just have to be hacked in.
def gizmo_queue_description(q_obj):
	called(gizmo_queue_description)

	# extract the types and data lists from the queue's object
	q_type = q_obj.getChildByName('queue_type').getValue()
	q_data = q_obj.getChildByName('queue_data')
	f_type = q_obj.getChildByName('filter_type').getValue()
	f_data = q_obj.getChildByName('filter_data')

	if f_type == 'NONE':
		f_blurb = _("Raw")

	elif f_type == 'MAGICFILTER':
		mf_type = f_data.getChildByName('mf_type').getValue()
		if mf_type == 'POSTSCRIPT':
			f_blurb = _("PostScript")

		elif mf_type == 'TEXT':
			f_blurb = _("Text")

		elif mf_type == 'MFOMATIC':
			printer_id = f_data.getChildByName('printer_id').getValue()
			printer = printer_db.get(printer_id, None)
			if printer:
				f_blurb = _("%s %s") % (printer['vendor'], printer['model'])
			else:
				f_blurb = _("Unknown")
		else:
			f_blurb = _("Unknown")
	else:
		f_blurb = _("Unknown")

	if q_type == 'LOCAL':
		details = _("%s queue on local device %s") % (
				f_blurb,
				q_data.getChildByName('local_printer_device').getValue())

	elif q_type == 'LPD':
		details = _("%s lpd queue %s@%s") % (
				f_blurb,
				q_data.getChildByName('lpd_queue').getValue(),
				q_data.getChildByName('lpd_server').getValue())

	elif q_type == 'SMB':
		details = _("%s SMB queue on share %s") % (
				f_blurb,
				q_data.getChildByName('smb_share').getValue())

	elif q_type == 'NCP':
		details = _("%s Novell queue %s on server %s") % (
				f_blurb, q_data.getChildByName('ncp_queue').getValue(), 
				q_data.getChildByName('ncp_server').getValue())

	elif q_type == 'JETDIRECT':
		details = _("%s JetDirect queue %s:%s") % (
				f_blurb,
				q_data.getChildByName('jetdirect_ip').getValue(),
				q_data.getChildByName('jetdirect_port').getValue())

	else:
		details = _("%s unknown queue type %s") % (
				f_blurb,
				q_type)

	return details


##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
## Literate Edit Barrier							  /|\
##										 / | \
## stuff below this line needs to be re-written to be above this line.		   |
## This is part of my scheme to not be hunted down by the next maintainer.	   |
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##

def valid_queue_name(name):
	called(valid_queue_name)

	# Checks the validity of a printer name (The strictness is an artifact of our encoding method.)
	return re.match(r'^[a-zA-Z][-a-zA-Z0-9_]*$', name)


def queue_name(queue):
	called(queue_name)

	return queue['object'].getName()

def queue_alias_list(queue):
	called(queue_alias_list)

	a_list = queue.getChildByName('alias_list')
	alias_list = []
	for i in range(a_list.getNumChildren()):
		alias_list.append(a_list.getChildByIndex(i).getValue())
	alias_list.sort()
	return alias_list

# find the name of the default queue
def default_queue_name():
	called(default_queue_name)

	# The default printer is either the first one after the merge,
	# or the one specified by default_queue, if it exists.
	# THis little song-and-dance predicts the default after a merge.
	try: return dynamic_ctx.getDataByPath('/printconf/default_queue').getValue()
	except: pass
	try: return static_ctx.getDataByPath('/printconf/default_queue').getValue()
	except: pass
	try: return static_ctx.getDataByPath('/printconf/print_queues').getChildByIndex(0).getName()
	except: pass
	try: return dynamic_ctx.getDataByPath('/printconf/print_queues').getChildByIndex(0).getName()
	except: return None

# build a list of 'queues', which are dictionaries for us, and return them.
def build_queue_group():
	called(build_queue_group)

	# to do 'proper' prediction of alias overloading, we have to remeber
	# that the default queue comes first in the precedence.
	def_queue_name = default_queue_name()

	name_hash = {}
	alias_hash = {} 
	queue_list = []

	queue_group = {
		"name_hash" : name_hash,
		"alias_hash": alias_hash,
		"queue_list": queue_list,
		"default_queue": None
	}

	# scan the dynamic and static contexts
	# The ctx_tuple nonsense is just a way to flagg the dynamic state cleanly.
	for ctx_tuple in [(dynamic_ctx,1), (static_ctx,0)]:
		if not ctx_tuple[0]: continue
		print_queues = ctx_tuple[0].getDataByPath("/printconf/print_queues")
		for i in range(print_queues.getNumChildren()):
			q_obj = print_queues.getChildByIndex(i)
			valid = validate_queue(q_obj)
			queue = {
				"object" : q_obj,
				"dynamic" : ctx_tuple[1],
				"active" : 1,
				"valid" : valid,
				"queue_group" : queue_group
			}
			queue_list.append(queue)
	
			q_name = q_obj.getName()
			if name_hash.has_key(q_name):
				name_hash[q_name].append(queue)
				queue['active'] = None
			else:
				name_hash[q_name] = [queue]

			if not valid:
				continue

			a_list = q_obj.getChildByName("alias_list")
			for j in range(a_list.getNumChildren()):
				a_obj = a_list.getChildByIndex(j)
				alias = a_obj.getValue()
	
				if not alias_hash.has_key(alias):
					alias_hash[alias] = [queue]
				elif def_queue_name == q_name and \
					name_hash[def_queue_name][0] == queue:
					alias_hash[alias].insert(0,queue)
				else:
					alias_hash[alias].append(queue)

	# figure out which queue is the default
	try:
		queue_group['default_queue'] = name_hash[def_queue_name][0]
	except:
		try:	queue_group['default_queue'] = queue_list[0]
		except:	pass

	return queue_group

#------------------------------------------------------------------------------------------------------
# LPD Control Section (qed)
#

# attempt to restart the lpd service
def restart_lpd():
	called(restart_lpd)

	return not os.system("service lpd restart > /dev/null")
	
# print a postscript testpage
def print_letter_postscript_test_page(printer):
	called(print_letter_postscript_test_page)

	return not os.system("lpr -P" + printer + " " + printconf_dir + "/tests/testpage.ps")

def print_a4_postscript_test_page(printer):
	called(print_a4_postscript_test_page)

	return not os.system("lpr -P" + printer + " " + printconf_dir + "/tests/testpage-a4.ps")

# print a postscript testpage
def print_ascii_test_page(printer):
	called(print_ascii_test_page)

	return not os.system("lpr -P" + printer + " " + printconf_dir + "/tests/testpage.asc")


#------------------------------------------------------------------------------------------------------
# Queue Edit Dialog Section (qed)
#

queue_edit_dialog = program_glade_context.get_widget("queue_edit_dialog")
queue_edit_dialog.close_hides(1)
qed_help_dialog = program_glade_context.get_widget("qed_help_dialog")
qed_help_dialog.close_hides(1)
qed_help_dialog_text = program_glade_context.get_widget("qed_help_dialog_text")
qed_help_dialog_text.set_word_wrap(1)

# initialize the queue edit dialog
def init_qed():
	called(init_qed)

	init_qed_section()
	init_qed_type_section()
	init_qed_driver_section()
	return

qed_queue = None
# This function is the core of the editor
def qed_run(queue=None):
	called(qed_run)

	# initialize the edit context.
	global qed_queue
	qed_queue = queue

	# flip to the name section
	qed_section_ctree.select(qed_sections['name']['qed_section_ctree_node'])

	if not (qed_name_section_setup() and qed_type_section_setup() and qed_driver_section_setup()):
		gui_error(_("This queue is in an unknown format.\nCannot Edit."))
		return 0

	while 1:
		pressed = queue_edit_dialog.run()
		if pressed == 0: # Ok
			if not qed_name_section_validate():
				continue
			if not qed_type_section_validate():
				continue
			if not qed_driver_section_validate():
				continue
			break

		elif pressed == 2: # Help
			sel = qed_section_ctree.selection
			section = qed_section_ctree.node_get_row_data(sel[0])
			gnome.help.goto(section['help_func']())
			continue

		else: # Cancel
			queue_edit_dialog.hide()
			return 0

	if queue:
		q_obj = queue['object']
	else:
		q_name = qed_name_entry.get_text()
		q_obj = dynamic_ctx.getDataByPath("/printconf/print_queues").addChild(Alchemist.Data.ADM_TYPE_LIST, q_name)
		q_obj.setAtomic(1)
		q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'alias_list').setAnonymous(1)
		q_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'queue_type')
		q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'queue_data')
		q_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'filter_type')
		q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'filter_data')

	qed_name_section_teardown(q_obj)
	qed_type_section_teardown(q_obj)
	qed_driver_section_teardown(q_obj)

	queue_edit_dialog.hide()
	return q_obj

#------------------------------------------------------------
# qed section Section
#

qed_section_ctree = program_glade_context.get_widget("qed_section_ctree")
qed_section_notebook = program_glade_context.get_widget("qed_section_notebook")
qed_sections = {}

def init_qed_section():
	called(init_qed_section)

	qed_section_ctree.set_line_style(gtk.CTREE_LINES_NONE)
	qed_section_ctree.set_expander_style(gtk.CTREE_EXPANDER_TRIANGLE)

	# set up the 'Name and Alias' section
	n_node = qed_section_ctree.insert_node(None, None, [_("Name and Aliases")], is_leaf = 0, expanded=gtk.TRUE)
	n_sec = {
		'name' : 'name',
		'notebook_page': 0,
		'qed_section_ctree_node' : n_node,
		'pre_show_function' : None,
		'post_show_function' : None,
		'help_func' : (lambda: '%s/printconf-aliases.html' % doc_dir)
	}
	qed_sections['name'] = n_sec
	qed_section_ctree.node_set_row_data(n_node, n_sec)

	# set up the 'Queue Type' section
	t_node = qed_section_ctree.insert_node(None, None, [_("Queue Type")], is_leaf = 0, expanded=gtk.TRUE)
	t_sec = {
		'name' : 'type',
		'notebook_page': 1,
		'qed_section_ctree_node' : t_node,
		'pre_show_function' : None,
		'post_show_function' : None,
		'help_func' : qed_queue_type_section_help
	}
	qed_sections['type'] = t_sec
	qed_section_ctree.node_set_row_data(t_node, t_sec)

	# set up the 'Queue Driver' section
	d_node = qed_section_ctree.insert_node(None, None, [_("Printer Driver")], is_leaf = 0, expanded=gtk.TRUE)
	d_sec = {
		'name' : 'driver',
		'notebook_page': 2,
		'qed_section_ctree_node' : d_node,
		'pre_show_function' : None,
		'post_show_function' : qed_printer_ctree_moveto_printer,
		'help_func' : (lambda: '%s/index.html' % doc_dir)
	}
	qed_sections['driver'] = d_sec 
	qed_section_ctree.node_set_row_data(d_node, d_sec)

	# set up the 'Driver Options' section
	do_node = qed_section_ctree.insert_node(d_node, None, [_("Driver Options")], is_leaf = 1, expanded=gtk.TRUE)
	do_sec = {
		'name' : 'driver_options',
		'notebook_page': 3,
		'qed_section_ctree_node' : do_node,
		'pre_show_function' : qed_display_driver_options,
		'post_show_function' : None,
		'help_func' : (lambda: '%s/index.html' % doc_dir)
	}
	qed_sections['driver_options'] = do_sec
	qed_section_ctree.node_set_row_data(do_node, do_sec)

def on_qed_section_ctree_select_row(ctree, node, *args):
	called(on_qed_section_ctree_select_row)

	section = ctree.node_get_row_data(node)
	if not section:
		return

	pre_show_func = section['pre_show_function']
	if pre_show_func:
		pre_show_func()

	qed_section_notebook.set_page(section['notebook_page'])

	post_show_func = section['post_show_function']
	if post_show_func:
		post_show_func()

gtk_signal_handlers['on_qed_section_ctree_select_row'] = on_qed_section_ctree_select_row

#--------------------------------------------------------------------
# qed name and alias section section
#

qed_name_entry = program_glade_context.get_widget("qed_name_entry")
qed_alias_clist = program_glade_context.get_widget("qed_alias_clist")
alias_edit_dialog = program_glade_context.get_widget("alias_edit_dialog")
alias_edit_dialog.close_hides(1)
aed_alias_entry = program_glade_context.get_widget("aed_alias_entry")

qed_alias_list = None
def qed_name_section_setup():
	called(qed_name_section_setup)

	global qed_alias_list

	if qed_queue:
		qed_name_entry.set_text(queue_name(qed_queue))
		qed_alias_list = queue_alias_list(qed_queue['object'])
	else:
		qed_name_entry.set_text('')
		qed_alias_list = []

	qed_display_alias_clist()
	return 1

def qed_name_section_validate():
	called(qed_name_section_validate)

	# We assume that the alias list is valid, because we've been validating it
	# while we edit it.

	name = qed_name_entry.get_text()

	def qne_show():
		qed_section_ctree.select(qed_sections['name']['qed_section_ctree_node'])
		qed_name_entry.grab_focus()
		qed_name_entry.select_region (0, -1)

	if not valid_queue_name(name):
		qne_show()
		gui_error(_("You must select a valid name.\nUse only the characters -,a-z,A-Z,0-9,_ please."))
		return None

	n_queue_list = qld_queue_group['name_hash'].get(name)
	if n_queue_list and len(n_queue_list) and qed_queue != n_queue_list[0]:
		qne_show()
		gui_error(_("Another print queue is already using \"%s\" as a name.") % name)
		return None

	a_queue_list = qld_queue_group['alias_hash'].get(name)
	if a_queue_list and len(a_queue_list) and qed_queue != a_queue_list[0]:
		qne_show()
		aq_name = queue_name(a_queue_list[0])
		gui_error(_("The print queue \"%s\" is already using \"%s\" as an alias.") % (aq_name, name))
		return None

	return 1

def qed_name_section_teardown(q_obj):
	called(qed_name_section_teardown)

	name = qed_name_entry.get_text()
	if q_obj.getName() != name:
		q_obj.setName(name)

	# Rebuild the object's alias list
	q_obj.getChildByName("alias_list").unlink()
	al_obj = q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'alias_list')
	al_obj.setAnonymous(1)
	for alias in qed_alias_list:
		al_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'alias').setValue(alias)

def qed_selected_alias():
	called(qed_selected_alias)

	sel = qed_alias_clist.selection
	if len(sel) == 0:
		return None
	return qed_alias_clist.get_text(sel[0], 0)

def qed_display_alias_clist():
	called(qed_display_alias_clist)

	qed_alias_list.sort()
	qed_alias_clist.freeze()
	qed_alias_clist.clear()
	for alias in qed_alias_list:
		qed_alias_clist.append([alias])
	qed_alias_clist.thaw()

def on_qed_alias_add_button_clicked(*args):
	called(on_qed_alias_add_button_clicked)

	aed_alias_entry.set_text('')
	aed_run(None)

def on_qed_alias_edit_button_clicked(*args):
	called(on_qed_alias_edit_button_clicked)

	alias = qed_selected_alias()
	if not alias:
		gui_error(_("You must select an alias to edit."))
		return
	aed_run(alias)

def aed_run(alias):
	called(aed_run)

	aed_alias_entry.set_text(alias or '')
	while 1:
		pressed = alias_edit_dialog.run()
		if pressed != 0: # Cancel
			break
		else: # Ok
			new_alias = aed_alias_entry.get_text()
			if new_alias == alias:
				break

			if not valid_queue_name(new_alias):
				gui_error(_("You must select a valid name for an alias.\n" + \
						"Use only the characters -,a-z,A-Z,0-9,_ please."))
				continue

			
			n_queue_list = qld_queue_group['name_hash'].get(new_alias)
			if n_queue_list and len(n_queue_list) and qed_queue != n_queue_list[0]:
				gui_error(_("Another print queue is already using \"%s\" as a name.") % new_alias)
				continue

			a_queue_list = qld_queue_group['alias_hash'].get(new_alias)
			if a_queue_list and len(a_queue_list) and qed_queue != a_queue_list[0]:
				aq_name = queue_name(a_queue_list[0])
				gui_error(_("The print queue \"%s\" is already using \"%s\" as an alias.") % (aq_name, new_alias))
				continue

			# Zap the old alias
			if alias:
				qed_alias_list.remove(alias)

			# Don't add an alias twice
			try:	qed_alias_list.index(new_alias)
			except: qed_alias_list.append(new_alias)

			break

	qed_display_alias_clist()
	alias_edit_dialog.hide()
	return

def on_qed_alias_delete_button_clicked(*args):
	called(on_qed_alias_delete_button_clicked)

	alias = qed_selected_alias()
	if not alias:
		gui_error(_("You must select an alias to delete."))
		return

	qed_alias_list.remove(alias)
	qed_display_alias_clist()

gtk_signal_handlers.update({	'on_qed_alias_add_button_clicked' : on_qed_alias_add_button_clicked,
				'on_qed_alias_edit_button_clicked' : on_qed_alias_edit_button_clicked,
				'on_qed_alias_delete_button_clicked' : on_qed_alias_delete_button_clicked })

#--------------------------------------------------------------------
# qed type section section
#

# This list lets us lookup the the page of a queue type quickly, by doing a queue_type_table.index(q_type) call.
queue_type_table = ["LOCAL", "LPD", "SMB", "NCP", "JETDIRECT"]

qed_type_notebook = program_glade_context.get_widget("qed_type_notebook")
qed_type_menu = program_glade_context.get_widget("qed_type_menu")

qed_local_printer_device_entry = program_glade_context.get_widget("qed_local_printer_device_entry")
qed_local_printer_device_combo = program_glade_context.get_widget("qed_local_printer_device_combo")
qed_lpd_server_entry = program_glade_context.get_widget("qed_lpd_server_entry")
qed_lpd_queue_entry = program_glade_context.get_widget("qed_lpd_queue_entry")
qed_lpd_strict_rfc1179_check = program_glade_context.get_widget("qed_lpd_strict_rfc1179_check")
qed_smb_share_entry = program_glade_context.get_widget("qed_smb_share_entry")
qed_smb_ip_entry = program_glade_context.get_widget("qed_smb_ip_entry")
qed_smb_workgroup_entry = program_glade_context.get_widget("qed_smb_workgroup_entry")
qed_smb_user_entry = program_glade_context.get_widget("qed_smb_user_entry")
qed_smb_password_entry = program_glade_context.get_widget("qed_smb_password_entry")
qed_smb_translate_check = program_glade_context.get_widget("qed_smb_translate_check")
qed_ncp_server_entry = program_glade_context.get_widget("qed_ncp_server_entry")
qed_ncp_queue_entry = program_glade_context.get_widget("qed_ncp_queue_entry")
qed_ncp_user_entry = program_glade_context.get_widget("qed_ncp_user_entry")
qed_ncp_password_entry = program_glade_context.get_widget("qed_ncp_password_entry")
qed_jetdirect_ip_entry = program_glade_context.get_widget("qed_jetdirect_ip_entry")
qed_jetdirect_port_entry = program_glade_context.get_widget("qed_jetdirect_port_entry")

def init_qed_type_section():
	called(init_qed_type_section)

	# set up the local printer's device menu
	strs = map(lambda x: x['device'], local_printers)
	if len(strs):
		qed_local_printer_device_combo.set_popdown_strings(strs)

def qed_queue_type_section_help():
	called(qed_queue_type_section_help)

	q_type = qed_type()
	if q_type == 'LOCAL':
		page = 'printconf-local-printer.html'
	elif q_type == 'LPD':
		page = 'printconf-remote-printer.html'
	elif q_type == 'SMB':
		page = 'printconf-smb-printer.html'
	elif q_type == 'NCP':
		page = 'printconf-ncp-printer.html'
	elif q_type == 'JETDIRECT':
		page = 'printconf-jetdirect-printer.html'
	else:
		page = 'index.html'
	return "%s/%s" % (doc_dir, page)

def qed_type_section_reset():
	called(qed_type_section_reset)

	qed_set_type('LOCAL')

	local_devices = qed_local_printer_device_combo.list.children()
	if len(local_devices):
		local_devices[0].select()

	qed_lpd_server_entry.set_text('')
	qed_lpd_queue_entry.set_text('')
	qed_lpd_strict_rfc1179_check.set_active(0)
	qed_smb_share_entry.set_text('')
	qed_smb_ip_entry.set_text('')
	qed_smb_workgroup_entry.set_text('')
	qed_smb_user_entry.set_text('')
	qed_smb_password_entry.set_text('')
	qed_smb_translate_check.set_active(0)
	qed_ncp_server_entry.set_text('')
	qed_ncp_queue_entry.set_text('')
	qed_ncp_user_entry.set_text('')
	qed_ncp_password_entry.set_text('')
	qed_jetdirect_ip_entry.set_text('')
	qed_jetdirect_port_entry.set_text('9100')

def qed_type_section_setup():
	called(qed_type_section_setup)

	qed_type_section_reset()

	if not qed_queue:
		return 1

	q_obj = qed_queue['object']
	q_type = q_obj.getChildByName('queue_type').getValue()
	data_obj = q_obj.getChildByName('queue_data')

	try:
		if q_type == 'LOCAL':
			qed_set_type(q_type)
			qed_local_printer_device_entry.set_text(data_obj.getChildByName('local_printer_device').getValue())

		elif q_type == 'LPD':
			qed_set_type(q_type)
			qed_lpd_server_entry.set_text(data_obj.getChildByName('lpd_server').getValue())
			qed_lpd_queue_entry.set_text(data_obj.getChildByName('lpd_queue').getValue())
			qed_lpd_strict_rfc1179_check.set_active(data_obj.getChildByName('lpd_strict_rfc1179').getValue())

		elif q_type == 'SMB':
			qed_set_type(q_type)
			qed_smb_share_entry.set_text(data_obj.getChildByName('smb_share').getValue())
			qed_smb_ip_entry.set_text(data_obj.getChildByName('smb_ip').getValue())
			qed_smb_workgroup_entry.set_text(data_obj.getChildByName('smb_workgroup').getValue())
			qed_smb_user_entry.set_text(data_obj.getChildByName('smb_user').getValue())
			qed_smb_password_entry.set_text(data_obj.getChildByName('smb_password').getValue())
			qed_smb_translate_check.set_active(data_obj.getChildByName('smb_translate').getValue())

		elif q_type == 'NCP':
			qed_set_type(q_type)
			qed_ncp_server_entry.set_text(data_obj.getChildByName('ncp_server').getValue())
			qed_ncp_queue_entry.set_text(data_obj.getChildByName('ncp_queue').getValue())
			qed_ncp_user_entry.set_text(data_obj.getChildByName('ncp_user').getValue())
			qed_ncp_password_entry.set_text(data_obj.getChildByName('ncp_password').getValue())

		elif q_type == 'JETDIRECT':
			qed_set_type(q_type)
			qed_jetdirect_ip_entry.set_text(data_obj.getChildByName('jetdirect_ip').getValue())
			qed_jetdirect_port_entry.set_text(data_obj.getChildByName('jetdirect_port').getValue())

		else:
			return 0
	except:
		return 0

	return 1

def qed_type_section_validate():
	called(qed_type_section_validate)

	# This function could/should be smarter
	# That is why i've not consolodated the checks, they will evolve. (I hope.)

	q_type = qed_type()
	if q_type == 'LOCAL':
		#local_printer_device
		local_printer_device = qed_local_printer_device_entry.get_text()
		if local_printer_device == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_local_printer_device_entry.grab_focus()
			qed_local_printer_device_entry.select_region (0, -1)
			gui_error(_("You must specify a printer device."))
			return None

	elif q_type == 'LPD':
		#lpd_server
		lpd_server = qed_lpd_server_entry.get_text()
		if lpd_server == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_lpd_server_entry.grab_focus()
			qed_lpd_server_entry.select_region (0, -1)
			gui_error(_("You must specify a remote lpd host to print to."))
			return None

		#lpd_queue

	elif q_type == 'SMB':
		if not os.path.exists("/usr/bin/smbclient"):
			# Oops, I don't see samba
			gui_warn(_("You do not seem to have the samba client installed.\nYou will not be able to print to SMB servers.\nConsider installing the 'samba-client' rpm."))

		#smb_share
		smb_share = qed_smb_share_entry.get_text()
		if smb_share == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_smb_share_entry.grab_focus()
			qed_smb_share_entry.select_region (0, -1)
			gui_error(_("You must specify a SMB share to print to."))
			return None

		#smb_ip
		#smb_workgroup
		#smb_user
		#smb_password
		#smb_translate

	elif q_type == 'NCP':
		if not os.path.exists("/usr/bin/nprint"):
			# Oops, I don't see samba
			gui_warn(_("You do not seem to have the nprint program installed.\nYou will not be able to print to NCP servers.\nConsider installing the 'ncpfs' rpm."))

		#ncp_server
		ncp_server = qed_ncp_server_entry.get_text()
		if ncp_server == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_ncp_server_entry.grab_focus()
			qed_ncp_server_entry.select_region (0, -1)
			gui_error(_("You must specify an NCP server to print to."))
			return None

		#ncp_queue
		ncp_queue = qed_ncp_queue_entry.get_text()
		if ncp_queue == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_ncp_queue_entry.grab_focus()
			qed_ncp_queue_entry.select_region (0, -1)
			gui_error(_("You must specify a queue on the NCP server to print to."))
			return None

		#ncp_user
		#ncp_password

	elif q_type == 'JETDIRECT':
		#jetdirect_ip
		jetdirect_ip = qed_jetdirect_ip_entry.get_text()
		if jetdirect_ip == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_jetdirect_ip_entry.grab_focus()
			qed_jetdirect_ip_entry.select_region (0, -1)
			gui_error(_("You must specify the ip address of a JetDirect printer."))
			return None

		#jetdirect_port
		jetdirect_port = qed_jetdirect_port_entry.get_text()
		if jetdirect_port == '':
			qed_section_ctree.select(qed_sections['type']['qed_section_ctree_node'])
			qed_jetdirect_port_entry.grab_focus()
			qed_jetdirect_port_entry.select_region (0, -1)
			gui_error(_("You must specify the port of the JetDirect printer."))
			return None
	# Success!
	return 1

def qed_type_section_teardown(q_obj):
	called(qed_type_section_teardown)

	q_type = qed_type()
	q_obj.getChildByName('queue_type').setValue(q_type)

	q_obj.getChildByName('queue_data').unlink()
	data_obj = q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'queue_data')

	if q_type == 'LOCAL':
		local_printer_device = qed_local_printer_device_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'local_printer_device').setValue(local_printer_device)

	elif q_type == 'LPD':
		lpd_server = qed_lpd_server_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'lpd_server').setValue(lpd_server)
		lpd_queue = qed_lpd_queue_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'lpd_queue').setValue(lpd_queue)
		data_obj.addChild(Alchemist.Data.ADM_TYPE_BOOL, 'lpd_strict_rfc1179').setValue(qed_lpd_strict_rfc1179_check.active)

	elif q_type == 'SMB':
		smb_share = qed_smb_share_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'smb_share').setValue(smb_share)
		smb_ip = qed_smb_ip_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'smb_ip').setValue(smb_ip)
		smb_workgroup = qed_smb_workgroup_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'smb_workgroup').setValue(smb_workgroup)
		smb_user = qed_smb_user_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'smb_user').setValue(smb_user)
		smb_password = qed_smb_password_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'smb_password').setValue(smb_password)
		data_obj.addChild(Alchemist.Data.ADM_TYPE_BOOL, 'smb_translate').setValue(qed_smb_translate_check.active)

	elif q_type == 'NCP':
		ncp_server = qed_ncp_server_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'ncp_server').setValue(ncp_server)
		ncp_queue = qed_ncp_queue_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'ncp_queue').setValue(ncp_queue)
		ncp_user = qed_ncp_user_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'ncp_user').setValue(ncp_user)
		ncp_password = qed_ncp_password_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'ncp_password').setValue(ncp_password)

	elif q_type == 'JETDIRECT':
		jetdirect_ip = qed_jetdirect_ip_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'jetdirect_ip').setValue(jetdirect_ip)
		jetdirect_port = qed_jetdirect_port_entry.get_text()
		data_obj.addChild(Alchemist.Data.ADM_TYPE_STRING, 'jetdirect_port').setValue(jetdirect_port)

def qed_set_type(q_type):
	called(qed_set_type)

	n_type = queue_type_table.index(q_type)
	qed_type_menu.set_history(n_type)
	qed_type_notebook.set_page(n_type)

def qed_type():
	called(qed_type)

	menu = qed_type_menu.get_menu()
	return queue_type_table[menu.children().index(menu.get_active())]

# handles the type menu, and switches the type notebook
def on_qed_type_menu_selection(menu):
	called(on_qed_type_menu_selection)

	type = menu.children().index(menu.get_active())
	qed_type_notebook.set_page(type)

qed_type_menu.get_menu().signal_connect('selection-done', on_qed_type_menu_selection)


#--------------------------------------------------------------------
# qed driver section section
#

qed_printer_ctree = program_glade_context.get_widget("qed_printer_ctree")
qed_driver_menu = program_glade_context.get_widget("qed_driver_menu")


def init_qed_driver_section():
	called(init_qed_driver_section)

	# init the printer driver ctree
	qed_printer_ctree.set_line_style(gtk.CTREE_LINES_NONE)
	qed_printer_ctree.set_expander_style(gtk.CTREE_EXPANDER_TRIANGLE)

	# Insert all the normal printers (The ones in the vendor database)
	qed_printer_ctree.set_auto_sort(1)
	for vendor in vendor_db.keys():
		vendor_node = qed_printer_ctree.insert_node(None, None, [vendor], is_leaf = 0)
		qed_printer_ctree.node_set_selectable(vendor_node, 0)
                for printer in vendor_db[vendor]:
                        printer_node = qed_printer_ctree.insert_node(vendor_node, None, [printer['model']], is_leaf = 1)
                        qed_printer_ctree.node_set_row_data(printer_node, printer)
			printer['printer_ctree_node'] = printer_node
	qed_printer_ctree.set_auto_sort(0)

	# Insert the special case printers
	last_node = qed_printer_ctree.node_nth(0)
	for printer in [printer_db["NONE"], printer_db["TEXT"], printer_db["POSTSCRIPT"]]:
		node = qed_printer_ctree.insert_node(None, last_node, [printer['model']], is_leaf = 1)
		qed_printer_ctree.node_set_row_data(node, printer)
		printer['printer_ctree_node'] = node
		last_node = node

qed_driver_option_settings = None
def qed_driver_section_setup():
	called(qed_driver_section_setup)

	global qed_driver_option_settings, qed_driver_options_key

	# Clear the old driver settings
	qed_driver_option_settings = {}
	qed_clear_driver_option_table()

	qed_driver_option_settings[('page_size','__local_enum__')] = 'Letter'

	qed_driver_options_key = (None, None)

	# Colapse the printer tree
	for node in qed_printer_ctree.selection:
		qed_printer_ctree.unselect(node)
	for node in qed_printer_ctree.base_nodes():
		qed_printer_ctree.collapse(node)

	if qed_queue:
		filter_type = qed_queue['object'].getChildByName('filter_type').getValue()
		if filter_type == 'NONE':
			printer = printer_db['NONE']
			qed_show_printer(printer, None)

		elif filter_type == 'MAGICFILTER':
			filter_data = qed_queue['object'].getChildByName('filter_data')
			mf_type = filter_data.getChildByName('mf_type').getValue()

			# Read the flags list
			flag_hash = {}
			flags = filter_data.getChildByName('flags')
			for i in range(flags.getNumChildren()):
				flag = flags.getChildByIndex(i)
				flag_hash[flag.getName()] = flag.getValue()

			# Grab the ones we care about
			qed_driver_option_settings[('rerender_Postscript','__local_bool__')] = \
				flag_hash.get('rerender_Postscript', 0)
			qed_driver_option_settings[('send_EOT','__local_bool__')] = \
				flag_hash.get('send_EOT', 0)

			if mf_type == 'POSTSCRIPT':
				printer = printer_db['POSTSCRIPT']
				qed_driver_option_settings[('page_size','__local_enum__')] = \
					filter_data.getChildByName('page_size').getValue()
				qed_show_printer(printer, None)

			elif mf_type == 'TEXT':
				printer = printer_db['TEXT']
				qed_show_printer(printer, None)

			elif mf_type == 'MFOMATIC':
				printer_id = filter_data.getChildByName('printer_id').getValue()
				gs_driver = filter_data.getChildByName('gs_driver').getValue()

				q_foo_defs = filter_data.getChildByName("foomatic_defaults")
				for i in range(q_foo_defs.getNumChildren()):
					option = q_foo_defs.getChildByIndex(i);

					o_name = option.getChildByName("name").getValue()
					o_type = option.getChildByName("type").getValue()
					o_default = option.getChildByName("default").getValue()

					qed_driver_option_settings[(o_name,o_type)] = o_default;

				printer = printer_db.get(printer_id)
				# What driver? hmm, I'll guess postscript ...
				if not printer:
					printer = printer_db.get('POSTSCRIPT')
				qed_show_printer(printer, gs_driver)

				# Must happen after show printer
				qed_display_driver_options()

			else:
				return 0
	else:
		printer = printer_db['POSTSCRIPT']
		qed_show_printer(printer, None)

	return 1

def qed_driver_section_validate():
	called(qed_driver_section_validate)

	# Nothing to do here at the moment.
	return 1

def qed_driver_section_teardown(q_obj):
	called(qed_driver_section_teardown)

	printer = qed_printer_selected()
	driver = qed_driver_selected()

	printer_id = printer['id']

	# Make sure we have the most recent options
	qed_read_driver_option_settings()

	if printer_id == 'NONE':
		q_obj.getChildByName('filter_type').setValue('NONE')
		q_obj.getChildByName('filter_data').unlink()
		q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'filter_data')

	else:
		q_obj.getChildByName('filter_type').setValue('MAGICFILTER')

		# Clear the filter data
		q_obj.getChildByName('filter_data').unlink()
		f_data = q_obj.addChild(Alchemist.Data.ADM_TYPE_LIST, 'filter_data')

		# Set the magicfilter subtype
		if printer_id == 'POSTSCRIPT':
			mf_type = printer_id
			page_size = qed_driver_option_settings.get(('page_size', '__local_enum__'))
			f_data.addChild(Alchemist.Data.ADM_TYPE_STRING, 'page_size').setValue(page_size)

		elif printer_id == 'TEXT':
			mf_type = printer_id
		else:
			mf_type = 'MFOMATIC'
		f_data.addChild(Alchemist.Data.ADM_TYPE_STRING, 'mf_type').setValue(mf_type)

		# Set up the flags list
		flags = f_data.addChild(Alchemist.Data.ADM_TYPE_LIST, 'flags')
		if qed_driver_option_settings.get(('send_EOT','__local_bool__')) == 1:
			flags.addChild(Alchemist.Data.ADM_TYPE_BOOL, 'send_EOT').setValue(1)

		if printer_id != 'TEXT':
			if qed_driver_option_settings.get(('rerender_Postscript','__local_bool__')) == 1:
				flags.addChild(Alchemist.Data.ADM_TYPE_BOOL, 'rerender_Postscript').setValue(1)

		if printer_id == 'POSTSCRIPT' or printer_id == 'TEXT':
			return

		f_data.addChild(Alchemist.Data.ADM_TYPE_STRING, 'printer_id').setValue(printer_id)
		f_data.addChild(Alchemist.Data.ADM_TYPE_STRING, 'gs_driver').setValue(driver)

		foo_defs = f_data.addChild(Alchemist.Data.ADM_TYPE_LIST, 'foomatic_defaults')
		foo_defs.setAnonymous(1)

		foo_options = foomatic_options(printer_id, driver)
		if not foo_options:
			return

		for option in foo_options.values():
			f_name = option['name']
			f_type = option['type']
			f_def = option['default']

			q_def = qed_driver_option_settings.get((f_name, f_type))

			# Throw out empty defaults and 'default' defaults.
			# ( no point in overriding something to ... itself )
			if not q_def or q_def == f_def:
				continue

			# we are set for an enumerated default that this foomatic file doesn't know
			if f_type == 'enum' and not option['vals'].has_key(q_def):
				continue
			
			o_list = foo_defs.addChild(Alchemist.Data.ADM_TYPE_LIST, 'option_default')
			o_list.addChild(Alchemist.Data.ADM_TYPE_STRING, 'name').setValue(f_name)
			o_list.addChild(Alchemist.Data.ADM_TYPE_STRING, 'type').setValue(f_type)
			o_list.addChild(Alchemist.Data.ADM_TYPE_STRING, 'default').setValue(q_def)


qed_driver_options_table = program_glade_context.get_widget("qed_driver_options_table")
def qed_clear_driver_option_table():
	called(qed_clear_driver_option_table)

	for child in qed_driver_options_table.children():
		qed_driver_options_table.remove(child)
	qed_driver_options_table.resize(1,2)

qed_driver_options_key = (None, None)
def qed_display_driver_options():
	called(qed_display_driver_options)

	global qed_driver_options_key

	printer = qed_printer_selected()
	driver = qed_driver_selected()

	# If the driver options key matches the current key,
	# there is no need to redisplay
	if qed_driver_options_key == (printer, driver):
		return
	qed_driver_options_key = (printer, driver)

	#
	# clear the table, but read its old values first
	#
	qed_read_driver_option_settings()
	qed_clear_driver_option_table()

	# Read and display the foomatic options
	printer_id = printer['id']
	foo_options = foomatic_options(printer_id, driver)
	if foo_options:
		option_list = foo_options.values()
	else:
		option_list = []

	# Though the 'idx' field seems to contain some ordering information, it is inconsitant enough that the
	# interface is more useable if we sort by the comment field.
	option_list.sort(lambda x, y: cmp(_(x['comment']), _(y['comment'])))

	# If the printer id is 'POSTSCRIPT', we have to sythesise the ability to set page size, so that the enscripting
	# command has access to the page size. Nothing else will be done with the option. We are using 'mpage' as the
	# enscripter, so the values listed are those that 'mpage' understands.
	if printer_id == 'POSTSCRIPT':
		option_list.insert(0,{	'name':'page_size',
					'type':'__local_enum__',
					'default':'Letter',
					'comment':'Page Size',
					'idx':-1,
					'vals': {
						'Letter' : { 'value' : 'Letter', 'comment' : 'US Letter' },
						'Tabloid' : { 'value' : 'Tabloid', 'comment' : 'Tabloid' },
						'Ledger' : { 'value' : 'Ledger', 'comment' : 'Ledger' },
						'Legal' : { 'value' : 'Legal', 'comment' : 'Legal' },
						'Statement' : { 'value' : 'Statement', 'comment' : 'Statement' },
						'Executive' : { 'value' : 'Executive', 'comment' : 'Executive' },
						'A3' : { 'value' : 'A3', 'comment' : 'A3' },
						'A4' : { 'value' : 'A4', 'comment' : 'A4' },
						'A5' : { 'value' : 'A5', 'comment' : 'A5' },
						'B4' : { 'value' : 'B4', 'comment' : 'B4' },
						'B5' : { 'value' : 'B5', 'comment' : 'B5' },
						'Folio' : { 'value' : 'Folio', 'comment' : 'Folio' },
						'Quatro' : { 'value' : 'Quatro', 'comment' : 'Quatro' },
						'10x14' : { 'value' : '10x14', 'comment' : '10x14' }
						}
					})

	# The option to pass all postscript through ghostscript before it
	# goes to the filter or printer (for kanji)
	if printer_id != 'TEXT' and printer_id != 'NONE':
		option_list.insert(0,{	'name':'rerender_Postscript',
					'type':'__local_bool__',
					'default':0,
					'comment':'Rerender Postscript',
					'idx':-2})

	# Ask about the EOT character
	if printer_id != 'NONE':
		option_list.insert(0,{	'name':'send_EOT',
					'type':'__local_bool__',
					'default':0,
					'comment':'Send EOT',
					'idx':-3})

	option_widget_list = []
	for option in option_list:
		o_name = option['name']
		o_type = option['type']

		o_def = option['default']
		q_def = qed_driver_option_settings.get((o_name, o_type), None)
		if q_def:
			if o_type != 'enum' or option['vals'].has_key(q_def):
				o_def = q_def
		
		l_string = _(option['comment'])

		l_widget = gtk.GtkLabel(label = l_string)
		l_widget.set_alignment(0.0,0.5)

		if o_type == 'bool' or o_type == '__local_bool__':
			try:	val = int(o_def)
			except:	continue

			d_widget = gtk.GtkCheckButton()
			d_widget.set_active(val)

		elif o_type == 'int':
			try:
				val = int(o_def)
				min = int(option['min'])
				max = int(option['max'])
			except:	continue

			adjustment = gtk.GtkAdjustment(val, lower = min, upper = max, step_incr = 1, page_incr = 10)
			d_widget = gtk.GtkSpinButton(adjustment)
			d_widget.set_digits(0)
		
		elif o_type == 'float':
			try:
				val = float(o_def)
				min = float(option['min'])
				max = float(option['max'])
			except:	continue

			adjustment = gtk.GtkAdjustment(val, lower = min, upper = max, step_incr = 0.1, page_incr = 1)
			d_widget = gtk.GtkSpinButton(adjustment)
			d_widget.set_digits(1)
			
		elif o_type == 'enum' or o_type == '__local_enum__':
			val_list = option['vals'].values()
			val_list.sort(lambda x, y: cmp(_(x['comment']), _(y['comment'])))

			if not len(val_list):
				continue

			d_widget = gtk.GtkCombo()
			for val in val_list:
				item = gtk.GtkListItem(label = _(val['comment']))
				item.set_data('val', val)
				d_widget.list.append_items([item])
				item.show()

				if val['value'] == o_def:
					item.select()

		else:
			continue

		d_widget.set_data('option', option)

		option_widget_list.append((l_widget, d_widget))


	if not len(option_widget_list):
		label = gtk.GtkLabel(label = _("There are no options available for this driver."))
		qed_driver_options_table.attach(label,0,2,0,1,gtk.FILL)
		qed_driver_options_table.show_all()
		return

	qed_driver_options_table.resize(len(option_widget_list), 2)
	for i in range(len(option_widget_list)):
		(l_widget, d_widget) = option_widget_list[i]
		qed_driver_options_table.attach(l_widget,0,1,i,i+1,gtk.FILL)
		qed_driver_options_table.attach(d_widget,1,2,i,i+1)

	qed_driver_options_table.show_all()

def qed_read_driver_option_settings():
	called(qed_read_driver_option_settings)

	global qed_driver_option_settings
	for widget in qed_driver_options_table.children():
		option = widget.get_data('option')
		if not option:
			continue

		o_name = option['name']
		o_type = option['type']

		if o_type == '__local_bool__' or o_type == 'bool':
			val = widget.get_active()
			w_def = str(val)

		elif o_type == 'int':
			val = widget.get_value_as_int()
			w_def = str(val)

		elif o_type == 'float':
			val = widget.get_value_as_float()
			w_def = "%.1f" % (val)

		elif o_type == 'enum' or o_type == '__local_enum__':
			val = widget.list.get_selection()[0].get_data('val')
			w_def = val['value']

		else:
			continue

		debug_print('setting option: (%s,%s) = %s' % (o_name, o_type, w_def))
		qed_driver_option_settings[(o_name, o_type)] = w_def

def qed_printer_selected():
	called(qed_printer_selected)

	sel = qed_printer_ctree.selection
	if len(sel) == 0:
		return None
	return qed_printer_ctree.node_get_row_data(sel[0])

def qed_driver_selected():
	called(qed_driver_selected)

	menu = qed_driver_menu.get_menu()
	printer = qed_printer_selected()
	return printer['drivers'][menu.children().index(menu.get_active())]

def on_qed_printer_ctree_select_row(*args):
	called(on_qed_printer_ctree_select_row)

	printer = qed_printer_selected()

	if not printer:
		return None

	# distroy the old menu
	qed_driver_menu.remove_menu()

	menu = gtk.GtkMenu()
	for p_driver in printer['drivers']:
		menu_item = gtk.GtkMenuItem(label = p_driver)
		menu_item.show()
		menu.append(menu_item)

	# add the new menu
	qed_driver_menu.set_menu(menu)

def qed_printer_ctree_moveto_printer():
	called(qed_printer_ctree_moveto_printer)

	printer = qed_printer_selected()
	if not printer:
		return

	printer_node = printer['printer_ctree_node']
	if printer_node.parent:
		qed_printer_ctree.expand(printer_node.parent)

	qed_printer_ctree.node_moveto(printer_node, 0, 0.5, 0)

def qed_show_printer(printer, driver):
	called(qed_show_printer)
	
	printer_node = printer['printer_ctree_node']
	qed_printer_ctree.select(printer_node)
	try:
		qed_driver_menu.set_history(printer['drivers'].index(driver))
	except:
		pass

def strip_html(msg):
	called(strip_html)

	# This could be 'much' smarter.
	return re.sub(r'<[^^]*>', '', msg)

qed_notes_dialog = program_glade_context.get_widget('qed_notes_dialog')
qed_notes_dialog.close_hides(1)
qed_notes_dialog_text = program_glade_context.get_widget('qed_notes_dialog_text')
qed_notes_dialog_text.set_word_wrap(1)
def on_qed_printer_notes_button_clicked(*args):
	called(on_qed_printer_notes_button_clicked)

	printer = qed_printer_selected()
	if not printer:
		return

	notes = printer.get('notes')
	if notes:
		text = _("Notes from the Linux Printing Database\nhttp://www.linuxprinting.org\n\n") + \
			strip_html(printer['notes'])
	else:
		text = _("Sorry,\nthere are no notes available for this printer.")

	qed_notes_dialog_text.set_point(0)
	qed_notes_dialog_text.forward_delete(qed_notes_dialog_text.get_length())
	qed_notes_dialog_text.insert_defaults(text)
	qed_notes_dialog.run_and_close()

gtk_signal_handlers.update( {	'on_qed_printer_ctree_select_row' : on_qed_printer_ctree_select_row,
				'on_qed_printer_notes_button_clicked' : on_qed_printer_notes_button_clicked })

#-------------------------------------------------------------------------------------------------------
# Queue List Dialog Section (qld)

def init_qld():
	called(init_qld)
	
	qld_display_queue_list()

#---------------------------------------------------------------------
# qld queue list section
#

qld_queue_clist = program_glade_context.get_widget("qld_queue_clist")
qld_queue_clist.set_sort_column(1)
qld_queue_clist.set_auto_sort(1)
qld_queue_clist.set_column_width (0, 30)
qld_queue_clist.set_column_resizeable (0, 0)

# get the current selected queue
def qld_selected_queue():
	called(qld_selected_queue)
	
	sel = qld_queue_clist.selection
	if len(sel) == 0:
		return None
	return qld_queue_clist.get_row_data(sel[0])

# set the current selected queue
def qld_select_queue(queue):
	called(qld_select_queue)

	q_row = qld_queue_clist.find_row_from_data(queue)
	qld_queue_clist.select_row(q_row,0)
	qld_queue_clist.moveto(q_row, 0, 0.5, 0)

qld_queue_group = None
# Display the queues we are currently editing.
def qld_display_queue_list():
	called(qld_display_queue_list)

	# rebuild the queue group
	global qld_queue_group
	qld_queue_group = build_queue_group()

	# get the name of the currently selected queue.
	# We can't use the queue group, because the data may be
	# dead by this point :(
	sel = qld_queue_clist.selection
	if len(sel) == 0:
		old_name = None
	else:
		old_name = qld_queue_clist.get_text(sel[0], 1)

# Freeze --->
	qld_queue_clist.freeze()

	# clear the list
	qld_queue_clist.clear()

	q_list = qld_queue_group['queue_list']
	q_list.reverse()
	for queue in q_list:
		qld_display_queue(queue)

		# restore the 'right' selected queue (strange in the override case)
		if queue_name(queue) == old_name and queue['active']:
			qld_select_queue(queue)

	def_queue = qld_queue_group['default_queue']
	if def_queue:
		def_row = qld_queue_clist.find_row_from_data(def_queue)
		if def_queue['dynamic']:
			qld_queue_clist.set_pixmap(	def_row,
							0,
							images['printer-default'][0],
							images['printer-default'][1])
		elif def_queue['active']:
			qld_queue_clist.set_pixmap(	def_row,
							0,
							images['printer-default-static'][0],
							images['printer-default-static'][1])

# Thaw <---
	qld_queue_clist.thaw()


def qld_display_queue(queue):
	called(qld_display_queue)

	q_name = queue_name(queue)

	if not queue['valid']:
		row = qld_queue_clist.append(['', q_name, '', "INVALID", _("Invalid Printer Record")])
		qld_queue_clist.set_row_data(row, queue)
		return

	q_aliases = queue_alias_list(queue['object'])
	q_type = queue['object'].getChildByName('queue_type').getValue()
	q_description = gizmo_queue_description(queue['object'])

	row = qld_queue_clist.append(['', q_name, string.join(q_aliases, ', '), q_type, q_description])
	qld_queue_clist.set_row_data(row, queue)

	if queue['dynamic']:
		qld_queue_clist.set_text (row, 0, "")
	elif queue['active']:
		qld_queue_clist.set_pixmap(	row,
						0,
						images['printer-static'][0],
						images['printer-static'][1])
	else:
		qld_queue_clist.set_pixmap(	row,
						0,
						images['printer-overridden'][0],
						images['printer-overridden'][1])

		qld_queue_clist.set_selectable(row, 0)

#----------------------------------------------------------------
# the qld file menu
#

# Handler for the "Save Changes" option
def on_qld_save_activate(*args):
	called(on_qld_save_activate)

	dynamic_box.write(dynamic_ctx)
	clr_changed()

# Handler for the "Restart Lpd" option
def on_qld_restart_lpd_activate(*args):
	called(on_qld_restart_lpd_activate)

	if restart_lpd():
		gui_info(_("lpd restart succeeded"))
	else:
		gui_error(_("lpd restart failed"))

# Handler for the "Override Queue" option
def on_qld_override_activate(*args):
	called(on_qld_override_activate)

	queue = qld_selected_queue()
	if not queue:
		gui_error(_("You must select a printer queue to override."))
		return

	if queue['dynamic']:
		gui_error(_("You cannot override a locally defined printer."))
		return
		
	dynamic_ctx.getDataByPath('/printconf/print_queues').copyData(queue['object'])

	qld_display_queue_list()

	# mark ourselves dirty
	set_changed()

# Handler for the "Exit" option
def on_qld_exit_activate(*args):
	called(on_qld_exit_activate)

	if changed and not gui_ask(_("Exiting will discard your unsaved changes.\nContinue?")):
		# Returning '1' stalls the delete event :)
		return 1
	gtk.mainquit()

# Register the file menu option handlers
gtk_signal_handlers.update( {	'on_qld_save_activate' : on_qld_save_activate,
				'on_qld_restart_lpd_activate' : on_qld_restart_lpd_activate,
				'on_qld_override_activate' : on_qld_override_activate,
				'on_qld_exit_activate' : on_qld_exit_activate })

#----------------------------------------------------------------
# the qld test menu
#

# Handler for the "Print PostScript Test Page" option
def on_qld_ps_test_page_activate(*args):
	called(on_qld_ps_test_page_activate)

	queue = qld_selected_queue()
        if not queue:
                gui_error(_("You must select a printer queue\n to send a test page to."))
                return
	if print_letter_postscript_test_page(queue_name(queue)):
		gui_info(_("Sent a PostScript test page to \"%s\".") % (queue_name(queue)))
	else:
		gui_error(_("There was an error trying to print the test page."))

def on_qld_a4_ps_test_page_activate(*args):
	called(on_qld_a4_ps_test_page_activate)

	queue = qld_selected_queue()
        if not queue:
                gui_error(_("You must select a printer queue\n to send a test page to."))
                return
	if print_a4_postscript_test_page(queue_name(queue)):
		gui_info(_("Sent an A4 PostScript test page to \"%s\".") % (queue_name(queue)))
	else:
		gui_error(_("There was an error trying to print the test page."))

# Handler for the "Print Ascii Test Page" option
def on_qld_ascii_test_page_activate(*args):
	called(on_qld_ascii_test_page_activate)

	queue = qld_selected_queue()
        if not queue:
                gui_error(_("You must select a printer queue\n to send a test page to."))
                return
	if print_ascii_test_page(queue_name(queue)):
		gui_info(_("Sent an ASCII test page to \"%s\".") % (queue_name(queue)))
	else:
		gui_error(_("There was an error trying to print the test page."))

# Register the test menu handlers
gtk_signal_handlers.update( {	'on_qld_ps_test_page_activate' : on_qld_ps_test_page_activate,
				'on_qld_a4_ps_test_page_activate' : on_qld_a4_ps_test_page_activate,
				'on_qld_ascii_test_page_activate' : on_qld_ascii_test_page_activate })

#----------------------------------------------------------------
# The qld help menu
#

# Handler for the "About" option
def show_about(*args):
	called(show_about)

	about = gnome.ui.GnomeAbout(	title = "%s %s" % (program_name, program_version),
					list = program_authors,
					copyright = program_copyright,
					comments = program_comments)
	about.set_position(gtk.WIN_POS_CENTER)
	about.run()

gtk_signal_handlers['on_qld_about_activate'] = show_about

# Show off tammy's nice docs. Um, wait. How about, showcase tammy's well written documentation?
gtk_signal_handlers['on_qld_browse_help_activate'] = lambda *x: gnome.help.goto('%s/index.html' % doc_dir)

#----------------------------------------------------------------
# The qld menu bar
#

# Handler for the "New" Button on the qld menu bar
def on_qld_new_button_clicked(button, *args):
	called(on_qld_new_button_clicked)

	q_obj = qed_run()
	if q_obj:
		# mark ourselves dirty, if qed_run() succeds
		set_changed()

		# redisplay, and reselect the current queue
		q_name = q_obj.getName()
		qld_display_queue_list()
		queue = qld_queue_group['name_hash'][q_name][0]
		qld_select_queue(queue)

# Handler for the "Edit" Button on the qld menu bar
def on_qld_edit_button_clicked(button, *args):
	called(on_qld_edit_button_clicked)

	queue = qld_selected_queue()
	if not queue:
		gui_error(_("You must select a printer queue to edit."))
		return

	if not queue['dynamic']:
		gui_error(_("You cannot edit an imported printer."))
		return

	if not queue['valid']:
		gui_error(_("The printer's format is not understood, and can not be edited."))
		return

	q_obj = qed_run(queue)
	if q_obj:
		# mark ourselves dirty, if qed_run() succeds
		set_changed()

		# redisplay, and reselect the current queue
		q_name = q_obj.getName()
		qld_display_queue_list()
		queue = qld_queue_group['name_hash'][q_name][0]
		qld_select_queue(queue)

# Handler for the "Delete" Button on the qld menu bar
def on_qld_delete_button_clicked(button, *args):
	called(on_qld_delete_button_clicked)

	queue = qld_selected_queue()
	if not queue:
		gui_error(_("You must select a printer queue to delete."))
		return

	if not queue['dynamic']:
		gui_error(_("You cannot delete an imported printer."))
		return

	if not gui_ask(_("Are you sure you want to delete '%s'?") % queue_name(queue)):
		return

	q_name = queue_name(queue)
	queue['object'].unlink()

	# if this was the default queue, and there is not an
	# overriden queue with the same name, we fry it.
	if queue == queue['queue_group']['default_queue']:
		delete_def = 1
		for q in queue['queue_group']['queue_list']:
			if q != queue and queue_name(q) == q_name:
				delete_def = None
				break
			
		if delete_def:
			try:	dynamic_ctx.getDataByPath("/printconf/default_queue").unlink()
			except:	pass

	qld_display_queue_list()

	# mark ourselves dirty
	set_changed()

# Handler for the "Default" Button on the qld menu bar
def on_qld_default_button_clicked(button, *args):
	called(on_qld_default_button_clicked)

	queue = qld_selected_queue()
	if not queue:
		gui_error(_("You must select a printer queue\n to make the default."))
		return

	# this makes the queue list dirty
	try:	def_queue = dynamic_ctx.getDataByPath('/printconf/default_queue')
	except:	def_queue = dynamic_ctx.getDataByPath('/printconf').addChild(Alchemist.Data.ADM_TYPE_STRING, 'default_queue')
	def_queue.setValue(queue_name(queue))
	qld_display_queue_list()

	# mark ourselves dirty
	set_changed()

# Handler for the "Apply" Button on the qld menu bar
def on_qld_apply_button_clicked(button, *args):
	called(on_qld_apply_button_clicked)

	dynamic_box.write(dynamic_ctx)
	clr_changed()

	if restart_lpd():
		gui_info(_("lpd restart succeeded"))
	else:
		gui_error(_("lpd restart failed"))

# Register the qld menu bar buttons to be handled
gtk_signal_handlers.update( {	'on_qld_new_button_clicked' : on_qld_new_button_clicked,
				'on_qld_edit_button_clicked' : on_qld_edit_button_clicked,
				'on_qld_delete_button_clicked' : on_qld_delete_button_clicked,
				'on_qld_default_button_clicked' : on_qld_default_button_clicked,
				'on_qld_apply_button_clicked' : on_qld_apply_button_clicked } )


#-------------------------------------------------------------------------------------------------------
# Gui Section
#

# initialize the Queue List Dialog (the main window)
init_qld()

# initialize the Queue Edit Dialog
init_qed()


##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
## Literate Edit Barrier							  |
##										  |
## stuff above this line needs to be re-written to be below this line.		\ | /
## This is part of my scheme to not be hunted down by the next maintainer.	 \|/
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##
##-------------------------------------------------------------------------------------------------------------------##

# Gtk Signal Handler Connection
# -----------------------------
#
# All the gtk signal handlers are now collected in the gtk_signal_handler dictionary. A call to signal_autoconnect will
# wire them up to the gtk callbacks, and they will be 'real' signal handlers now. (You're a real boy now, pinochio!)
program_glade_context.signal_autoconnect(gtk_signal_handlers)


# Main Loop
# ---------
#
# Now that everything is defined, intialized, and conected, program execution can be handed over to gtk's mainloop, and
# callbacks and state machines described in therest of the program will take care of the rest.
gtk.mainloop()


# Prolog
# ------
#
# I would recommend, to anyone wanting to write their own printing system, that they invest their efforts into
# extending and supporting IPP (Internet Printing Protocol) based printing. Any serious attempt to 'fix' printing
# subsystems which use RFC1179 based protocols is doomed to failure, as the protocol lacks discovery, flexible media
# specifiaction, authentication, etc., and will probaly tweak your sanity.
#
# But then again, I was crazy before I started ;>
#				- Crutcher

