# Sketch - A Python-based interactive drawing program  
# Copyright (C) 1998, 1999, 2000, 2003 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


import PIL.Image
from X import GXxor, ZPixmap

from colorsys import hsv_to_rgb, rgb_to_hsv

from Sketch.const import CHANGED, ConstraintMask

from Sketch import _sketch
from Sketch import CreateRGBColor, StandardColors, Trafo, SketchError, \
     Publisher, _
from Sketch.Graphics import color

from Tkinter import Frame, Button, Label, DoubleVar, IntVar
from Tkinter import TOP, BOTTOM, LEFT, RIGHT, X, BOTH
from tkext import PyWidget
from miniscroll import create_mini_entry
from sketchdlg import SKModal

import skpixmaps
pixmaps = skpixmaps.PixmapTk



class MyDoubleVar(DoubleVar):

    def __init__(self, master = None, precision = 3):
	self.precision = precision
	DoubleVar.__init__(self, master)

    def set(self, value):
	DoubleVar.set(self, round(value, self.precision))

    def set_precision(self, precision):
	self.precision = precision


class ColorSample(PyWidget):

    def __init__(self, master=None, color = None, **kw):
	apply(PyWidget.__init__, (self, master), kw)
	self.gc_initialized = 0
	if color is None:
	    color = StandardColors.black
	self.color = color

    def MapMethod(self):
	if not self.gc_initialized:
	    self.init_gc()
	    self.gc_initialized = 1

    def init_gc(self):
	self.visual = color.skvisual
	self.set_color(self.color)

    def set_color(self, color):
	self.color = color
	self.tkwin.SetBackground(self.visual.get_pixel(self.color))
	self.UpdateWhenIdle()

    def SetColor(self, color):
	if self.color != color:
	    self.set_color(color)

    def RedrawMethod(self, region = None):
	self.tkwin.ClearArea(0, 0, 0, 0, 0)

    def ResizedMethod(self, width, height):
	pass


class ImageView(PyWidget):

    # display a PIL Image

    def __init__(self, master, image, **kw):
	width, height = image.size
	if not kw.has_key('width'):
	    kw["width"] = width
	if not kw.has_key('height'):
	    kw["height"] = height
	apply(PyWidget.__init__, (self, master), kw)
	self.gc_initialized = 0
	self.image = image
	self.ximage = None

    def MapMethod(self):
	if not self.gc_initialized:
	    self.init_gc()
	    self.gc_initialized = 1

    def init_gc(self):
	self.gc = self.tkwin.GetGC()
	self.visual = color.skvisual
	w = self.tkwin
	width, height = self.image.size
	depth = self.visual.depth
	if depth > 16:
	    bpl = 4 * width
	elif depth > 8:
	    bpl = ((2 * width + 3) / 4) * 4
	elif depth == 8:
	    bpl = ((width + 3) / 4) * 4
	else:
	    raise SketchError('unsupported depth for images')
	self.ximage = w.CreateImage(depth, ZPixmap, 0, None, width, height,
				    32, bpl)
	self.set_image(self.image)

    def set_image(self, image):
	self.image = image
	if self.ximage:
	    ximage = self.ximage
	    _sketch.copy_image_to_ximage(self.visual, image.im, ximage,
					 0, 0, ximage.width, ximage.height)
	    self.UpdateWhenIdle()

    def RedrawMethod(self, region = None):
	self.gc.PutImage(self.ximage, 0, 0, 0, 0,
			 self.ximage.width, self.ximage.height)

    def ResizedMethod(self, width, height):
	pass

class ChooseComponent(ImageView, Publisher):

    def __init__(self, master, width, height, color = (0, 0, 0), **kw):
	image = PIL.Image.new('RGB', (width, height))
	apply(ImageView.__init__, (self, master, image), kw)
	self.set_color(color)
	self.drawn = 0
	self.dragging = 0
	self.drag_start = (0, 0, 0)
	self.update_pending = 1
	self.invgc = None
	self.bind('<ButtonPress>', self.ButtonPressEvent)
	self.bind('<Motion>', self.PointerMotionEvent)
	self.bind('<ButtonRelease>', self.ButtonReleaseEvent)

    def destroy(self):
	ImageView.destroy(self)
	Publisher.Destroy(self)

    def set_color(self, color):
	self.color = tuple(color)

    def init_gc(self):
	ImageView.init_gc(self)
	self.invgc = self.tkwin.GetGC(foreground = ~0,
				      function = GXxor)
	self.tk.call(self._w, 'motionhints')
	self.show_mark()

    def ButtonPressEvent(self, event):
	if not self.dragging:
	    self.drag_start = self.color
	self.dragging = self.dragging + 1
	self.move_to(self.win_to_color(event.x, event.y), event.state)

    def ButtonReleaseEvent(self, event):
	self.dragging = self.dragging - 1
	self.move_to(self.win_to_color(event.x, event.y), event.state)

    def PointerMotionEvent(self, event):
	if self.dragging:
	    x, y = self.tkwin.QueryPointer()[4:6]
	    self.move_to(self.win_to_color(x, y), event.state)

    #def moveto(self, x, y): #to be supplied by derived classes

    def hide_mark(self):
	if self.drawn:
	    self.draw_mark()
	    self.drawn = 0

    def show_mark(self):
	if not self.drawn and self.invgc:
	    self.draw_mark()
	    self.drawn = 1

    #def draw_mark(self):	# to be supplied by derived classes

    def UpdateWhenIdle(self):
	if not self.update_pending:
	    self.update_pending = 1
	    ImageView.UpdateWhenIdle(self)

    def RedrawMethod(self, region = None):
	if self.update_pending:
	    self.update_ramp()
	    self.update_pending = 0
	ImageView.RedrawMethod(self, region)
	if self.drawn:
	    self.draw_mark()

    def RGBColor(self):
	return apply(CreateRGBColor, apply(hsv_to_rgb, self.color))

class ChooseRGBXY(ChooseComponent):

    def __init__(self, master, width, height, xcomp = 0, ycomp = 1,
		 color = (0, 0, 0), **kw):
	self.xcomp = xcomp
	self.ycomp = ycomp
	self.win_to_color = Trafo(1 / float(width - 1), 0,
				  0, -1 / float(height - 1),
				  0, 1)
	self.color_to_win = self.win_to_color.inverse()
	apply(ChooseComponent.__init__, (self, master, width, height, color),
	      kw)

    def SetColor(self, color):
	color = apply(rgb_to_hsv, tuple(color))
	otheridx = 3 - self.xcomp - self.ycomp
	if color[otheridx] != self.color[otheridx]:
	    self.UpdateWhenIdle()
	self.hide_mark()
	self.color = color
	self.show_mark()

    def update_ramp(self):
	_sketch.fill_hsv_xy(self.image.im, self.xcomp, self.ycomp, self.color)
	self.set_image(self.image)

    def move_to(self, p, state):
	x, y = p
	if state & ConstraintMask:
	    sx = self.drag_start[self.xcomp]
	    sy = self.drag_start[self.ycomp]
	    if abs(sx - x) < abs(sy - y):
		x = sx
	    else:
		y = sy
	if x < 0:	x = 0
	elif x >= 1.0:	x = 1.0
	if y < 0:	y = 0
	elif y >= 1.0:	y = 1.0

	color = list(self.color)
	color[self.xcomp] = x
	color[self.ycomp] = y
	self.hide_mark()
	self.color = tuple(color)
	self.show_mark()
	self.issue(CHANGED, self.RGBColor())

    def draw_mark(self):
        color = self.color
        w, h = self.image.size
        x, y = self.color_to_win(color[self.xcomp], color[self.ycomp])
        x = int(x)
        y = int(y)
        self.invgc.DrawLine(x, 0, x, h)
        self.invgc.DrawLine(0, y, w, y)


class ChooseRGBZ(ChooseComponent):

    def __init__(self, master, width, height, comp = 1, color = (0, 0, 0),
		 **kw):
	self.comp = comp
	self.win_to_color = Trafo(1, 0, 0, -1 / float(height - 1), 0, 1)
	self.color_to_win = self.win_to_color.inverse()
	apply(ChooseComponent.__init__, (self, master, width, height, color),
	      kw)

    def SetColor(self, color):
	c = self.color;
	color = apply(rgb_to_hsv, tuple(color))
	if ((self.comp == 0 and (color[1] != c[1] or color[2] != c[2]))
	    or (self.comp == 1 and (color[0] != c[0] or color[2] != c[2]))
	    or (self.comp == 2 and (color[0] != c[0] or color[1] != c[1]))):
	    self.hide_mark()
	    self.color = color
	    self.show_mark()
	    self.UpdateWhenIdle()

    def update_ramp(self):
	_sketch.fill_hsv_z(self.image.im, self.comp, self.color)
	self.set_image(self.image)

    def move_to(self, p, state):
	y = p.y
	if y < 0:	y = 0
	elif y >= 1.0:	y = 1.0

	color = list(self.color)
	color[self.comp] = y
	self.hide_mark()
	self.color = tuple(color)
	self.show_mark()
	self.issue(CHANGED, self.RGBColor())

    def draw_mark(self):
        w, h = self.image.size
        x, y = self.color_to_win(0, self.color[self.comp])
        x = int(x)
        y = int(y)
        self.invgc.DrawLine(0, y, w, y)


xyramp_size = (150, 150)
zramp_size = (15, 150)

class ChooseColorDlg(SKModal):

    title = _("Select Color")
    class_name = 'ColorDialog'

    def __init__(self, master, color, **kw):
	self.color = color
	self.orig_color = color
	apply(SKModal.__init__, (self, master), kw)

    def build_dlg(self):
	top = self.top

	frame = Frame(top)
	frame.pack(side = BOTTOM, fill = BOTH, expand = 1)
	button = Button(frame, text = _("OK"), command = self.ok)
	button.pack(side = LEFT, expand = 1)
	button = Button(frame, text = _("Cancel"), command = self.cancel)
	button.pack(side = RIGHT, expand = 1)

	self.label = Label(top)
	self.label.pack(side = BOTTOM)

	viewxy = ChooseRGBXY(top, xyramp_size[0], xyramp_size[1], 0, 1)
	viewxy.pack(side = LEFT)

	viewz = ChooseRGBZ(top, zramp_size[0], zramp_size[1], 2)
	viewz.pack(side = LEFT)

	frame1 = Frame(top)
	frame1.pack(side = RIGHT)

	frame = Frame(frame1)
	frame.pack(side = TOP)

	sample = ColorSample(frame, self.color, width = 30, height = 30)
	sample.pack(side = RIGHT)
	sample = ColorSample(frame, self.color, width = 30, height = 30)
	sample.pack(side = RIGHT)

	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "H")
	label.pack(side = LEFT)
	self.var1 = create_mini_entry(top, frame, self.component_changed,
				      min = 0, max = 1.0, step = 0.01,
				      vartype = MyDoubleVar)
	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "S")
	label.pack(side = LEFT)
	self.var2 = create_mini_entry(top, frame, self.component_changed,
				      min = 0, max = 1.0, step = 0.01,
				      vartype = MyDoubleVar)
	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "V")
	label.pack(side = LEFT)
	self.var3 = create_mini_entry(top, frame, self.component_changed,
				      min = 0, max = 1.0, step = 0.01,
				      vartype = MyDoubleVar)

	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "R")
	label.pack(side = LEFT)
	self.var4 = create_mini_entry(top, frame, self.rgb_component_changed,
				      min = 0, max = 255, step = 1,
				      vartype = IntVar)

	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "G")
	label.pack(side = LEFT)
	self.var5 = create_mini_entry(top, frame, self.rgb_component_changed,
				      min = 0, max = 255, step = 1,
				      vartype = IntVar)

	frame = Frame(frame1)
	frame.pack(side = TOP)
	label = Label(frame, text = "B")
	label.pack(side = LEFT)
	self.var6 = create_mini_entry(top, frame, self.rgb_component_changed,
				      min = 0, max = 255, step = 1,
				      vartype = IntVar)

	viewxy.Subscribe(CHANGED, self.color_changed)
	viewz.Subscribe(CHANGED, self.color_changed)
	self.viewxy = viewxy
	self.viewz = viewz
	self.sample = sample

	self.color_changed(self.color)

    def color_changed(self, color):
	self.label.configure(text = '%.3f %.3f %.3f' % tuple(color))
	self.viewxy.SetColor(color)
	self.viewz.SetColor(color)
	self.sample.SetColor(color)
	v1, v2, v3 = apply(rgb_to_hsv, tuple(color))
	self.var1.set(v1)
	self.var2.set(v2)
	self.var3.set(v3)
	self.var4.set(int(color[0]*255))
	self.var5.set(int(color[1]*255))
	self.var6.set(int(color[2]*255))
	self.color = color


    def component_changed(self, *rest):
	color = (self.var1.get(), self.var2.get(), self.var3.get())
	color = apply(CreateRGBColor, apply(hsv_to_rgb, color))
	self.color_changed(color)

    def rgb_component_changed(self, *rest):
        self.color_changed(CreateRGBColor(self.var4.get() / 255.0,
                                          self.var5.get() / 255.0,
                                          self.var6.get() / 255.0))



    def ok(self, *args):
	self.close_dlg(self.color)

def GetColor(master, color):
    dlg = ChooseColorDlg(master, color)
    return dlg.RunDialog()
