/* +-------------------------------------------------------------------+ */
/* | Copyright 1993, David Koblas (koblas@netcom.com)                  | */
/* |                                                                   | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.  There is no           | */
/* | representations about the suitability of this software for        | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.                                              | */
/* |                                                                   | */
/* +-------------------------------------------------------------------+ */

#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Text.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>

#ifndef NOSTDHDRS
#include <stdlib.h>
#include <unistd.h>
#endif

#include "palette.h"

#include <math.h>

/*
**  xpaint.h for MIN, MAX
**  misc.h   for GetShell
*/
#include "xpaint.h"
#include "misc.h"
#include "image.h"

#ifndef M_PI
#define M_PI            3.14159265358979323846
#endif

#define PIXMAP_WIDTH  120
#define PIXMAP_HEIGHT 120
#define RADIUS	      50

#define RECOMPUTE(l)							\
		if (l->isRGB) {						\
			rgbTOhsv(l->r,l->g,l->b,&l->h,&l->s,&l->v);	\
		} else {						\
			hsvTOrgb(l->h,l->s,l->v,&l->r,&l->g,&l->b);	\
		}

typedef void	(*cbFunc_t)(Widget, XtPointer, Pixel);

typedef struct LocalInfo_s {
	Boolean	initing, inCallback;
	Widget	form;

	Widget	valueBar, redBar, greenBar, blueBar;

	/*
	**  Color image picker widget, and delta x, y;
	**   also a copy gc, and the image
	*/
	GC		gc, xgc;
	Pixmap		pixmap;
	Widget		picker;
	int		dx, dy;

	/*
	**  Static current view
	*/
	Widget		view;
	Pixel		pixel;
	Colormap	cmap;

	/*
	**
	*/
	Widget	valueText, redText, greenText, blueText;
	Boolean	textChange;

	Boolean	isRGB;
	float	h,s,v;
	float	r,g,b;

	Palette			*map;
	cbFunc_t		func;
	XtPointer		data;

	struct LocalInfo_s	*next;
} LocalInfo;

static LocalInfo	*head = NULL;

static void rgbTOhsv(float r, float g, float b, float *h, float *s, float *v)
{
	float	max = MAX(r,MAX(g,b));
	float	min = MIN(r,MIN(g,b));
	float	delta;

	*v = max;
	if (max != 0.0)
		*s = (max - min) / max;
	else
		*s = 0.0;

	if (*s == 0) {
		*h = 0;
	} else {
		delta = max - min;
		if (r == max) 
			*h = (g - b) / delta;
		else if (g == max)
			*h = 2 + (b - r) / delta;
		else if (b == max)
			*h = 4 + (r - g) / delta;
		*h *= 60;
		if (*h < 0.0)
			*h += 360;
	}
}

static void hsvTOrgb(float h, float s, float v, float *r, float *g, float *b)
{
	int	i;
	float	f,p,q,t;

	if (s == 0 && h == 0) {
		*r = *g = *b = v;
	} else {
		if (h >= 360.0)
			h = 0.0;
		h /= 60.0;

		i = h;
		f = h - i;
		p = v * (1 - s);
		q = v * (1 - (s * f));
		t = v * (1 - (s * (1 - f)));
		switch (i) {
		case 0: *r = v; *g = t; *b = p; break;
		case 1: *r = q; *g = v; *b = p; break;
		case 2: *r = p; *g = v; *b = t; break;
		case 3: *r = p; *g = q; *b = v; break;
		case 4: *r = t; *g = p; *b = v; break;
		case 5: *r = v; *g = p; *b = q; break;
		}
	}
}

/*
**  Event callback routines
*/
static void drawCursor(LocalInfo *l, Boolean flag)
{
	Dimension	width, height;
	int		sx, sy;

	if (!XtIsRealized(l->picker))
		return;

	XtVaGetValues(l->picker, XtNwidth, &width, XtNheight, &height, NULL);

	sx = -l->dx - 3 + width / 2;
	sy = -l->dy - 3 + height / 2;

	if (flag) {
		XDrawArc(XtDisplay(l->picker), XtWindow(l->picker), l->xgc,
				sx, sy, 6, 6, 0, 360*64);
	} else {
		int	cx, cy;
		cx = PIXMAP_WIDTH  / 2;
		cy = PIXMAP_HEIGHT / 2;
		
		XCopyArea(XtDisplay(l->picker), l->pixmap, XtWindow(l->picker), l->gc,
				-l->dx + cx - 4, -l->dy + cy - 4,
				8, 8, sx - 1, sy - 1);
	}
}

static void update(LocalInfo *l, Boolean doSB, Boolean doText, Boolean doCursor)
{
	XColor	col;
	Pixel	p;

	if (doSB) {
		XawScrollbarSetThumb(l->valueBar, l->v, -1.0);
		XawScrollbarSetThumb(l->redBar, l->r, -1.0);
		XawScrollbarSetThumb(l->greenBar, l->g, -1.0);
		XawScrollbarSetThumb(l->blueBar, l->b, -1.0);
	}
	if (doText) {
		char	buf[20];

		sprintf(buf,"%5.3f",l->v);
		XtVaSetValues(l->valueText, XtNstring, buf, NULL);
		sprintf(buf,"%d",(int)(l->r * 255));
		XtVaSetValues(l->redText, XtNstring, buf, NULL);
		sprintf(buf,"%d",(int)(l->g * 255));
		XtVaSetValues(l->greenText, XtNstring, buf, NULL);
		sprintf(buf,"%d",(int)(l->b * 255));
		XtVaSetValues(l->blueText, XtNstring, buf, NULL);
	}
	if (doCursor) {
		drawCursor(l, False);

		l->dx = sin(l->h * M_PI / 180.0) * l->s * RADIUS;
		l->dy = cos(l->h * M_PI / 180.0) * l->s * RADIUS;

		drawCursor(l, True);
	}

	if (l->initing)
		return;

	col.flags = DoRed|DoGreen|DoBlue;
	col.red   = l->r * 0xffff;
	col.green = l->g * 0xffff;
	col.blue  = l->b * 0xffff;
	col.pixel = l->pixel;
	p         = l->pixel;
	if (l->map == NULL) {
		XStoreColor(XtDisplay(l->view), l->cmap, &col);
	} else {
		if (!l->map->readonly)
			XStoreColor(XtDisplay(l->view), l->cmap, &col);
		else
			XtVaSetValues(l->view, XtNbackground, 
					p = PaletteAlloc(l->map, &col), NULL);
	}
	if (l->func != NULL) {
		l->inCallback = True;
		l->func(l->form, l->data, p);
		l->inCallback = False;
	}
}

static void expose(Widget w, LocalInfo *l, XExposeEvent *event, Boolean *flg)
{
	Dimension	width, height;
	int		dx, dy;

	XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL);

	dx = (PIXMAP_WIDTH - width)   / 2;
	dy = (PIXMAP_HEIGHT - height) / 2;

	XCopyArea(XtDisplay(w), l->pixmap, XtWindow(w), l->gc,
			event->x + dx, event->y + dy, 
			event->width, event->height,
			event->x, event->y);
	drawCursor(l, True);
}

static void motion(Widget w, LocalInfo *l, XEvent *event, Boolean *flg)
{
	Dimension	width, height;
	int		cx, cy;
	int		dx, dy, dx2, dy2;

	while (XCheckTypedWindowEvent(XtDisplay(w), XtWindow(w), MotionNotify, (XEvent*)event));

	XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL);

	cx = width / 2;
	cy = height / 2;

	if (event->type == ButtonPress) {
		dx = cx - event->xbutton.x;
		dy = cy - event->xbutton.y;
	} else {
		dx = cx - event->xmotion.x;
		dy = cy - event->xmotion.y;
	}
	dx2 = dx * dx;
	dy2 = dy * dy;


	if (dx == 0 && dy == 0)
		l->h = 0;
	else if ((l->h = atan2((double)dx, (double)dy)) < 0.0)
		l->h += 2*M_PI;
	l->s = sqrt((double)(dx2 + dy2)) / RADIUS;
	l->h *= 360.0 / (2*M_PI);

	if (l->s > 1.0) {
		l->s = 1.0;
		dx = sin(l->h * M_PI / 180.0) * RADIUS;
		dy = cos(l->h * M_PI / 180.0) * RADIUS;
	}

	drawCursor(l, False);
	l->dx = dx;
	l->dy = dy;
	drawCursor(l, True);

	l->isRGB = False;
	RECOMPUTE(l);
	update(l, True, True, False);
}

static void barCB(Widget w, LocalInfo *l, float *percent)
{
	if (l->redBar == w) {
		l->r = *percent;
		l->isRGB = True;
		RECOMPUTE(l);
	} else if (l->greenBar == w) {
		l->g = *percent;
		l->isRGB = True;
		RECOMPUTE(l);
	} else if (l->blueBar == w) {
		l->b = *percent;
		l->isRGB = True;
		RECOMPUTE(l);
	} else if (l->valueBar == w) {
		l->v = *percent;
		l->isRGB = False;
		RECOMPUTE(l);
	}

	update(l, True, True, l->isRGB);
}

static void textExitCB(Widget w, LocalInfo *l, XtPointer junk)
{
	String	val;
	int	v;

#if 0
	if (!l->textChange)
		return;
#endif

	XtVaGetValues(w, XtNstring, &val, NULL);
	if (w != l->valueText) {
		v = atoi(val);
		if (v > 255) {
			v = 255;
			XtVaSetValues(w, XtNstring, "255", NULL);
		}
		if (v < 0) {
			v = 0;
			XtVaSetValues(w, XtNstring, "0", NULL);
		}

		if (w == l->redText) {
			if ((int)(l->r * 255.0) == v)
				return;
			l->r = v / 255.0;
			l->isRGB = True;
			RECOMPUTE(l);
		}
		if (w == l->greenText) {
			if ((int)(l->g * 255.0) == v)
				return;
			l->g = v / 255.0;
			l->isRGB = True;
			RECOMPUTE(l);
		}
		if (w == l->blueText) {
			if ((int)(l->b * 255.0) == v)
				return;
			l->b = v / 255.0;
			l->isRGB = True;
			RECOMPUTE(l);
		}
	} else {
		float	f = atof(val);

		/*
		**  Same to 3 decimal places?
		*/
		if ((int)(f * 1000) == (int)(l->v * 1000))
			return;

		l->v = f;
		l->isRGB = False;
		RECOMPUTE(l);
	}

	update(l, True, True, l->isRGB);
	l->textChange = False;
}

static void textAction(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
	Widget		cpick;
	LocalInfo       *l = head;

	cpick = w;
	while (cpick != None && strcmp(XtName(cpick), "colorPicker") != 0)
		cpick = XtParent(cpick);

	if (cpick == None)
		return;

        for (; l != NULL && l->form != cpick; l = l->next);

	textExitCB(w, l, NULL);
}

#if 0
static void textCB(Widget w, LocalInfo *l, XtPointer junk)
{
	l->textChange = True;
}
#endif

/*
**  Grab the pixel value from some other window
**
**   General strategy:
**     Grab the cursor
**     Wait for the up/down button event
**     Lookup what window the event is over
**     Query the pixel value
**     Query the colormap of the window
**     Query the rgb pixel value
*/
static void grabCB(Widget w, LocalInfo *l, XtPointer junk)
{
	XColor			*xcol = DoGrabColor(w);

	l->isRGB = True;
	l->r = (float)((xcol->red >> 8)   & 0xff) / (float)0xff;
	l->g = (float)((xcol->green >> 8) & 0xff) / (float)0xff;
	l->b = (float)((xcol->blue >> 8)  & 0xff) / (float)0xff;

	RECOMPUTE(l);
	update(l, True, True, True);
}

/*
**  Create a nice little color wheel that we chan pick colors from
**
**   Create it as an Image so that we can compress it down to the
**    supported number of colors, after we've allocated "perfect" ones
**
*/
static void drawInPixmap(Widget w, Palette *map, Colormap cmap, 
				Pixel bg, Pixmap pix, GC gc)
{
	XImage		*xim = XGetImage(XtDisplay(w), pix, 0, 0, 
						PIXMAP_WIDTH, PIXMAP_HEIGHT,
						AllPlanes, ZPixmap);
	int		offX = PIXMAP_WIDTH / 2 - RADIUS;
	int		offY = PIXMAP_HEIGHT / 2 - RADIUS;
	float		r, g, b;
	float		h, s;
	int		x, y;
	static Image	*image = NULL;
	unsigned char	*ip;
	int		i;
	XColor		xcol[64];
	Pixel		pval[XtNumber(xcol)];
	int		backgroundIndex = 0;
	Boolean		isTrue = (map != NULL && !map->isMapped);

	StateSetBusyWatch(True);

	if (image == NULL || isTrue) {
		image = ImageNew(2 * RADIUS, 2 * RADIUS);
		ip = image->data;

		for (y = 0; y < 2*RADIUS; y++) {
			for (x = 0; x < 2*RADIUS; x++) {
				int	dx, dy, dx2, dy2;

				dx = RADIUS - x;	dx2 = dx * dx;
				dy = RADIUS - y;	dy2 = dy * dy;

				if (dx2 + dy2 > RADIUS*RADIUS) {
					*ip++ = 0;
					*ip++ = 0;
					*ip++ = 0;
					continue;
				}

				if (dx == 0 && dy == 0)
					h = 0;
				else if ((h = atan2((double)dx, (double)dy)) < 0.0)
					h += 2*M_PI;
				s = sqrt((double)(dx2 + dy2)) / RADIUS;

				h *= 360.0 / (2*M_PI);

				hsvTOrgb(h, s, 1.0, &r, &g, &b);
				
				*ip++ = r * 255;
				*ip++ = g * 255;
				*ip++ = b * 255;
				if (isTrue) {
					XColor	c;
					c.red   = r * 0xffff;
					c.green = g * 0xffff;
					c.blue  = b * 0xffff;
					XPutPixel(xim, offX + x, offY + y, 
							PaletteAlloc(map, &c));
				}
			}
		}
		if (isTrue) {
			ImageDelete(image);
			image = NULL;
		} else {
			image = ImageCompress(image, XtNumber(xcol));
		}
	}

	if (!isTrue) {
		for (i = 0; i < image->cmapSize; i++) {
			xcol[i].flags = DoRed|DoGreen|DoBlue;
			xcol[i].red = image->cmapData[i*3+0] * 256;
			xcol[i].green = image->cmapData[i*3+1] * 256;
			xcol[i].blue = image->cmapData[i*3+2] * 256;
			if (xcol[i].red == xcol[i].green && 
			    xcol[i].green == xcol[i].blue &&
			    xcol[i].red == 0) {
				backgroundIndex = i;
				continue;
			}

			if (map == NULL) {
				XAllocColor(XtDisplay(w), cmap, &xcol[i]);
				pval[i] = xcol[i].pixel;
			}
		}
	
		if (map != NULL)
			PaletteAllocN(map, xcol, image->cmapSize, pval);

		for (ip = image->data, y = 0; y < 2*RADIUS; y++) {
			for (x = 0; x < 2*RADIUS; x++, ip++) {
				if (*ip == backgroundIndex)
					XPutPixel(xim, offX + x, offY + y, bg);
				else
					XPutPixel(xim, offX + x, offY + y, pval[*ip]);
			}
		}
	}
	
	XPutImage(XtDisplay(w), pix, gc, xim, 0, 0, 0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
	XDestroyImage(xim);

	StateSetBusyWatch(False);
}

static Widget createBarText(Widget parent, Widget above, String msg,
				Widget *bar, Widget *text)
{
	static String	textTranslations = "#override\n\
					 <Key>Return: color-text-ok()\n\
					 <Key>Linefeed: color-text-ok()\n\
					 Ctrl<Key>M: color-text-ok()\n\
					 Ctrl<Key>J: color-text-ok()\n";
	static XtTranslations	trans = None;
	Widget			subform, label;

	if (trans == None) {
		static XtActionsRec     act = { "color-text-ok", (XtActionProc)textAction };

		XtAppAddActions(XtWidgetToApplicationContext(parent), &act, 1);

		trans = XtParseTranslationTable(textTranslations);
	}

	subform = XtVaCreateManagedWidget("form", formWidgetClass, parent, 
				XtNborderWidth, 0,
				XtNfromVert, above,
				NULL);
	label = XtVaCreateManagedWidget("valueLabel", labelWidgetClass, subform,
				XtNlabel, msg,
				XtNborderWidth, 0,
				XtNright, XtChainLeft,
				XtNleft, XtChainLeft,
				NULL);
	*bar = XtVaCreateManagedWidget("valueBar", scrollbarWidgetClass, subform,
				XtNorientation, XtorientHorizontal,
				XtNlength, 200,
				XtNfromHoriz, label,
				XtNleft, XtChainLeft,
				NULL);
	*text = XtVaCreateManagedWidget("valueText", asciiTextWidgetClass, subform,
				XtNfromHoriz, *bar,
				XtNeditType, XawtextEdit,
				XtNwrap, XawtextWrapNever,
				XtNresize, XawtextResizeWidth,
				XtNtranslations, trans,
				XtNwidth, 50,
				XtNlength, 5,
				NULL);
	return subform;
}

static void freePixel(Widget w, LocalInfo *l)
{
	XFreeColors(XtDisplay(w), l->cmap, &l->pixel, 1, 0);
}

static LocalInfo *colorPicker(Widget parent, Colormap cmap, Pixel *pixval)
{
	Widget		top = GetToplevel(parent);
	Widget		form, picker, subform;
	Widget		button;
	LocalInfo	*l = (LocalInfo*)XtNew(LocalInfo);
	XGCValues	gcvalues;

	if (cmap == None)
		cmap = DefaultColormapOfScreen(XtScreen(parent));

	l->initing = True;
	l->inCallback = False;
	l->func = NULL;
	
	l->cmap = cmap;

	form = XtVaCreateManagedWidget("colorPicker", formWidgetClass, parent, NULL);

	/*
	**  The HS space image
	*/
	picker = XtVaCreateManagedWidget("palette", coreWidgetClass, form, 
				XtNwidth, PIXMAP_WIDTH,
				XtNheight, PIXMAP_WIDTH,
				NULL);
	l->picker = picker;

	XtAddEventHandler(picker, ExposureMask, False, (XtEventHandler)expose, (XtPointer)l);
	XtAddEventHandler(picker, ButtonPressMask, False, (XtEventHandler)motion, (XtPointer)l);
	XtAddEventHandler(picker, ButtonMotionMask, False, (XtEventHandler)motion, (XtPointer)l);

	gcvalues.foreground = BlackPixel(XtDisplay(top), DefaultScreen(XtDisplay(top)));
	l->xgc = XtGetGC(picker, GCForeground, &gcvalues);

	/*
	**  The color "view"
	*/
	l->pixel = None;
	if (pixval == NULL) {
		if (XAllocColorCells(XtDisplay(top), cmap, False, NULL, 0, &l->pixel, 1)) {
			XColor	col;
			col.pixel = l->pixel;
			col.flags = DoRed|DoGreen|DoBlue;
			col.red   = 0xffff;
			col.green = 0xffff;
			col.blue  = 0xffff;
			XStoreColor(XtDisplay(top), cmap, &col);

		        XtAddCallback(form, XtNdestroyCallback, (XtCallbackProc)freePixel, (XtPointer)l);
		}
	} else {
		l->pixel = *pixval;
	}
	l->view = XtVaCreateManagedWidget("view", coreWidgetClass, form,
				XtNwidth, 40,
				XtNheight, 40,
				XtNfromHoriz, picker,
				XtNbackground, l->pixel,
				NULL);

	button = XtVaCreateManagedWidget("match", commandWidgetClass, form,
				XtNfromVert, l->view,
				XtNfromHoriz, picker,
				NULL);
	XtAddCallback(button, XtNcallback, (XtCallbackProc)grabCB, (XtPointer)l);
			
	/*
	**  Now create the scroll bars
	*/
	subform = createBarText(form, picker,  "Value", &l->valueBar, &l->valueText);
	subform = createBarText(form, subform, "Red", &l->redBar, &l->redText);
	subform = createBarText(form, subform, "Green", &l->greenBar, &l->greenText);
	subform = createBarText(form, subform, "Blue", &l->blueBar, &l->blueText);

	XtAddCallback(l->valueBar, XtNjumpProc, (XtCallbackProc)barCB, (XtPointer)l);
	XtAddCallback(l->redBar, XtNjumpProc, (XtCallbackProc)barCB, (XtPointer)l);
	XtAddCallback(l->greenBar, XtNjumpProc, (XtCallbackProc)barCB, (XtPointer)l);
	XtAddCallback(l->blueBar, XtNjumpProc, (XtCallbackProc)barCB, (XtPointer)l);

#if 0
	XtAddCallback(l->valueText, XtNcallback, (XtCallbackProc)textCB, (XtPointer)l);
	XtAddCallback(l->redText,   XtNcallback, (XtCallbackProc)textCB, (XtPointer)l);
	XtAddCallback(l->greenText, XtNcallback, (XtCallbackProc)textCB, (XtPointer)l);
	XtAddCallback(l->blueText,  XtNcallback, (XtCallbackProc)textCB, (XtPointer)l);
#endif

	l->textChange = False;
	l->isRGB = True;
	l->r = l->g = l->b = 1.0;
	RECOMPUTE(l);
	update(l, True, True, True);

	l->form = form;
	l->next = head;
	head = l;

	return l;
}

Widget ColorPickerPalette(Widget parent, Palette *map, Pixel *pixval)
{
	LocalInfo	*l = colorPicker(parent, map->cmap, pixval);
	XGCValues	gcvalues;
	Cardinal	depth;

	XtVaGetValues(l->picker, XtNbackground, &gcvalues.foreground, XtNdepth, &depth, NULL);
	l->pixmap = XCreatePixmap(XtDisplay(l->picker), 
				DefaultRootWindow(XtDisplay(l->picker)), 
				PIXMAP_WIDTH, PIXMAP_HEIGHT, depth);
	l->gc = XtGetGC(l->picker, GCForeground, &gcvalues);
	XFillRectangle(XtDisplay(l->picker), l->pixmap, l->gc, 
				0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
	l->map = map;
	drawInPixmap(l->picker, map, l->cmap, gcvalues.foreground, l->pixmap, l->gc);

	l->initing = False;
	return l->form;
}

Widget ColorPicker(Widget parent, Colormap cmap, Pixel *pixval)
{
	LocalInfo	*l = colorPicker(parent, cmap, pixval);
	XGCValues	gcvalues;
	Cardinal	depth;

	l->map = NULL;

	XtVaGetValues(l->picker, XtNbackground, &gcvalues.foreground, XtNdepth, &depth, NULL);
	l->pixmap = XCreatePixmap(XtDisplay(l->picker), 
				DefaultRootWindow(XtDisplay(l->picker)), 
				PIXMAP_WIDTH, PIXMAP_HEIGHT, depth);
	l->gc = XtGetGC(l->picker, GCForeground, &gcvalues);
	XFillRectangle(XtDisplay(l->picker), l->pixmap, l->gc, 
				0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
	drawInPixmap(l->picker, NULL, l->cmap, gcvalues.foreground, l->pixmap, l->gc);

	l->initing = False;
	return l->form;
}

void ColorPickerShell(Widget w)
{
	Widget		top = GetToplevel(w);
	Widget		shell;
	Colormap	cmap = DefaultColormapOfScreen(XtScreen(top));

	shell = XtVaCreatePopupShell("color", transientShellWidgetClass, 
					GetShell(w), NULL);

	ColorPicker(shell, cmap, NULL);

	XtPopup(shell, XtGrabNone);
}

void ColorPickerSetXColor(Widget w, XColor *xcol)
{
	LocalInfo	*l = head;

	for (; l != NULL && l->form != w; l = l->next);

	if (l == NULL || l->inCallback)
		return;

	l->isRGB = True;
	l->r = (float)((xcol->red >> 8)   & 0xff) / (float)0xff;
	l->g = (float)((xcol->green >> 8) & 0xff) / (float)0xff;
	l->b = (float)((xcol->blue >> 8)  & 0xff) / (float)0xff;

	RECOMPUTE(l);
	update(l, True, True, True);
}

void ColorPickerSetPixel(Widget w, Pixel pix)
{
	LocalInfo	*l = head;
	XColor		xcol;

	for (; l != NULL && l->form != w; l = l->next);

	if (l == NULL || l->inCallback)
		return;

	XtVaSetValues(l->view, XtNbackground, pix, NULL);

	xcol.pixel = pix;
	XQueryColor(XtDisplay(w), l->cmap, &xcol);

	l->isRGB = True;
	l->r = (float)((xcol.red >> 8)   & 0xff) / (float)0xff;
	l->g = (float)((xcol.green >> 8) & 0xff) / (float)0xff;
	l->b = (float)((xcol.blue >> 8)  & 0xff) / (float)0xff;
	l->pixel = pix;

	RECOMPUTE(l);
	update(l, True, True, True);
}

void ColorPickerSetFunction(Widget w, XtCallbackProc func, XtPointer data)
{
	LocalInfo	*l = head;

	for (; l != NULL && l->form != w; l = l->next);

	if (l == NULL)
		return;

	l->func = (cbFunc_t)func;
	l->data = data;
}

Pixel ColorPickerGetPixel(Widget w)
{
	LocalInfo	*l = head;

	for (; l != NULL && l->form != w; l = l->next);

	if (l == NULL)
		return None;
	return l->pixel;
}

XColor *ColorPickerGetXColor(Widget w)
{
	static XColor	xcol;
	LocalInfo	*l = head;

	for (; l != NULL && l->form != w; l = l->next);

	if (l == NULL)
		return NULL;
	xcol.pixel = l->pixel;
	xcol.red   = l->r * 0xffff;
	xcol.green = l->g * 0xffff;
	xcol.blue  = l->b * 0xffff;

	return &xcol;
}
