# Sketch - A Python-based interactive drawing program
# Copyright (C) 1998, 1999, 2000, 2002 by Bernhard Herzog
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#
#	PropertyPanel for plugin properties
#

#
# The parameters are described by a sequence of tuples like this
#
#	(NAME, TYPE, VALUE, RANGE, LABEL)
#
# LABEL is text suitable for a label in a dialog box. NAME is the
# internal name for that property. The constructor of the plugin object
# must accept a keyword argument NAME. Also, the plugin object must have
# a method CAPNAME to read the current value of that property and a
# method SetParameters accepting a dictionary of parameters. CAPNAME is
# a `Capitalized' version of the lowercase NAME (if NAME is
# `the_parameter', CAPNAME is `TheParameter')
#
# TYPE describes the type of the parameter, VALUE is the initial value
# and RANGE describes the valid values.
#
# Supported types:
#
#	type	range
#
#	int	(MIN, MAX)	The minimal and maximal value. MIN and/or MAX
#				may be None indicating no limit (apart from the
#				builtin limits).
#	float	(MIN, MAX)	same as for int.
#
#	length	(MIN, MAX)	A length in pt. The user can change the unit.
#
#	text	ignored		A text string (the range should be None for
#				now)
#

from string import split, join, capitalize

from Sketch.warn import warn, USER
from Sketch.const import SELECTION
from Sketch import _

from Tkinter import Label, IntVar, DoubleVar, StringVar, RIGHT, E
from tkext import MyEntry
from miniscroll import MiniScroller

from sketchdlg import PropertyPanel
from lengthvar import create_length_widgets



def capwords(ident):
    return join(map(capitalize, split(ident, '_')), '')

class Parameter:

    def __init__(self, panel, name, value, range, label):
	self.name = name
	self.value = value
	self.range = range
	self.label = label
	self.var = None
	self.panel = panel


    def build_widgets(self, master, row):
	label = Label(master, text = self.label)
	label.grid(row = row, column = 0, sticky = E)

    def __call__(self, *args):
	if args:
	    self.var.set(args[0])
	else:
	    return self.var.get()

parameter_types = {}

class NumberParameter(Parameter):

    def is_valid(self):
	value = self.var.get()
	min, max = self.range
	if min is not None and min > value:
	    return 0
	if max is not None and max < value:
	    return 0
	return 1

    def init_var(self):
	lo, hi = self.range
	value = self.value
	if lo is None:
	    if hi is not None:
		if value > hi:
		    value = hi
	else:
	    if hi is None:
		if value < lo:
		    value = lo
	    else:
		value = max(lo, min(hi, value))
	if value != self.value:
	    warn(USER,
		 'Initial value in parameter %s of plugin %s out of range',
		 self.name, self.panel.title)
	self.var.set(value)

    def var_changed(self, *rest):
	if self.is_valid():
	    self.panel.set_parameter(self)

    def build_widgets(self, master, row, build_entry = 1):
	self.init_var()
	Parameter.build_widgets(self, master, row)
	if build_entry:
	    entry = MyEntry(master, textvariable = self.var, justify = RIGHT,
			    width = 6, command = self.var_changed)
	    entry.grid(row = row, column = 1, sticky = 'ew')
	    min, max = self.range
	    scroll = MiniScroller(master, variable = self.var,
				  min = min, max = max, step = 1)
	    scroll.grid(row = row, column = 2, sticky = 'news')


class IntParameter(NumberParameter):

    def build_widgets(self, master, row):
	self.var = IntVar(master)
	NumberParameter.build_widgets(self, master, row)

parameter_types['int'] = IntParameter

class FloatParameter(NumberParameter):

    def build_widgets(self, master, row):
	self.var = DoubleVar(master)
	NumberParameter.build_widgets(self, master, row)

parameter_types['float'] = FloatParameter

class LengthParameter(NumberParameter):

    def build_widgets(self, master, row):
	self.var, entry, scroll, menu = create_length_widgets(master, master,
							      self.var_changed)
	NumberParameter.build_widgets(self, master, row, build_entry = 0)
	entry.grid(row = row, column = 1, sticky = 'ew')
	scroll.grid(row = row, column = 2, sticky = 'ns')
	menu.grid(row = row, column = 3)

parameter_types['length'] = LengthParameter

class TextParameter(Parameter):

    def build_widgets(self, master, row):
	Parameter.build_widgets(self, master, row)
	self.var = StringVar(master)
	self.var.set(self.value)
	entry = MyEntry(master, textvariable = self.var, width = 0,
			command = self.var_changed)
	entry.grid(row = row, column = 1, columnspan = 3, sticky = 'ew')

    def var_changed(self, *rest):
	self.panel.set_parameter(self)

parameter_types['text'] = TextParameter

class PluginPanel(PropertyPanel):

    def __init__(self, master, main_window, doc, info):
	self.var_auto_update = IntVar(master)
	self.var_auto_update.set(1)
	self.info = info
	self.title = info.menu_text
	self.vars = []
	name = 'dlg' + info.class_name
	PropertyPanel.__init__(self, master, main_window, doc, name = name)

    def build_dlg(self):
	top = self.top
	row = 0

	for row in range(len(self.info.parameters)):
	    name, type, value, prange, label = self.info.parameters[row]
	    try:
		#print name, type, value, prange, label
		var = parameter_types[type](self, name, value, prange, label)
		var.build_widgets(top, row)
		self.vars.append(var)
	    except KeyError:
		warn(USER, 'Unknown plugin parameter type %s' % type)
		continue
	row = row + 1
	top.columnconfigure(0, weight = 0)
	top.columnconfigure(1, weight = 1)
	top.columnconfigure(2, weight = 0)
	top.columnconfigure(3, weight = 0)


	frame = self.create_std_buttons(top)
	frame.grid(row = row, columnspan = 4, sticky = 'ew')

    def set_parameter(self, var):
	if not self.current_is_plugin():
	    return
	doc = self.document
	doc.BeginTransaction(_("Set %s") % var.label) # NLS
	try:
	    try:
		kw = {var.name: var()}
		doc.AddUndo(self.current_object().SetParameters(kw))
	    except:
		doc.AbortTransaction()
	finally:
	    doc.EndTransaction()

    def can_apply(self):
	return 1

    def do_apply(self):
	if self.current_is_plugin():
	    doc = self.document
	    doc.BeginTransaction(_("Set Parameters"))
	    try:
		try:
		    kw = {}
		    for var in self.vars:
			kw[var.name] = var()
		    doc.AddUndo(self.current_object().SetParameters(kw))
		except:
		    doc.AbortTransaction()
	    finally:
		doc.EndTransaction()
	else:
	    # create that object
	    dict = {}
	    for var in self.vars:
		dict[var.name] = var()
	    obj = apply(self.info.Constructor(), (), dict)
	    text = 'Create ' + self.info.menu_text # NLS
	    self.main_window.canvas.PlaceObject(obj, text)


    def current_is_plugin(self):
	o = self.document.CurrentObject()
	return o is not None and o.is_Plugin and \
	       o.class_name == self.info.class_name

    def current_object(self):
	return self.document.CurrentObject()

    def init_from_doc(self):
	self.Update()
	self.issue(SELECTION)

    def Update(self):
	if self.current_is_plugin():
	    object = self.current_object()
	    for var in self.vars:
		var(getattr(object, capwords(var.name))())

    def update_from_object_cb(self, obj):
        print 'update_from_object_cb', obj
        while not obj.is_Plugin and not obj.is_Layer:
            obj = obj.parent
	if obj.is_Plugin and obj.class_name == self.info.class_name:
            for var in self.vars:
                print 'update_from_object_cb', var
		var(getattr(obj, capwords(var.name))())

    def close_dlg(self):
	self.vars = []
	PropertyPanel.close_dlg(self)
