/* 
 * ColorSB.c
 *
 * (c) Copyright 1993-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Author:  Adobe Systems Incorporated
 */
/* $XFree86$ */

#ifndef X_NOT_POSIX
#include <unistd.h>
#endif

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/ShellP.h>
#include <stdlib.h>
#include <Xm/Xm.h>

/* There are no words to describe how I feel about having to do this */

#if XmVersion > 1001		
#include <Xm/ManagerP.h>
#else
#include <Xm/XmP.h>
#endif

#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/SeparatoG.h>
#include <Xm/DrawingA.h>
#include <Xm/Scale.h>
#include <Xm/RowColumn.h>
#include <Xm/Frame.h>
#include <Xm/MessageB.h>

#include <DPS/dpsXclient.h>
#include "dpsXcommonI.h"
#include <DPS/dpsXshare.h>
#include "eyedrop16.xbm"
#include "eyedropmask16.xbm"
#include "eyedrop32.xbm"
#include "eyedropmask32.xbm"
#include "heyedrop.xbm"
#include "square.xbm"
#include "squaremask.xbm"
#include "CSBwraps.h"
#include <math.h>
#include <stdio.h>
#include <pwd.h>
#include <DPS/ColorSBP.h>

#define PATH_BUF_SIZE 1024

/* Turn a string into a compound string */
#define CS(str, w) CreateSharedCS(str, w)

#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define TO_PCT(val) ((int) (val * 100.0 + 0.5))
#define TO_X(color) ((color) * 65535)

#define Offset(field) XtOffsetOf(ColorSelectionBoxRec, csb.field)

static XtResource resources[] = {
    {XtNcontext, XtCContext, XtRDPSContext, sizeof(DPSContext),
	Offset(context), XtRDPSContext, (XtPointer) NULL},
    {XtNrgbLabels, XtCRgbLabels, XtRString, sizeof(String),
	Offset(rgb_labels), XtRString, (XtPointer) "R:G:B"},
    {XtNcmykLabels, XtCCmykLabels, XtRString, sizeof(String),
	Offset(cmyk_labels), XtRString, (XtPointer) "C:M:Y:K"},
    {XtNhsbLabels, XtCHsbLabels, XtRString, sizeof(String),
	Offset(hsb_labels), XtRString, (XtPointer) "H:S:B"},
    {XtNgrayLabels, XtCGrayLabels, XtRString, sizeof(String),
	Offset(gray_labels), XtRString, (XtPointer) "Gray"},
    {XtNcellSize, XtCCellSize, XtRDimension, sizeof(Dimension),
	Offset(cell_size), XtRImmediate, (XtPointer) 15},
    {XtNnumCells, XtCNumCells, XtRShort, sizeof(short),
	Offset(num_cells), XtRImmediate, (XtPointer) 30},
    {XtNfillMe, XtCFillMe, XtRString, sizeof(String),
	Offset(fill_me), XtRString, (XtPointer) "Fill me with colors"},
    {XtNcurrentSpace, XtCCurrentSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(current_space), XtRImmediate, (XtPointer) CSBSpaceHSB},
    {XtNcurrentRendering, XtCCurrentRendering, XtRRenderingType,
	sizeof(CSBRenderingType), Offset(current_rendering),
	XtRImmediate, (XtPointer) CSBDisplayDPS},
    {XtNcurrentPalette, XtCCurrentPalette, XtRShort, sizeof(short),
	Offset(current_palette), XtRImmediate, (XtPointer) 0},
    {XtNbrokenPaletteLabel, XtCBrokenPaletteLabel, XtRString,
	sizeof(String), Offset(broken_palette_label),
	XtRString, (XtPointer) "(broken)"},
    {XtNbrokenPaletteMessage, XtCBrokenPaletteMessage, XtRString,
	sizeof(String), Offset(broken_palette_message),
	XtRString, (XtPointer) "The current palette contains an error"},

    {XtNpalette0Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[0]), XtRString, (XtPointer) NULL},
    {XtNpalette0Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[0]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette0ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[0]), XtRImmediate, (XtPointer) False},
    {XtNpalette0Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[0]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette1Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[1]), XtRString, (XtPointer) NULL},
    {XtNpalette1Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[1]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette1ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[1]), XtRImmediate, (XtPointer) False},
    {XtNpalette1Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[1]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette2Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[2]), XtRString, (XtPointer) NULL},
    {XtNpalette2Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[2]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette2ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[2]), XtRImmediate, (XtPointer) False},
    {XtNpalette2Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[2]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette3Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[3]), XtRString, (XtPointer) NULL},
    {XtNpalette3Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[3]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette3ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[3]), XtRImmediate, (XtPointer) False},
    {XtNpalette3Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[3]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette4Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[4]), XtRString, (XtPointer) NULL},
    {XtNpalette4Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[4]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette4ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[4]), XtRImmediate, (XtPointer) False},
    {XtNpalette4Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[4]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette5Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[5]), XtRString, (XtPointer) NULL},
    {XtNpalette5Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[5]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette5ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[5]), XtRImmediate, (XtPointer) False},
    {XtNpalette5Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[5]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette6Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[6]), XtRString, (XtPointer) NULL},
    {XtNpalette6Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[6]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette6ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[6]), XtRImmediate, (XtPointer) False},
    {XtNpalette6Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[6]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette7Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[7]), XtRString, (XtPointer) NULL},
    {XtNpalette7Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[7]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette7ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[7]), XtRImmediate, (XtPointer) False},
    {XtNpalette7Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[7]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette8Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[8]), XtRString, (XtPointer) NULL},
    {XtNpalette8Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[8]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette8ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[8]), XtRImmediate, (XtPointer) False},
    {XtNpalette8Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[8]), XtRImmediate, (XtPointer) NULL},

    {XtNpalette9Label, XtCPaletteLabel, XtRString, sizeof(String),
	Offset(palette_label[9]), XtRString, (XtPointer) NULL},
    {XtNpalette9Space, XtCPaletteSpace, XtRColorSpace, sizeof(CSBColorSpace),
	Offset(palette_space[9]), XtRImmediate, (XtPointer) CSBSpaceRGB},
    {XtNpalette9ColorDependent, XtCPaletteColorDependent,
	XtRBoolean, sizeof(Boolean),
	Offset(palette_color_dependent[9]), XtRImmediate, (XtPointer) False},
    {XtNpalette9Function, XtCPaletteFunction, XtRString, sizeof(String),
	Offset(palette_function[9]), XtRImmediate, (XtPointer) NULL},

    {XtNokCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	Offset(ok_callback), XtRCallback, (XtPointer) NULL},
    {XtNapplyCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	Offset(apply_callback), XtRCallback, (XtPointer) NULL},
    {XtNresetCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	Offset(reset_callback), XtRCallback, (XtPointer) NULL},
    {XtNcancelCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	Offset(cancel_callback), XtRCallback, (XtPointer) NULL},
    {XtNvalueChangedCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
	Offset(value_changed_callback), XtRCallback, (XtPointer) NULL}
};

static Boolean SetColor         (Widget w, CSBColorSpace space, double c1, double c2, double c3, double c4, Bool setSpace);
static Boolean SetValues        (Widget old, Widget req, Widget new, ArgList args, Cardinal *num_args);
static XtGeometryResult GeometryManager (Widget w, XtWidgetGeometry *desired, XtWidgetGeometry *allowed);
static void ChangeLabel         (Widget label, double n);
static void ChangeManaged       (Widget w);
static void ClassInitialize     (void);
static void ClassPartInitialize (WidgetClass widget_class);
static void CreateChildren      (ColorSelectionBoxWidget csb);
static void Destroy             (Widget widget);
static void DrawDock            (ColorSelectionBoxWidget csb);
static void DrawPalette         (ColorSelectionBoxWidget csb);
static void FillPatch           (ColorSelectionBoxWidget csb);
static void GetColor            (Widget w, CSBColorSpace space, float *c1, float *c2, float *c3, float *c4);
static void Initialize          (Widget request, Widget new, ArgList args, Cardinal *num_args);
static void InitializeDock      (ColorSelectionBoxWidget csb);
static void Realize             (Widget w, XtValueMask *mask, XSetWindowAttributes *attr);
static void Resize              (Widget widget);
static void SaveDockContents    (ColorSelectionBoxWidget csb);
static void SetBackground       (ColorSelectionBoxWidget csb);
static void SetCMYKValues       (ColorSelectionBoxWidget csb);
static void SetColorSpace       (ColorSelectionBoxWidget csb);
static void SetGrayValues       (ColorSelectionBoxWidget csb);
static void SetHSBValues        (ColorSelectionBoxWidget csb);
static void SetRGBValues        (ColorSelectionBoxWidget csb);
static void SetRendering        (ColorSelectionBoxWidget csb);
static void SetSliders          (ColorSelectionBoxWidget csb);
static void UpdateColorSpaces   (ColorSelectionBoxWidget csb, CSBColorSpace masterSpace);

static void DockPress           (Widget w, XtPointer data, XEvent *event, Boolean *goOn);
static void EyedropPointer      (Widget w, XtPointer data, XEvent *event, Boolean *goOn);
static void FormResize          (Widget w, XtPointer data, XEvent *event, Boolean *goOn);
static void PalettePress        (Widget w, XtPointer data, XEvent *event, Boolean *goOn);
static void PatchPress          (Widget w, XtPointer data, XEvent *event, Boolean *goOn);
static void PatchRelease        (Widget w, XtPointer data, XEvent *event, Boolean *goOn);

static void ApplyCallback       (Widget w, XtPointer clientData, XtPointer callData);
static void DoEyedropCallback   (Widget w, XtPointer clientData, XtPointer callData);
static void DrawDockCallback    (Widget w, XtPointer clientData, XtPointer callData);
static void DrawPaletteCallback (Widget w, XtPointer clientData, XtPointer callData);
static void FillPatchCallback   (Widget w, XtPointer clientData, XtPointer callData);
static void OKCallback          (Widget w, XtPointer clientData, XtPointer callData);
static void SetCMYKCallback     (Widget w, XtPointer clientData, XtPointer callData);
static void SetGrayCallback     (Widget w, XtPointer clientData, XtPointer callData);
static void SetHSBCallback      (Widget w, XtPointer clientData, XtPointer callData);
static void SetRGBCallback      (Widget w, XtPointer clientData, XtPointer callData);
static void Slider1Callback     (Widget w, XtPointer clientData, XtPointer callData);
static void Slider2Callback     (Widget w, XtPointer clientData, XtPointer callData);
static void Slider3Callback     (Widget w, XtPointer clientData, XtPointer callData);
static void Slider4Callback     (Widget w, XtPointer clientData, XtPointer callData);

ColorSelectionBoxClassRec colorSelectionBoxClassRec = {
    /* Core class part */
  {
    /* superclass	     */	(WidgetClass) &xmManagerClassRec,
    /* class_name	     */ "ColorSelectionBox",
    /* widget_size	     */ sizeof(ColorSelectionBoxRec),
    /* class_initialize      */ ClassInitialize,
    /* class_part_initialize */ ClassPartInitialize,
    /* class_inited          */	False,
    /* initialize	     */	Initialize,
    /* initialize_hook       */	NULL,
    /* realize		     */	Realize,
    /* actions		     */	NULL,
    /* num_actions	     */	0,
    /* resources	     */	resources,
    /* num_resources	     */	XtNumber(resources),
    /* xrm_class	     */	NULLQUARK,
    /* compress_motion	     */	True,
    /* compress_exposure     */	XtExposeCompressMultiple,
    /* compress_enterleave   */	True,
    /* visible_interest	     */	False,
    /* destroy		     */	Destroy,
    /* resize		     */	Resize,
    /* expose		     */	NULL,
    /* set_values	     */	SetValues,
    /* set_values_hook       */	NULL,			
    /* set_values_almost     */	XtInheritSetValuesAlmost,  
    /* get_values_hook       */	NULL,			
    /* accept_focus	     */	NULL,
    /* version		     */	XtVersion,
    /* callback offsets      */	NULL,
    /* tm_table              */	NULL,
    /* query_geometry	     */	XtInheritQueryGeometry,
    /* display_accelerator   */	NULL,
    /* extension	     */	NULL,
  },
   /* Composite class part */
  {
    /* geometry_manager	     */	GeometryManager,
    /* change_managed	     */	ChangeManaged,
    /* insert_child	     */	XtInheritInsertChild,
    /* delete_child	     */	XtInheritDeleteChild,
    /* extension	     */	NULL,
  },
   /* Constraint class part */
  {
    /* resources	     */ NULL,
    /* num_resources	     */ 0,
    /* constraint_size	     */ 0,
    /* initialize	     */ NULL,
    /* destroy		     */ NULL,
    /* set_values	     */ NULL,
    /* extension	     */ NULL,
  },
   /* Manager class part */
  {
    /* translations	     */ XtInheritTranslations,
    /* syn_resources	     */ NULL,
    /* num_syn_resources     */ 0,
    /* syn_constraint_resources */ NULL,
    /* num_syn_constraint_resources */ 0,
    /* parent_process	     */ XmInheritParentProcess,
    /* extension	     */ NULL,
  },
   /* ColorSelectionBox class part */
  {
    /* set_color	     */ SetColor,
    /* get_color	     */ GetColor,
    /* extension	     */	NULL,
  }
};

WidgetClass colorSelectionBoxWidgetClass =
	(WidgetClass) &colorSelectionBoxClassRec;

static XmString CreateSharedCS(String str, Widget w)
{
    XrmValue src, dst;
    XmString result;

    src.addr = str;
    src.size = strlen(str);

    dst.addr = (caddr_t) &result;
    dst.size = sizeof(result);

    if (XtConvertAndStore(w, XtRString, &src, XmRXmString, &dst)) {
	return result;
    } else return NULL;
}
 
static Boolean LowerCase(String from, String to, int size)
{
    register char ch;
    register int i;

    for (i = 0; i < size; i++) {
	ch = from[i];
	if (ch >= 'A' && ch <= 'Z') to[i] = ch - 'A' + 'a';
	else to[i] = ch;
	if (ch == '\0') return False;
    }
    return TRUE;
}

/* ARGSUSED */

static Boolean CvtStringToColorSpace(
    Display *dpy,
    XrmValuePtr args,
    Cardinal *num_args,
    XrmValuePtr from,
    XrmValuePtr to,
    XtPointer *data)
{
#define LOWER_SIZE 5
    char lower[LOWER_SIZE];	/* Lower cased string value */
    Boolean badConvert;
    static CSBColorSpace c;

    if (*num_args != 0) {	/* Check for correct number */
	XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
		"cvtStringToColorSpace", "wrongParameters",
		"XtToolkitError",
		"String to colorspace conversion needs no extra arguments",
		(String *) NULL, (Cardinal *) NULL);
    }

    /* Lower case the value */
    badConvert = LowerCase(from->addr, lower, LOWER_SIZE);

    /* Try to convert if a short enough string specified */
    if (!badConvert) {
	if (strcmp(lower, "rgb") == 0) c = CSBSpaceRGB;
	else if (strcmp(lower, "cmyk") == 0) c = CSBSpaceCMYK;
	else if (strcmp(lower, "hsb") == 0) c = CSBSpaceHSB;
	else if (strcmp(lower, "gray") == 0) c = CSBSpaceGray;
	else if (strcmp(lower, "grey") == 0) c = CSBSpaceGray;
	else badConvert = True;
    }

    /* String too long or unknown value -- issue warning */
    if (badConvert) {
	XtDisplayStringConversionWarning(dpy, from->addr, "ColorSpace");
    } else {
	if (to->addr == NULL) to->addr = (caddr_t) &c;

	else if (to->size < sizeof(CSBColorSpace)) badConvert = TRUE;
	else *(CSBColorSpace *) to->addr = c;

	to->size = sizeof(CSBColorSpace);
    }
    return !badConvert;
#undef LOWER_SIZE
}

/* ARGSUSED */

static Boolean CvtStringToRenderingType(
    Display *dpy,
    XrmValuePtr args,
    Cardinal *num_args,
    XrmValuePtr from,
    XrmValuePtr to,
    XtPointer *data)
{
#define LOWER_SIZE 5
    char lower[LOWER_SIZE];	/* Lower cased string value */
    Boolean badConvert;
    static CSBRenderingType c;

    if (*num_args != 0) {	/* Check for correct number */
	XtAppErrorMsg(XtDisplayToApplicationContext(dpy),
		"cvtStringToRenderingType", "wrongParameters",
		"XtToolkitError",
		"String to rendering type conversion needs no extra arguments",
		(String *) NULL, (Cardinal *) NULL);
    }

    /* Lower case the value */
    badConvert = LowerCase(from->addr, lower, LOWER_SIZE);

    /* Try to convert if a short enough string specified */
    if (!badConvert) {
	if (strcmp(lower, "x") == 0) c = CSBDisplayX;
	else if (strcmp(lower, "dps") == 0) c = CSBDisplayDPS;
	else if (strcmp(lower, "both") == 0) c = CSBDisplayBoth;
	else badConvert = True;
    }

    /* String too long or unknown value -- issue warning */
    if (badConvert) {
	XtDisplayStringConversionWarning(dpy, from->addr, "RenderingType");
    } else {
	if (to->addr == NULL) to->addr = (caddr_t) &c;

	else if (to->size < sizeof(CSBRenderingType)) badConvert = TRUE;
	else *(CSBRenderingType *) to->addr = c;

	to->size = sizeof(CSBRenderingType);
    }
    return !badConvert;
#undef LOWER_SIZE
}

static void ClassInitialize(void)
{
    /* Register converters */

    XtSetTypeConverter(XtRString, XtRColorSpace,
	    CvtStringToColorSpace, (XtConvertArgList) NULL, 0,
	    XtCacheAll, (XtDestructor) NULL);
    XtSetTypeConverter(XtRString, XtRRenderingType,
	    CvtStringToRenderingType, (XtConvertArgList) NULL, 0,
	    XtCacheAll, (XtDestructor) NULL);
}

/* ARGSUSED */

static void ClassPartInitialize(WidgetClass widget_class)
{
    register ColorSelectionBoxWidgetClass wc =
	    (ColorSelectionBoxWidgetClass) widget_class;
    ColorSelectionBoxWidgetClass super =
	    (ColorSelectionBoxWidgetClass) wc->core_class.superclass;

    if (wc->csb_class.set_color == InheritSetColor) {
	wc->csb_class.set_color = super->csb_class.set_color;
    }
    if (wc->csb_class.get_color == InheritGetColor) {
	wc->csb_class.get_color = super->csb_class.get_color;
    }
}

static void ToUserSpace(
    ColorSelectionBoxWidget csb,
    int xWidth, int xHeight,
    float *uWidth, float *uHeight)
{
    register float *i = csb->csb.itransform;

    *uWidth = i[0] * xWidth - i[2] * xHeight + i[4];
    *uHeight= i[1] * xWidth - i[3] * xHeight + i[5];
}

static void ColorizeRGB(ColorSelectionBoxWidget csb)
{
    Dimension height, width;
    int depth, steps;
    float w, h;

    XtVaGetValues(csb->csb.slider_child[0], XtNwidth, &width,
		  XtNheight, &height,
		  XtNdepth, &depth, NULL);

    if (csb->csb.red_pixmap != None && width != csb->csb.rgb_slider_width) {
	XFreePixmap(XtDisplay(csb), csb->csb.red_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.green_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.blue_pixmap);
	csb->csb.red_pixmap = None;
    }

    if (csb->csb.red_pixmap == None) {
	csb->csb.rgb_slider_width = width;
	if (csb->csb.visual_class == TrueColor) steps = width / 2;
	else steps = width / 4;
	
	ToUserSpace(csb, width, height, &w, &h);

	csb->csb.red_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					    width, height, depth);
    
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context, csb->csb.red_pixmap, height);

	_DPSCRGBBlend(csb->csb.context, 0.0, 0.0, w, h, "0 0", steps);

	csb->csb.green_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					      width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context,
			       csb->csb.green_pixmap, height);

	_DPSCRGBBlend(csb->csb.context, 0.0, 0.0, w, h, "0 exch 0", steps);

	csb->csb.blue_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					     width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.blue_pixmap, height);

	_DPSCRGBBlend(csb->csb.context,
		      0.0, 0.0, w, h, "0 0 3 -1 roll", steps);

	DPSWaitContext(csb->csb.context);
    }

    XtVaSetValues(csb->csb.slider_child[0],
		  XtNbackgroundPixmap, csb->csb.red_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[1],
		  XtNbackgroundPixmap, csb->csb.green_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[2],
		  XtNbackgroundPixmap, csb->csb.blue_pixmap, NULL);
}

static void ColorizeCMYK(ColorSelectionBoxWidget csb)
{
    Dimension height, width;
    int depth, steps;
    float w, h;

    XtVaGetValues(csb->csb.slider_child[0], XtNwidth, &width,
		  XtNheight, &height,
		  XtNdepth, &depth, NULL);

    if (csb->csb.cyan_pixmap != None && width != csb->csb.cmyk_slider_width) {
	XFreePixmap(XtDisplay(csb), csb->csb.cyan_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.magenta_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.yellow_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.black_pixmap);
	csb->csb.cyan_pixmap = None;
    }

    if (csb->csb.cyan_pixmap == None) {
	csb->csb.cmyk_slider_width = width;
	if (csb->csb.visual_class == TrueColor) steps = width / 2;
	else steps = width / 4;
	
	ToUserSpace(csb, width, height, &w, &h);

	csb->csb.cyan_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					     width, height, depth);
    
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context, csb->csb.cyan_pixmap, height);

	_DPSCCMYKBlend(csb->csb.context, 0.0, 0.0, w, h, "0 0 0", steps);

	csb->csb.magenta_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
						width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.magenta_pixmap,
			       height);

	_DPSCCMYKBlend(csb->csb.context, 0.0, 0.0, w, h, "0 exch 0 0", steps);

	csb->csb.yellow_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					       width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.yellow_pixmap,
			       height);

	_DPSCCMYKBlend(csb->csb.context, 0.0, 0.0, w, h, "0 0 3 -1 roll 0",
		       steps);

	csb->csb.black_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					      width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.black_pixmap,
			       height);

	_DPSCCMYKBlend(csb->csb.context, 0.0, 0.0, w, h, "0 0 0 4 -1 roll",
		       steps);

	DPSWaitContext(csb->csb.context);
    }

    XtVaSetValues(csb->csb.slider_child[0], XtNbackgroundPixmap,
		  csb->csb.cyan_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[1], XtNbackgroundPixmap,
		  csb->csb.magenta_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[2], XtNbackgroundPixmap,
		  csb->csb.yellow_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[3], XtNbackgroundPixmap,
		  csb->csb.black_pixmap, NULL);
}

static void ColorizeHSB(ColorSelectionBoxWidget csb)
{
    Dimension height, width;
    int depth, steps;
    float w, h;

    XtVaGetValues(csb->csb.slider_child[0], XtNwidth, &width,
		  XtNheight, &height,
		  XtNdepth, &depth, NULL);

    if (csb->csb.hue_pixmap != None && width != csb->csb.hsb_slider_width) {
	XFreePixmap(XtDisplay(csb), csb->csb.hue_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.sat_pixmap);
	XFreePixmap(XtDisplay(csb), csb->csb.bright_pixmap);
	csb->csb.hue_pixmap = None;
    }

    if (csb->csb.hue_pixmap == None) {
	csb->csb.hsb_slider_width = width;
	if (csb->csb.visual_class == TrueColor) steps = width / 2;
	else steps = width / 4;
	
	ToUserSpace(csb, width, height, &w, &h);

	csb->csb.hue_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					    width, height, depth);
    
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context, csb->csb.hue_pixmap, height);

	_DPSCHSBBlend(csb->csb.context, 0.0, 0.0, w, h, "1 1", steps);

	csb->csb.sat_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					    width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.sat_pixmap, height);

	_DPSCHSBBlend(csb->csb.context, 0.0, 0.0, w, h, "0 exch 1", steps);

	csb->csb.bright_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					       width, height, depth);
    
	XDPSSetContextDrawable(csb->csb.context, csb->csb.bright_pixmap,
			       height);

	_DPSCHSBBlend(csb->csb.context, 0.0, 0.0, w, h, "0 1 3 -1 roll",
		      steps);

	DPSWaitContext(csb->csb.context);
    }

    XtVaSetValues(csb->csb.slider_child[0], XtNbackgroundPixmap,
		  csb->csb.hue_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[1], XtNbackgroundPixmap,
		  csb->csb.sat_pixmap, NULL);
    XtVaSetValues(csb->csb.slider_child[2], XtNbackgroundPixmap,
		  csb->csb.bright_pixmap, NULL);
}

static void ColorizeGray(ColorSelectionBoxWidget csb)
{
    Dimension height, width;
    int depth, steps;
    float w, h;

    XtVaGetValues(csb->csb.slider_child[0], XtNwidth, &width,
		  XtNheight, &height,
		  XtNdepth, &depth, NULL);

    if (csb->csb.gray_pixmap != None && width != csb->csb.gray_slider_width) {
	XFreePixmap(XtDisplay(csb), csb->csb.gray_pixmap);
	csb->csb.gray_pixmap = None;
    }

    if (csb->csb.gray_pixmap == None) {
	csb->csb.gray_slider_width = width;
	if (csb->csb.visual_class == TrueColor) steps = width / 2;
	else steps = width / 4;
	
	ToUserSpace(csb, width, height, &w, &h);

	csb->csb.gray_pixmap = XCreatePixmap(XtDisplay(csb), XtWindow(csb),
					     width, height, depth);
    
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context, csb->csb.gray_pixmap, height);

	_DPSCGrayBlend(csb->csb.context, 0.0, 0.0, w, h, " ", steps);

	DPSWaitContext(csb->csb.context);
    }

    XtVaSetValues(csb->csb.slider_child[0], XtNbackgroundPixmap,
		  csb->csb.gray_pixmap, NULL);
}

static void ColorizeSliders(ColorSelectionBoxWidget csb)
{
    if (!XtIsRealized(csb)) return;

    switch (csb->csb.current_space) {
	case CSBSpaceRGB:
	    ColorizeRGB(csb);
	    break;
	case CSBSpaceCMYK:
	    ColorizeCMYK(csb);
	    break;
	case CSBSpaceHSB:
	    ColorizeHSB(csb);
	    break;
	case CSBSpaceGray:
	    ColorizeGray(csb);
	    break;
    }
}

/* ARGSUSED */

static void FormResize(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    
    if (event->type != ConfigureNotify && event->type != MapNotify) return;

    csb->csb.rgb_slider_width = csb->csb.cmyk_slider_width =
	    csb->csb.hsb_slider_width = csb->csb.gray_slider_width = 0;
    csb->csb.palette_pixmap_valid = False;
    if (csb->csb.patch_gstate != 0) {
	XDPSFreeContextGState(csb->csb.context, csb->csb.patch_gstate);
	csb->csb.patch_gstate = 0;
    }
    if (csb->csb.dock_gstate != 0) {
	XDPSFreeContextGState(csb->csb.context, csb->csb.dock_gstate);
	csb->csb.dock_gstate = 0;
    }
    ColorizeSliders(csb);
    DrawPalette(csb);
    if (XtIsRealized(csb->csb.patch_child)) {
	XClearArea(XtDisplay(csb), XtWindow(csb->csb.patch_child),
		   0, 0, 1000, 1000, True);
    }
}

static void FillCallbackRec(
    ColorSelectionBoxWidget csb,
    CSBCallbackRec *rec)
{
    rec->current_space = csb->csb.current_space;
    rec->red = csb->csb.current_color.red;
    rec->green = csb->csb.current_color.green;
    rec->blue = csb->csb.current_color.blue;
    rec->cyan = csb->csb.current_color.cyan;
    rec->magenta = csb->csb.current_color.magenta;
    rec->yellow = csb->csb.current_color.yellow;
    rec->black = csb->csb.current_color.black;
    rec->hue = csb->csb.current_color.hue;
    rec->saturation = csb->csb.current_color.saturation;
    rec->brightness = csb->csb.current_color.brightness;
    rec->gray = csb->csb.current_color.gray;
}

/* ARGSUSED */

static void OKCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    CSBCallbackRec rec;

    csb->csb.save_color = csb->csb.current_color;
    FillCallbackRec(csb, &rec);
    rec.reason = CSBOK;
    XtCallCallbackList((Widget) csb, csb->csb.ok_callback, (XtPointer) &rec);
    if (XtIsShell(XtParent(csb))) XtPopdown(XtParent(csb));

    SaveDockContents(csb);
}

/* ARGSUSED */

static void ApplyCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    CSBCallbackRec rec;

    csb->csb.save_color = csb->csb.current_color;
    FillCallbackRec(csb, &rec);
    rec.reason = CSBApply;
    XtCallCallbackList((Widget) csb, csb->csb.apply_callback,
		       (XtPointer) &rec);

    SaveDockContents(csb);
}

/* ARGSUSED */

static void ResetCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    CSBCallbackRec rec;

    csb->csb.current_color = csb->csb.save_color;
    FillPatch(csb);
    SetSliders(csb);
    FillCallbackRec(csb, &rec);
    rec.reason = CSBReset;
    XtCallCallbackList((Widget) csb, csb->csb.reset_callback,
		       (XtPointer) &rec);
}

/* ARGSUSED */

static void CancelCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    CSBCallbackRec rec;

    csb->csb.current_color = csb->csb.save_color;
    FillPatch(csb);
    SetSliders(csb);
    FillCallbackRec(csb, &rec);
    rec.reason = CSBCancel;
    XtCallCallbackList((Widget) csb, csb->csb.cancel_callback,
		       (XtPointer) &rec);
    if (XtIsShell(XtParent(csb))) XtPopdown(XtParent(csb));
}

/* ARGSUSED */

static void DoValueChangedCallback(ColorSelectionBoxWidget csb)
{
    CSBCallbackRec rec;

    FillCallbackRec(csb, &rec);
    rec.reason = CSBValueChanged;
    XtCallCallbackList((Widget) csb, csb->csb.value_changed_callback,
		       (XtPointer) &rec);
}

/* ARGSUSED */

static void ChangeLabelCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    XmScaleCallbackStruct *scaleData = (XmScaleCallbackStruct *) callData;

    ChangeLabel((Widget) clientData, ((float) scaleData->value) / 100.0);
}

static void ChangeLabel(Widget label, double n)
{
    char buf[10];

    sprintf(buf, "%d", TO_PCT(n));
    XtVaSetValues(label, XmNlabelString, CS(buf, label), NULL);
}

static void CreateModelMenu(Widget parent, Widget csb)
{
    Widget kids[4];

    kids[0] = XmCreatePushButtonGadget(parent, "rgb", (ArgList) NULL, 0);
    XtAddCallback(kids[0], XmNactivateCallback,
		  SetRGBCallback, (XtPointer) csb);
    kids[1] = XmCreatePushButtonGadget(parent, "cmyk", (ArgList) NULL, 0);
    XtAddCallback(kids[1], XmNactivateCallback,
		  SetCMYKCallback, (XtPointer) csb);
    kids[2] = XmCreatePushButtonGadget(parent, "hsb", (ArgList) NULL, 0);
    XtAddCallback(kids[2], XmNactivateCallback,
		  SetHSBCallback, (XtPointer) csb);
    kids[3] = XmCreatePushButtonGadget(parent, "gray", (ArgList) NULL, 0);
    XtAddCallback(kids[3], XmNactivateCallback,
		  SetGrayCallback, (XtPointer) csb);

    XtManageChildren(kids, 4);
}

typedef struct {
    ColorSelectionBoxWidget csb;
    CSBRenderingType rendering;
} RenderingRec;

/* ARGSUSED */

static void SetRenderingCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    RenderingRec *r = (RenderingRec *) clientData;

    r->csb->csb.current_rendering = r->rendering;
    FillPatch(r->csb);
}

static void CreateDisplayMenu(Widget parent, ColorSelectionBoxWidget csb)
{
    Widget kids[3];
    RenderingRec *r;

    r = XtNew(RenderingRec);
    r->csb = csb;
    r->rendering = CSBDisplayDPS;
    kids[0] = XmCreatePushButtonGadget(parent, "displayDPS",
				       (ArgList) NULL, 0);
    XtAddCallback(kids[0], XmNactivateCallback,
		  SetRenderingCallback, (XtPointer) r);
    r = XtNew(RenderingRec);
    r->csb = csb;
    r->rendering = CSBDisplayX;
    kids[1] = XmCreatePushButtonGadget(parent, "displayX", (ArgList) NULL, 0);
    XtAddCallback(kids[1], XmNactivateCallback,
		  SetRenderingCallback, (XtPointer) r);
    r = XtNew(RenderingRec);
    r->csb = csb;
    r->rendering = CSBDisplayBoth;
    kids[2] = XmCreatePushButtonGadget(parent, "displayBoth",
				       (ArgList) NULL, 0);
    XtAddCallback(kids[2], XmNactivateCallback,
		  SetRenderingCallback, (XtPointer) r);

    XtManageChildren(kids, 3);
}

typedef struct {
    ColorSelectionBoxWidget csb;
    int n;
} PaletteRec;

/* ARGSUSED */

static void SetPaletteCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    PaletteRec *p = (PaletteRec *) clientData;

    if (p->csb->csb.palette_broken[p->n]) return;

    if (p->n != p->csb->csb.current_palette ||
	p->csb->csb.palette_color_dependent[p->n]) {
	p->csb->csb.palette_pixmap_valid = False;
    }

    p->csb->csb.current_palette = p->n;
    DrawPalette(p->csb);
}

static void CreatePaletteMenu(Widget parent, ColorSelectionBoxWidget csb)
{
    Widget w, managed[PALETTE_MAX];
    int j, k;
    char buf[10];
    PaletteRec *p;

    j = 0;

    for (k = 0; k < PALETTE_MAX; k++) {
	p = XtNew(PaletteRec);
	p->csb = csb;
	p->n = k;
	sprintf(buf, "palette%d", k);
	w = XtVaCreateWidget(buf, xmPushButtonGadgetClass, parent, NULL);
	if (csb->csb.palette_label[k] != NULL) {
	    XtVaSetValues(w, XtVaTypedArg, XmNlabelString,
			     XtRString, csb->csb.palette_label[k],
			     strlen(csb->csb.palette_label[k])+1,
			     NULL);
	}
	XtAddCallback(w, XmNactivateCallback,
		      SetPaletteCallback, (XtPointer) p);
	if (csb->csb.palette_function[k] != NULL) managed[j++] = w;
    }

    if (j != 0) XtManageChildren(managed, j);
}

static void CreateChildren(ColorSelectionBoxWidget csb)
{
    int i;
    Arg args[20];
    Widget form, menu, button, w, dock_frame, palette_frame;
    Pixel fg, bg;
    int depth;
    Pixmap eyedrop;

    i = 0;
    XtSetArg(args[i], XmNresizePolicy, XmRESIZE_NONE);			i++;
    form = XtCreateManagedWidget("panel", xmFormWidgetClass,
				 (Widget) csb, args, i);
    csb->csb.form_child = form;
    XtAddEventHandler(form, StructureNotifyMask, False, FormResize,
		      (XtPointer) csb);

    i = 0;
    menu = XmCreatePulldownMenu(form, "modelMenu", args, i);
    CreateModelMenu(menu, (Widget) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_FORM);			i++;
    XtSetArg(args[i], XmNsubMenuId, menu);				i++;
    csb->csb.model_option_menu_child =
	    XmCreateOptionMenu(form, "modelOptionMenu",
			       args, i);
    XtManageChild(csb->csb.model_option_menu_child);

    XtVaGetValues(form, XtNbackground, &bg, XmNforeground, &fg,
		  XtNdepth, &depth, NULL);
    eyedrop = XCreatePixmapFromBitmapData(XtDisplay(csb),
					  RootWindowOfScreen(XtScreen(csb)),
					  (char *) heyedrop_bits,
					  heyedrop_width, heyedrop_height,
					  fg, bg, depth);
				    
    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.model_option_menu_child);	i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.model_option_menu_child);	i++;
    XtSetArg(args[i], XmNlabelPixmap, eyedrop);				i++;
    button = XtCreateManagedWidget("eyedropButton", xmPushButtonWidgetClass,
				   form, args, i);
    XtAddCallback(button, XmNactivateCallback,
		  DoEyedropCallback, (XtPointer) csb);
    XtInsertRawEventHandler(button, PointerMotionMask | ButtonReleaseMask,
			    False, EyedropPointer, (XtPointer) csb,
			    XtListHead);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.model_option_menu_child);	i++;
    csb->csb.label_child[0] =
	    XtCreateManagedWidget("label1", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.model_option_menu_child);	i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_POSITION);		i++;
    csb->csb.value_child[0] =
	    XtCreateManagedWidget("value1", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.label_child[0]);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.model_option_menu_child);	i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.value_child[0]);		i++;
    csb->csb.slider_child[0] =
	    XtCreateManagedWidget("slider1", xmScaleWidgetClass,
				  form, args, i);
    XtAddCallback(csb->csb.slider_child[0], XmNvalueChangedCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[0]);
    XtAddCallback(csb->csb.slider_child[0], XmNdragCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[0]);
    XtAddCallback(csb->csb.slider_child[0], XmNvalueChangedCallback,
		  Slider1Callback, (XtPointer) csb);
    XtAddCallback(csb->csb.slider_child[0], XmNdragCallback,
		  Slider1Callback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[0]);		i++;
    csb->csb.label_child[1] =
	    XtCreateManagedWidget("label2", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[0]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.value_child[0]);		i++;
    csb->csb.value_child[1] =
	    XtCreateManagedWidget("value2", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.slider_child[0]);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[0]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.slider_child[0]);	i++;
    csb->csb.slider_child[1] =
	    XtCreateManagedWidget("slider2", xmScaleWidgetClass,
				  form, args, i);
    XtAddCallback(csb->csb.slider_child[1], XmNvalueChangedCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[1]);
    XtAddCallback(csb->csb.slider_child[1], XmNdragCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[1]);
    XtAddCallback(csb->csb.slider_child[1], XmNvalueChangedCallback,
		  Slider2Callback, (XtPointer) csb);
    XtAddCallback(csb->csb.slider_child[1], XmNdragCallback,
		  Slider2Callback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[1]);		i++;
    csb->csb.label_child[2] =
	    XtCreateManagedWidget("label3", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[1]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.value_child[0]);		i++;
    csb->csb.value_child[2] =
	    XtCreateManagedWidget("value3", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.slider_child[0]);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[1]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.slider_child[0]);	i++;
    csb->csb.slider_child[2] =
	    XtCreateManagedWidget("slider3", xmScaleWidgetClass,
				  form, args, i);
    XtAddCallback(csb->csb.slider_child[2], XmNvalueChangedCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[2]);
    XtAddCallback(csb->csb.slider_child[2], XmNdragCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[2]);
    XtAddCallback(csb->csb.slider_child[2], XmNvalueChangedCallback,
		  Slider3Callback, (XtPointer) csb);
    XtAddCallback(csb->csb.slider_child[2], XmNdragCallback,
		  Slider3Callback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[2]);		i++;
    csb->csb.label_child[3] =
	    XtCreateManagedWidget("label4", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[2]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.value_child[0]);		i++;
    csb->csb.value_child[3] =
	    XtCreateManagedWidget("value4", xmLabelWidgetClass, form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.slider_child[0]);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.slider_child[2]);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNrightWidget, csb->csb.slider_child[0]);	i++;
    csb->csb.slider_child[3] =
	    XtCreateManagedWidget("slider4", xmScaleWidgetClass,
				  form, args, i);
    XtAddCallback(csb->csb.slider_child[3], XmNvalueChangedCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[3]);
    XtAddCallback(csb->csb.slider_child[3], XmNdragCallback,
		  ChangeLabelCallback, (XtPointer) csb->csb.value_child[3]);
    XtAddCallback(csb->csb.slider_child[3], XmNvalueChangedCallback,
		  Slider4Callback, (XtPointer) csb);
    XtAddCallback(csb->csb.slider_child[3], XmNdragCallback,
		  Slider4Callback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_FORM);		i++;
    button = XtCreateManagedWidget("okButton", xmPushButtonWidgetClass,
				   form, args, i);
    XtAddCallback(button, XmNactivateCallback, OKCallback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNdefaultButton, button);			i++;
    XtSetValues(form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNleftWidget, button);				i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_FORM);		i++;
    button = XtCreateManagedWidget("applyButton", xmPushButtonWidgetClass,
				   form, args, i);

    XtAddCallback(button, XmNactivateCallback, ApplyCallback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNleftWidget, button);				i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_FORM);		i++;
    button = XtCreateManagedWidget("resetButton", xmPushButtonWidgetClass,
				   form, args, i);
    XtAddCallback(button, XmNactivateCallback, ResetCallback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNleftWidget, button);				i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_FORM);		i++;
    button = XtCreateManagedWidget("cancelButton", xmPushButtonWidgetClass,
				  form, args, i);
    XtAddCallback(button, XmNactivateCallback,
		  CancelCallback, (XtPointer) csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNbottomWidget, button);				i++;
    w = XtCreateManagedWidget("separator", xmSeparatorGadgetClass,
			      form, args, i);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNbottomWidget, w);				i++;
    palette_frame = XtCreateManagedWidget("paletteFrame", xmFrameWidgetClass,
					  form, args, i);

    i = 0;
    csb->csb.palette_child =
	    XtCreateManagedWidget("palette", xmDrawingAreaWidgetClass,
				  palette_frame, args, i);
    XtAddCallback(csb->csb.palette_child, XmNexposeCallback,
		  DrawPaletteCallback, (XtPointer) csb);
    XtAddEventHandler(csb->csb.palette_child, ButtonPressMask, False,
		      PalettePress, (XtPointer) csb);

    i = 0;
    menu = XmCreatePulldownMenu(form, "paletteMenu", args, i);
    CreatePaletteMenu(menu, csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNbottomWidget, palette_frame);			i++;
    XtSetArg(args[i], XmNsubMenuId, menu);				i++;
    csb->csb.palette_option_menu_child =
	    XmCreateOptionMenu(form, "paletteOptionMenu",
			       args, i);
    XtManageChild(csb->csb.palette_option_menu_child);

    i = 0;
    menu = XmCreatePulldownMenu(form, "displayMenu", args, i);
    CreateDisplayMenu(menu, csb);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_POSITION);		i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_FORM);			i++;
    XtSetArg(args[i], XmNsubMenuId, menu);				i++;
    csb->csb.display_option_menu_child =
	    XmCreateOptionMenu(form, "displayOptionMenu",
			       args, i);
    XtManageChild(csb->csb.display_option_menu_child);

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNleftWidget, csb->csb.display_option_menu_child);i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNbottomWidget, palette_frame);			i++;
    dock_frame = XtCreateManagedWidget("dockFrame", xmFrameWidgetClass,
				       form, args, i);

    i = 0;
    csb->csb.dock_child =
	    XtCreateManagedWidget("dock", xmDrawingAreaWidgetClass,
				  dock_frame, args, i);
    XtAddCallback(csb->csb.dock_child, XmNexposeCallback,
		  DrawDockCallback, (XtPointer) csb);
    XtAddEventHandler(csb->csb.dock_child, ButtonPressMask, False, DockPress,
		      (XtPointer) csb);

    {
	Dimension height;
	int q;

	XtVaGetValues(csb->csb.dock_child, XtNheight, &height, NULL);
	if (height < csb->csb.cell_size) height = csb->csb.cell_size;
	else if (height % csb->csb.cell_size != 0) {
	    q = height / csb->csb.cell_size;
	    height = csb->csb.cell_size * q;
	}
	XtVaSetValues(csb->csb.dock_child, XtNheight, height, NULL);
    }

    i = 0;
    XtSetArg(args[i], XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET);	i++;
    XtSetArg(args[i], XmNleftWidget, dock_frame);			i++;
    XtSetArg(args[i], XmNrightAttachment, XmATTACH_FORM);		i++;
    XtSetArg(args[i], XmNtopAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNtopWidget, csb->csb.display_option_menu_child);i++;
    XtSetArg(args[i], XmNbottomAttachment, XmATTACH_WIDGET);		i++;
    XtSetArg(args[i], XmNbottomWidget, dock_frame);			i++;
    w = XtCreateManagedWidget("patchFrame", xmFrameWidgetClass,
			      form, args, i);

    i = 0;
    csb->csb.patch_child =
	    XtCreateManagedWidget("patch", xmDrawingAreaWidgetClass,
				  w, args, i);
    XtAddCallback(csb->csb.patch_child, XmNexposeCallback,
		  FillPatchCallback, (XtPointer) csb);
    XtAddRawEventHandler(csb->csb.patch_child, ButtonPressMask,
			 False, PatchPress, (XtPointer) csb);
    XtAddRawEventHandler(csb->csb.patch_child, ButtonReleaseMask,
			 False, PatchRelease, (XtPointer) csb);
}

static void NoBackgroundPixel(ColorSelectionBoxWidget csb)
{
    Widget w, message;

    csb->csb.no_background = True;
    w = XtNameToWidget((Widget) csb, "*displayX");
    XtSetSensitive(w, False);
    w = XtNameToWidget((Widget) csb, "*displayBoth");
    XtSetSensitive(w, False);
    w = XtNameToWidget((Widget) csb, "*displayDPS");
    XtVaSetValues(csb->csb.display_option_menu_child, XmNmenuHistory, w, NULL);

    message = XmCreateInformationDialog(csb->csb.form_child,
					"noBackgroundMessage",
					(ArgList) NULL, 0);
    w =	XmMessageBoxGetChild(message, XmDIALOG_CANCEL_BUTTON);
    XtUnmanageChild(w);
    w =	XmMessageBoxGetChild(message, XmDIALOG_HELP_BUTTON);
    XtUnmanageChild(w);
    
    XtManageChild(message);
}

/* labelString is changed by this */

static void ParseLabels(String labelString, String labels[4], int n)
{
    register char *ch;
    int i;

    ch = labelString;
    for (i = 0; i < n; i++) {
	labels[i] = ch;
	while (*ch != ':' && *ch != '\0') ch++;
	*ch++ = '\0';
    }

    for (i = n; i < 4; i++) labels[i] = NULL;
}

static void SetLabels(ColorSelectionBoxWidget csb, String *labels)
{
    Widget w = (Widget) csb;
    int i;

    for (i = 0; i < 4; i++) {
	if (labels[i] != NULL) {
	    XtVaSetValues(csb->csb.label_child[i],
			  XmNlabelString, CS(labels[i], w), NULL);
	}
    }
}

static void MapChildren(Widget *children, int n)
{
    XtManageChildren(children, n);
}

static void UnmapChildren(Widget *children, int n)
{
    XtUnmanageChildren(children, n);
}

static void SetSliders(ColorSelectionBoxWidget csb)
{
    switch(csb->csb.current_space) {
	case CSBSpaceRGB:		SetRGBValues(csb);	break;
	case CSBSpaceCMYK:		SetCMYKValues(csb);	break;
	case CSBSpaceHSB:		SetHSBValues(csb);	break;
	case CSBSpaceGray:		SetGrayValues(csb);	break;
    }
}

static void SetRGBValues(ColorSelectionBoxWidget csb)
{
    XmScaleSetValue(csb->csb.slider_child[0],
		    TO_PCT(csb->csb.current_color.red));
    XmScaleSetValue(csb->csb.slider_child[1],
		    TO_PCT(csb->csb.current_color.green));
    XmScaleSetValue(csb->csb.slider_child[2],
		    TO_PCT(csb->csb.current_color.blue));
    ChangeLabel(csb->csb.value_child[0], csb->csb.current_color.red);
    ChangeLabel(csb->csb.value_child[1], csb->csb.current_color.green);
    ChangeLabel(csb->csb.value_child[2], csb->csb.current_color.blue);
}

/* ARGSUSED */

static void SetRGBCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Widget rgb;
    Widget children[6];
    String labels[4];
    int i, j;

    csb->csb.current_space = CSBSpaceRGB;

    ParseLabels(csb->csb.rgb_labels, labels, 3);

    rgb = XtNameToWidget((Widget) csb, "*rgb");
    
    XtVaSetValues(csb->csb.model_option_menu_child, XmNmenuHistory, rgb, NULL);

    SetLabels(csb, labels);

    SetRGBValues(csb);

    j = 0;
    for (i = 1; i < 3; i++) {
	children[j++] = csb->csb.label_child[i];
	children[j++] = csb->csb.slider_child[i];
	children[j++] = csb->csb.value_child[i];
    }

    MapChildren(children, 6);

    children[0] = csb->csb.label_child[3];
    children[1] = csb->csb.slider_child[3];
    children[2] = csb->csb.value_child[3];

    UnmapChildren(children, 3);

    ColorizeSliders(csb);
    FillPatch(csb);
}

static void SetCMYKValues(ColorSelectionBoxWidget csb)
{
    XmScaleSetValue(csb->csb.slider_child[0],
		    TO_PCT(csb->csb.current_color.cyan));
    XmScaleSetValue(csb->csb.slider_child[1],
		    TO_PCT(csb->csb.current_color.magenta));
    XmScaleSetValue(csb->csb.slider_child[2],
		    TO_PCT(csb->csb.current_color.yellow));
    XmScaleSetValue(csb->csb.slider_child[3],
		    TO_PCT(csb->csb.current_color.black));
    ChangeLabel(csb->csb.value_child[0], csb->csb.current_color.cyan);
    ChangeLabel(csb->csb.value_child[1], csb->csb.current_color.magenta);
    ChangeLabel(csb->csb.value_child[2], csb->csb.current_color.yellow);
    ChangeLabel(csb->csb.value_child[3], csb->csb.current_color.black);
}

/* ARGSUSED */

static void SetCMYKCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Widget cmyk;
    Widget children[9];
    String labels[4];
    int i, j;

    csb->csb.current_space = CSBSpaceCMYK;

    ParseLabels(csb->csb.cmyk_labels, labels, 4);

    cmyk = XtNameToWidget((Widget) csb, "*cmyk");
    
    XtVaSetValues(csb->csb.model_option_menu_child,
		  XmNmenuHistory, cmyk, NULL);

    SetLabels(csb, labels);

    SetCMYKValues(csb);

    j = 0;
    for (i = 1; i < 4; i++) {
	children[j++] = csb->csb.label_child[i];
	children[j++] = csb->csb.slider_child[i];
	children[j++] = csb->csb.value_child[i];
    }

    MapChildren(children, 9);

    ColorizeSliders(csb);
    FillPatch(csb);
}

static void SetHSBValues(ColorSelectionBoxWidget csb)
{
    XmScaleSetValue(csb->csb.slider_child[0],
		    TO_PCT(csb->csb.current_color.hue));
    XmScaleSetValue(csb->csb.slider_child[1],
		    TO_PCT(csb->csb.current_color.saturation));
    XmScaleSetValue(csb->csb.slider_child[2],
		    TO_PCT(csb->csb.current_color.brightness));
    ChangeLabel(csb->csb.value_child[0], csb->csb.current_color.hue);
    ChangeLabel(csb->csb.value_child[1], csb->csb.current_color.saturation);
    ChangeLabel(csb->csb.value_child[2], csb->csb.current_color.brightness);
}

/* ARGSUSED */

static void SetHSBCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Widget hsb;
    Widget children[6];
    String labels[4];
    int i, j;

    csb->csb.current_space = CSBSpaceHSB;

    ParseLabels(csb->csb.hsb_labels, labels, 3);

    hsb = XtNameToWidget((Widget) csb, "*hsb");
    
    XtVaSetValues(csb->csb.model_option_menu_child, XmNmenuHistory, hsb, NULL);

    SetLabels(csb, labels);

    SetHSBValues(csb);

    j = 0;
    for (i = 1; i < 3; i++) {
	children[j++] = csb->csb.label_child[i];
	children[j++] = csb->csb.slider_child[i];
	children[j++] = csb->csb.value_child[i];
    }

    MapChildren(children, 6);

    children[0] = csb->csb.label_child[3];
    children[1] = csb->csb.slider_child[3];
    children[2] = csb->csb.value_child[3];

    UnmapChildren(children, 3);

    ColorizeSliders(csb);
    FillPatch(csb);
}

static void SetGrayValues(ColorSelectionBoxWidget csb)
{
    XmScaleSetValue(csb->csb.slider_child[0],
		    TO_PCT(csb->csb.current_color.gray));
    ChangeLabel(csb->csb.value_child[0], csb->csb.current_color.gray);
}

/* ARGSUSED */

static void SetGrayCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Widget gray;
    Widget children[9];
    String labels[4];
    int i, j;

    csb->csb.current_space = CSBSpaceGray;

    gray = XtNameToWidget((Widget) csb, "*gray");
    
    XtVaSetValues(csb->csb.model_option_menu_child, XmNmenuHistory, gray, NULL);

    labels[0] = csb->csb.gray_labels;
    labels[1] = labels[2] = labels[3] = NULL;
    SetLabels(csb, labels);

    SetGrayValues(csb);

    j = 0;
    for (i = 1; i < 4; i++) {
	children[j++] = csb->csb.label_child[i];
	children[j++] = csb->csb.slider_child[i];
	children[j++] = csb->csb.value_child[i];
    }

    UnmapChildren(children, 9);

    ColorizeSliders(csb);
    FillPatch(csb);
}

static void RGBToCMYK(ColorSelectionBoxWidget csb)
{
    csb->csb.current_color.cyan = 1.0 - csb->csb.current_color.red;
    csb->csb.current_color.magenta = 1.0 - csb->csb.current_color.green;
    csb->csb.current_color.yellow = 1.0 - csb->csb.current_color.blue;
    csb->csb.current_color.black = 0.0;
}

static void RGBToGray(ColorSelectionBoxWidget csb)
{
    csb->csb.current_color.gray = .3 * csb->csb.current_color.red +
	    .59 * csb->csb.current_color.green +
	    .11 * csb->csb.current_color.blue;
}

static void HSBToRGB(ColorSelectionBoxWidget csb)
{
    double r, g, bl;
    double h, s, b;
    double f, m, n, k;
    int	i;
	
    if (csb->csb.current_color.saturation == 0) {
	r = g = bl = csb->csb.current_color.brightness;
    } else {
	h = csb->csb.current_color.hue;
	s = csb->csb.current_color.saturation;
	b = csb->csb.current_color.brightness;

	h = 6.0 * h;
	if (h >= 6.0) h = 0.0;
	i = (int) h;
	f = h - (double)i;
	m = b * (1.0 - s);
	n = b * (1.0 - (s * f));
	k = b * (1.0 - (s * (1.0 - f)));

	switch(i) {
	    default:
	    case 0:     r = b;		g = k;		bl = m;		break;
	    case 1:	r = n;		g = b;		bl = m;		break;
	    case 2:	r = m;		g = b;		bl = k;		break;
	    case 3:	r = m;		g = n;		bl = b;		break;
	    case 4:	r = k;		g = m;		bl = b;		break;
	    case 5:	r = b;		g = m;		bl = n;		break;
	}
    }

    csb->csb.current_color.red = r;
    csb->csb.current_color.green = g;
    csb->csb.current_color.blue = bl;
}

static void RGBToHSB(ColorSelectionBoxWidget csb)
{
    double hue, sat, value;
    double diff, x, r, g, b;
    double red, green, blue;

    red = csb->csb.current_color.red;
    green = csb->csb.current_color.green;
    blue = csb->csb.current_color.blue;

    hue = sat = 0.0;
    value = x = red;
    if (green > value) value = green;  else x = green;
    if (blue > value) value = blue;
    if (blue < x) x = blue;

    if (value != 0.0) {
	diff = value - x;
	if (diff != 0.0) {
	    sat = diff / value;
	    r = (value - red) / diff;
	    g = (value - green) / diff;
	    b = (value - blue) / diff;
	    if      (red == value)   hue = (green == x) ? 5.0 + b : 1.0 - g;
	    else if (green == value) hue = (blue == x) ? 1.0 + r : 3.0 - b;
	    else                     hue = (red == x) ? 3.0 + g : 5.0 - r;
	    hue /= 6.0;  if (hue >= 1.0 || hue <= 0.0) hue = 0.0;
	}
    }
    csb->csb.current_color.hue = hue;
    csb->csb.current_color.saturation = sat;
    csb->csb.current_color.brightness = value;
}

static void UpdateColorSpaces(
    ColorSelectionBoxWidget csb,
    CSBColorSpace masterSpace)
{
    switch (masterSpace) {
	case CSBSpaceRGB:
	    RGBToCMYK(csb);
	    RGBToHSB(csb);
	    RGBToGray(csb);
	    break;

	case CSBSpaceCMYK:
	    csb->csb.current_color.red =
		    1.0 - MIN(1.0, csb->csb.current_color.cyan +
			           csb->csb.current_color.black);
	    csb->csb.current_color.green =
		    1.0 - MIN(1.0, csb->csb.current_color.magenta +
			           csb->csb.current_color.black);
	    csb->csb.current_color.blue =
		    1.0 - MIN(1.0, csb->csb.current_color.yellow +
			           csb->csb.current_color.black);
	    RGBToHSB(csb);
	    RGBToGray(csb);
	    break;

	case CSBSpaceHSB:
	    HSBToRGB(csb);
	    RGBToCMYK(csb);
	    RGBToGray(csb);
	    break;

	case CSBSpaceGray:
	    csb->csb.current_color.red = csb->csb.current_color.green =
		    csb->csb.current_color.blue = csb->csb.current_color.gray;

	    csb->csb.current_color.hue =
		    csb->csb.current_color.saturation = 0.0;
	    csb->csb.current_color.brightness = csb->csb.current_color.gray;

	    csb->csb.current_color.cyan = csb->csb.current_color.magenta =
		    csb->csb.current_color.yellow = 0.0;
	    csb->csb.current_color.black = 1.0 - csb->csb.current_color.gray;
	    break;
    }
}

/* ARGSUSED */

static void Slider1Callback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    XmScaleCallbackStruct *scaleData = (XmScaleCallbackStruct *) callData;

    switch(csb->csb.current_space) {
	case CSBSpaceRGB:
	    csb->csb.current_color.red = scaleData->value / 100.0;
	    break;
	case CSBSpaceCMYK:
	    csb->csb.current_color.cyan = scaleData->value / 100.0;
	    break;
	case CSBSpaceHSB:
	    csb->csb.current_color.hue = scaleData->value / 100.0;
	    break;
	case CSBSpaceGray:
	    csb->csb.current_color.gray = scaleData->value / 100.0;
	    break;
    }

    UpdateColorSpaces(csb, csb->csb.current_space);
    DoValueChangedCallback(csb);
    FillPatch(csb);
}

/* ARGSUSED */

static void Slider2Callback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    XmScaleCallbackStruct *scaleData = (XmScaleCallbackStruct *) callData;

    switch(csb->csb.current_space) {
	case CSBSpaceRGB:
	    csb->csb.current_color.green = scaleData->value / 100.0;
	    break;
	case CSBSpaceCMYK:
	    csb->csb.current_color.magenta = scaleData->value / 100.0;
	    break;
	case CSBSpaceHSB:
	    csb->csb.current_color.saturation = scaleData->value / 100.0;
	    break;
	case CSBSpaceGray:
	    break;
    }

    UpdateColorSpaces(csb, csb->csb.current_space);
    DoValueChangedCallback(csb);
    FillPatch(csb);
}

/* ARGSUSED */

static void Slider3Callback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    XmScaleCallbackStruct *scaleData = (XmScaleCallbackStruct *) callData;

    switch(csb->csb.current_space) {
	case CSBSpaceRGB:
	    csb->csb.current_color.blue = scaleData->value / 100.0;
	    break;
	case CSBSpaceCMYK:
	    csb->csb.current_color.yellow = scaleData->value / 100.0;
	    break;
	case CSBSpaceHSB:
	    csb->csb.current_color.brightness = scaleData->value / 100.0;
	    break;
	case CSBSpaceGray:
	    break;
    }

    UpdateColorSpaces(csb, csb->csb.current_space);
    DoValueChangedCallback(csb);
    FillPatch(csb);
}

/* ARGSUSED */

static void Slider4Callback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    XmScaleCallbackStruct *scaleData = (XmScaleCallbackStruct *) callData;

    csb->csb.current_color.black = scaleData->value / 100.0;

    UpdateColorSpaces(csb, csb->csb.current_space);
    DoValueChangedCallback(csb);
    FillPatch(csb);
}

static void FillPatch(ColorSelectionBoxWidget csb)
{
    Colormap c;
    XColor xc;
    Widget patch = csb->csb.patch_child;

    if (!XtIsRealized(csb->csb.patch_child)) return;

    if (csb->csb.no_background) {
	XClearArea(XtDisplay(patch), XtWindow(patch), 0, 0, 1000, 1000, True);
	return;
    }

    /* All we have to do is set the background; the expose event will
       do the rest */

    XtVaGetValues(patch, XtNcolormap, (XtPointer) &c, NULL);

    if (csb->csb.current_space == CSBSpaceGray) {
	xc.red = xc.green = xc.blue = TO_X(csb->csb.current_color.gray);
    } else {
	xc.red = TO_X(csb->csb.current_color.red);
	xc.green = TO_X(csb->csb.current_color.green);
	xc.blue = TO_X(csb->csb.current_color.blue);
    }

    if (csb->csb.static_visual) {
	(void) XAllocColor(XtDisplay(patch), c, &xc);
	csb->csb.background = xc.pixel;
	XtVaSetValues(patch, XtNbackground, csb->csb.background, NULL);
    } else {
	xc.pixel = csb->csb.background;
	xc.flags = DoRed | DoGreen | DoBlue;
	XStoreColor(XtDisplay(patch), c, &xc);
    }

    XClearArea(XtDisplay(patch), XtWindow(patch), 0, 0, 1000, 1000, True);
}

/* ARGSUSED */

static void FillPatchCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Dimension height, width;
    float fh, fw;

    if (csb->csb.current_rendering != CSBDisplayX) {
	XtVaGetValues(w, XtNheight, &height, XtNwidth, &width, NULL);
	if (csb->csb.patch_gstate == 0) {
	    XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	    XDPSSetContextDrawable(csb->csb.context, XtWindow(w), height);
	    (void) XDPSCaptureContextGState(csb->csb.context,
					    &csb->csb.patch_gstate);
	} else XDPSSetContextGState(csb->csb.context, csb->csb.patch_gstate);

	switch (csb->csb.current_space) {
	    case CSBSpaceRGB:
	        DPSsetrgbcolor(csb->csb.context, csb->csb.current_color.red,
			       csb->csb.current_color.green,
			       csb->csb.current_color.blue);
		break;
	    case CSBSpaceCMYK:
		DPSsetcmykcolor(csb->csb.context, csb->csb.current_color.cyan,
				csb->csb.current_color.magenta,
				csb->csb.current_color.yellow,
				csb->csb.current_color.black);
		break;
	    case CSBSpaceHSB:
		DPSsethsbcolor(csb->csb.context, csb->csb.current_color.hue,
			       csb->csb.current_color.saturation,
			       csb->csb.current_color.brightness);
		break;
	    case CSBSpaceGray:
		DPSsetgray(csb->csb.context, csb->csb.current_color.gray);
		break;
	}
    }

    switch (csb->csb.current_rendering) {
	case CSBDisplayDPS:
	    DPSrectfill(csb->csb.context, 0.0, 0.0, 1000.0, 1000.0);
	    break;
	case CSBDisplayX:
	    break;
	case CSBDisplayBoth:
	    ToUserSpace(csb, width, height, &fw, &fh);
	    _DPSCTriangle(csb->csb.context, fh, fw);
	    break;
    }	
}

/* The following function Copyright 1987, 1988 by Digital Equipment
Corporation, Maynard, Massachusetts, and the Massachusetts Institute of
Technology, Cambridge, Massachusetts. */

static String GetRootDirName(String buf)
{
#ifndef X_NOT_POSIX
     uid_t uid;
#else
     int uid;
     extern int getuid();
#ifndef SYSV386
     extern struct passwd *getpwuid(), *getpwnam();
#endif
#endif
     struct passwd *pw;
     static char *ptr = NULL;

     if (ptr == NULL) {
        if (!(ptr = getenv("HOME"))) {
            if ((ptr = getenv("USER")) != 0) {
		pw = getpwnam(ptr);
            } else {
                uid = getuid();
                pw = getpwuid(uid);
            }
            if (pw) ptr = pw->pw_dir;
            else {
                ptr = NULL;
                *buf = '\0';
            }
        }
     }

     if (ptr)
        (void) strcpy(buf, ptr);

     buf += strlen(buf);
     *buf = '/';
     buf++;
     *buf = '\0';
     return buf;
}

static void AllocateDock(ColorSelectionBoxWidget csb)
{
    int entry;

    csb->csb.dock_cyan = (float *) XtCalloc(csb->csb.num_cells, sizeof(float));
    csb->csb.dock_magenta =
	    (float *) XtCalloc(csb->csb.num_cells, sizeof(float));
    csb->csb.dock_yellow =
	    (float *) XtCalloc(csb->csb.num_cells, sizeof(float));
    csb->csb.dock_black =
	    (float *) XtCalloc(csb->csb.num_cells, sizeof(float));
    csb->csb.dock_used =
	    (Boolean *) XtCalloc(csb->csb.num_cells, sizeof(Boolean));

    for (entry = 0; entry < csb->csb.num_cells; entry++) {
	csb->csb.dock_used[entry] = 0;
    }
}

static void InitializeDock(ColorSelectionBoxWidget csb)
{
    String dockEnv;
    char homeDir[PATH_BUF_SIZE];
    FILE *dockFile = NULL;
    char fileName[PATH_BUF_SIZE];
#define BUF 256
    char buf[BUF+1];
    int entry;
    float cyan, magenta, yellow, black;
#define CHECK(v) ((v) > 1.0 ? 1.0 : ((v) < 0.0 ? 0.0 : (v)))

    AllocateDock(csb);
    csb->csb.dock_changed = False;

    dockEnv = getenv("DPSCPICKRC");

    if (dockEnv != NULL) dockFile = fopen(dockEnv, "r");

    if (dockFile == NULL) {
	(void) GetRootDirName(homeDir);

	if (dockFile == NULL) {
	    sprintf(fileName, "%s/.dpscpickrc", homeDir);
	    dockFile = fopen(fileName, "r");
	    
	    if (dockFile == NULL) return;
	}
    }

    while (1) {
	if (fgets(buf, BUF, dockFile) == NULL) {
	    fclose(dockFile);
	    return;
	}
	if (sscanf(buf, "%d %f %f %f %f",
		   &entry, &cyan, &magenta, &yellow, &black) == 5) {
	    if (entry <= csb->csb.num_cells) {
		csb->csb.dock_cyan[entry] = CHECK(cyan);
		csb->csb.dock_magenta[entry] = CHECK(magenta);
		csb->csb.dock_yellow[entry] = CHECK(yellow);
		csb->csb.dock_black[entry] = CHECK(black);
		csb->csb.dock_used[entry] = True;
	    }
	}
    }

#undef BUF
#undef CHECK
}

static void SaveDockContents(ColorSelectionBoxWidget csb)
{
    String dockEnv;
    char homeDir[PATH_BUF_SIZE];
    FILE *dockFile = NULL;
    char fileName[PATH_BUF_SIZE];
    int i;

    if (!csb->csb.dock_changed) return;

    dockEnv = getenv("DPSCPICKRC");

    if (dockEnv != NULL) dockFile = fopen(dockEnv, "w");

    if (dockFile == NULL) {
	(void) GetRootDirName(homeDir);

	if (dockFile == NULL) {
	    sprintf(fileName, "%s/.dpscpickrc", homeDir);
	    dockFile = fopen(fileName, "w");
	    
	    if (dockFile == NULL) return;
	}
    }

    for (i = 0; i < csb->csb.num_cells; i++) {
	if (!csb->csb.dock_used[i]) continue;
	fprintf(dockFile, "%d %g %g %g %g\n", i, csb->csb.dock_cyan[i],
		csb->csb.dock_magenta[i], csb->csb.dock_yellow[i],
		csb->csb.dock_black[i]);
    }
    fclose(dockFile);
    csb->csb.dock_changed = False;
}

/* ARGSUSED */

static void DrawDockCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;

    XClearArea(XtDisplay(csb), XtWindow(csb->csb.dock_child),
	       0, 0, 1000, 1000, False);
    DrawDock(csb);
}

static void DrawDock(ColorSelectionBoxWidget csb)
{
    Dimension height;
    float w, h;
    int lines;
    int i, row, col;
    Boolean didAny = False;

    XtVaGetValues(csb->csb.dock_child, XtNheight, &height, NULL);

    lines = height / csb->csb.cell_size;

    if (csb->csb.dock_gstate == 0) {
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context,
			       XtWindow(csb->csb.dock_child), height);
	(void) XDPSCaptureContextGState(csb->csb.context,
					&csb->csb.dock_gstate);
    } else XDPSSetContextGState(csb->csb.context, csb->csb.dock_gstate);

    ToUserSpace(csb, csb->csb.cell_size, csb->csb.cell_size, &w, &h);

    for (i = 0; i < csb->csb.num_cells; i++) {
	if (!csb->csb.dock_used[i]) continue;
	row = (lines - 1) - (i % lines);
	col = i / lines;

	DPSsetcmykcolor(csb->csb.context, csb->csb.dock_cyan[i],
			csb->csb.dock_magenta[i], csb->csb.dock_yellow[i],
			csb->csb.dock_black[i]);
	
	DPSrectfill(csb->csb.context,
		    (float) (col * w), (float) (row * h), w, h);
	didAny = True;
    }
    if (!didAny) _DPSCShowFillMe(csb->csb.context, csb->csb.fill_me);
}

static void StoreColorInDock(
    ColorSelectionBoxWidget csb,
    int x_offset,
    int y_offset,
    Dimension dockHeight)
{
    int i, lines, row, col;

    lines = dockHeight / csb->csb.cell_size;

    row = y_offset / (int) csb->csb.cell_size;
    col = x_offset / (int) csb->csb.cell_size;
    i = col * lines + row;

    if (i >= csb->csb.num_cells) i = csb->csb.num_cells;
    csb->csb.dock_cyan[i] = csb->csb.current_color.cyan;
    csb->csb.dock_magenta[i] = csb->csb.current_color.magenta;
    csb->csb.dock_yellow[i] = csb->csb.current_color.yellow;
    csb->csb.dock_black[i] = csb->csb.current_color.black;
    csb->csb.dock_used[i] = True;
    csb->csb.dock_changed = True;
    DrawDock(csb);
}

/* ARGSUSED */

static void DockPress(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    Dimension height;
    int i, lines, row, col;

    XtVaGetValues(csb->csb.dock_child, XtNheight, &height, NULL);

    lines = height / csb->csb.cell_size;

    row = event->xbutton.y / (int) csb->csb.cell_size;
    col = event->xbutton.x / (int) csb->csb.cell_size;
    i = col * lines + row;
    if (i >= csb->csb.num_cells) i = csb->csb.num_cells;

    if (!csb->csb.dock_used[i]) return;

    csb->csb.current_color.cyan = csb->csb.dock_cyan[i];
    csb->csb.current_color.magenta = csb->csb.dock_magenta[i];
    csb->csb.current_color.yellow = csb->csb.dock_yellow[i];
    csb->csb.current_color.black = csb->csb.dock_black[i];
    UpdateColorSpaces(csb, CSBSpaceCMYK);
    DoValueChangedCallback(csb);
    FillPatch(csb);
    SetSliders(csb);
}

static void InitializePalettes(ColorSelectionBoxWidget csb)
{
    int k;

    for (k = 0; k < PALETTE_MAX; k++) {
	if (csb->csb.palette_function[k] != NULL) {
	    DPSPrintf(csb->csb.context,
		      "/palette%dfunc%d { %s } bind def\n", k, (int) csb,
		      csb->csb.palette_function[k]);
	}
	csb->csb.palette_broken[k] = False;
    }
}

static void InvalidatePalette(ColorSelectionBoxWidget csb)
{
    int len;
    char *buf;
    Widget w;
    register int i = csb->csb.current_palette;

    len = strlen(csb->csb.palette_label[i]) +
	    strlen(csb->csb.broken_palette_label) + 2;
    len = MAX(len, 11);
    buf = (char *) XtMalloc(len);

    csb->csb.palette_broken[i] = True;
    sprintf(buf, "*palette%d", csb->csb.current_palette);
    w = XtNameToWidget((Widget) csb, buf);
    if (w != NULL) XtSetSensitive(w, False);
    sprintf(buf, "%s %s", csb->csb.palette_label[i],
	    csb->csb.broken_palette_label);
    len = strlen(buf);
    XtVaSetValues(w, XtVaTypedArg, XmNlabelString, XtRString, buf, len, NULL);
}

static void DoPalette(
    ColorSelectionBoxWidget csb,
    Dimension pixelWidth,
    float w,
    float h)
{
    char whichFunc[25];
    int steps;
    int success;

    sprintf(whichFunc, "palette%dfunc%d", csb->csb.current_palette, (int) csb);
    if (csb->csb.visual_class == TrueColor) steps = pixelWidth / 2;
    else steps = pixelWidth / 4;
	
    if (csb->csb.palette_color_dependent[csb->csb.current_palette]) {
	switch (csb->csb.palette_space[csb->csb.current_palette]) {
	    case CSBSpaceRGB:
		_DPSCDoRGBColorPalette(csb->csb.context, whichFunc,
				       csb->csb.current_color.red,
				       csb->csb.current_color.green,
				       csb->csb.current_color.blue,
				       w, h, steps, &success);
		break;
	    case CSBSpaceCMYK:
		_DPSCDoCMYKColorPalette(csb->csb.context, whichFunc,
					csb->csb.current_color.cyan,
					csb->csb.current_color.magenta,
					csb->csb.current_color.yellow,
					csb->csb.current_color.black,
					w, h, steps, &success);
		break;
	    case CSBSpaceHSB:
		_DPSCDoHSBColorPalette(csb->csb.context, whichFunc,
				       csb->csb.current_color.hue,
				       csb->csb.current_color.saturation,
				       csb->csb.current_color.brightness,
				       w, h, steps, &success);
		break;
	    case CSBSpaceGray:
		_DPSCDoGrayColorPalette(csb->csb.context, whichFunc,
					csb->csb.current_color.gray,
					w, h, steps, &success);
		break;
	}
    } else {
	switch (csb->csb.palette_space[csb->csb.current_palette]) {
	    case CSBSpaceRGB:
		_DPSCDoRGBPalette(csb->csb.context, whichFunc, w, h,
				  steps, &success);
		break;
	    case CSBSpaceCMYK:
		_DPSCDoCMYKPalette(csb->csb.context, whichFunc, w, h,
				   steps, &success);
		break;
	    case CSBSpaceHSB:
		_DPSCDoHSBPalette(csb->csb.context, whichFunc, w, h,
				  steps, &success);
		break;
	    case CSBSpaceGray:
		_DPSCDoGrayPalette(csb->csb.context, whichFunc, w, h,
				   steps, &success);
		break;
	}
    }
    if (!success) {
	InvalidatePalette(csb);
	_DPSCShowMessage(csb->csb.context, csb->csb.broken_palette_message);
    }
}

static void DrawPalette(ColorSelectionBoxWidget csb)
{
    DrawPaletteCallback(csb->csb.palette_child,
			(XtPointer) csb, (XtPointer) NULL);
}

/* ARGSUSED */

static void DrawPaletteCallback(
    Widget wid,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Dimension width, height;
    Pixmap palette_pixmap;
    int depth;
    float w, h;

    if (csb->csb.palette_broken[csb->csb.current_palette]) return;
    if (!csb->csb.palette_pixmap_valid) {
	XtVaGetValues(csb->csb.palette_child,
		      XtNwidth, &width, XtNheight, &height,
		      XtNdepth, &depth, NULL);

	ToUserSpace(csb, width, height, &w, &h);

	palette_pixmap =
		XCreatePixmap(XtDisplay(csb), XtWindow(csb->csb.palette_child),
			      width, height, depth);
    
	XDPSSetContextGState(csb->csb.context, csb->csb.base_gstate);
	XDPSSetContextDrawable(csb->csb.context, palette_pixmap, height);

	DoPalette(csb, width, w, h);
	csb->csb.palette_color = csb->csb.current_color;
	DPSWaitContext(csb->csb.context);
	XtVaSetValues(csb->csb.palette_child,
		      XtNbackgroundPixmap, palette_pixmap, NULL);
	XFreePixmap(XtDisplay(csb), palette_pixmap);
	csb->csb.palette_pixmap_valid = True;
    }
}

/* ARGSUSED */

static void PalettePress(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    Dimension width;
    float pct;
    char whichFunc[25];
    int success;
    float f1, f2, f3, f4;

    if (csb->csb.palette_broken[csb->csb.current_palette]) return;

    sprintf(whichFunc, "palette%dfunc%d", csb->csb.current_palette, (int) csb);

    XtVaGetValues(csb->csb.palette_child, XtNwidth, &width, NULL);

    pct = ((float) event->xbutton.x) / ((float) width);

    if (csb->csb.palette_color_dependent[csb->csb.current_palette]) {
	switch (csb->csb.palette_space[csb->csb.current_palette]) {
	    case CSBSpaceRGB:
		_DPSCQueryRGBColorPalette(csb->csb.context, whichFunc, pct,
					  csb->csb.palette_color.red,
					  csb->csb.palette_color.green,
					  csb->csb.palette_color.blue,
					  &f1, &f2, &f3, &success);
		if (success) {
		    csb->csb.current_color.red = f1;
		    csb->csb.current_color.green = f2;
		    csb->csb.current_color.blue = f3;
		}
		break;
	    case CSBSpaceCMYK:
		_DPSCQueryCMYKColorPalette(csb->csb.context, whichFunc, pct,
					   csb->csb.palette_color.cyan,
					   csb->csb.palette_color.magenta,
					   csb->csb.palette_color.yellow,
					   csb->csb.palette_color.black,
					   &f1, &f2, &f3, &f4, &success);
		if (success) {
		    csb->csb.current_color.cyan = f1;
		    csb->csb.current_color.magenta = f2;
		    csb->csb.current_color.yellow = f3;
		    csb->csb.current_color.black = f4;
		}
		break;
	    case CSBSpaceHSB:
		_DPSCQueryHSBColorPalette(csb->csb.context, whichFunc, pct,
					  csb->csb.palette_color.hue,
					  csb->csb.palette_color.saturation,
					  csb->csb.palette_color.brightness,
					  &f1, &f2, &f3, &success);
		if (success) {
		    csb->csb.current_color.hue = f1;
		    csb->csb.current_color.saturation = f2;
		    csb->csb.current_color.brightness = f3;
		}
		break;
	    case CSBSpaceGray:
		_DPSCQueryGrayColorPalette(csb->csb.context, whichFunc, pct,
					   csb->csb.palette_color.gray,
					   &f1, &success);
		if (success) csb->csb.current_color.gray = f1;
		break;
	}
    } else {
	switch (csb->csb.palette_space[csb->csb.current_palette]) {
	    case CSBSpaceRGB:
		_DPSCQueryRGBPalette(csb->csb.context, whichFunc, pct,
				     &f1, &f2, &f3, &success);
		if (success) {
		    csb->csb.current_color.red = f1;
		    csb->csb.current_color.green = f2;
		    csb->csb.current_color.blue = f3;
		}
		break;
	    case CSBSpaceCMYK:
		_DPSCQueryCMYKPalette(csb->csb.context, whichFunc, pct,
				      &f1, &f2, &f3, &f4, &success);
		if (success) {
		    csb->csb.current_color.cyan = f1;
		    csb->csb.current_color.magenta = f2;
		    csb->csb.current_color.yellow = f3;
		    csb->csb.current_color.black = f4;
		}
		break;
	    case CSBSpaceHSB:
		_DPSCQueryHSBPalette(csb->csb.context, whichFunc, pct,
				     &f1, &f2, &f3, &success);
		if (success) {
		    csb->csb.current_color.hue = f1;
		    csb->csb.current_color.saturation = f2;
		    csb->csb.current_color.brightness = f3;
		}
		break;
	    case CSBSpaceGray:
		_DPSCQueryGrayPalette(csb->csb.context, whichFunc, pct,
				      &f1, &success);
		if (success) csb->csb.current_color.gray = f1;
		break;
	}
    }
    if (!success) InvalidatePalette(csb);
    else {
	UpdateColorSpaces(csb,
			  csb->csb.palette_space[csb->csb.current_palette]);
	DoValueChangedCallback(csb);
	FillPatch(csb);
	SetSliders(csb);
    }
}

/* ARGSUSED */

static void DoEyedropCallback(
    Widget w,
    XtPointer clientData, XtPointer callData)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) clientData;
    Pixmap eyedropBitmap, eyedropMaskBitmap;
    XColor black, fg;
    Display *dpy;
    unsigned int x, y;
    XEvent ev;

    dpy = XtDisplay(w);

    black.red = 0;
    black.green = 0;
    black.blue = 0;

    fg.red = 65535;
    fg.green = 65535;
    fg.blue = 65535;

    if (csb->csb.eyedrop == None) {
	XQueryBestCursor(dpy, XtWindow(w), 32, 32,
			 &x, &y);

	if (x >= 32 && y >= 32) {
	    eyedropBitmap =
		    XCreateBitmapFromData(dpy, XtWindow(w),
					  (char *) eyedrop32_bits,
					  eyedrop32_width, eyedrop32_height);

	    eyedropMaskBitmap =
		    XCreateBitmapFromData(dpy, XtWindow(w),
					  (char *) eyedropmask32_bits,
					  eyedropmask32_width,
					  eyedropmask32_height);

	    csb->csb.eyedrop =
		    XCreatePixmapCursor(dpy, eyedropBitmap,
					eyedropMaskBitmap,
					&fg, &black,
					eyedrop32_x_hot, eyedrop32_y_hot);
	} else {
	    eyedropBitmap =
		    XCreateBitmapFromData(dpy, XtWindow(w),
					  (char *) eyedrop16_bits,
					  eyedrop16_width, eyedrop16_height);

	    eyedropMaskBitmap =
		    XCreateBitmapFromData(dpy, XtWindow(w),
					  (char *) eyedropmask16_bits,
					  eyedropmask16_width,
					  eyedropmask16_height);

	    csb->csb.eyedrop =
		    XCreatePixmapCursor(dpy, eyedropBitmap,
					eyedropMaskBitmap,
					&fg, &black,
					eyedrop16_x_hot, eyedrop16_y_hot);
	}
    } else {
	XRecolorCursor(dpy, csb->csb.eyedrop, &fg, &black);
    }
				  
    (void) XtGrabPointer(w, False,
			 PointerMotionMask | PointerMotionHintMask |
				 ButtonReleaseMask,
			 GrabModeAsync, GrabModeAsync,
			 None, csb->csb.eyedrop,
			 XtLastTimestampProcessed(dpy));
    csb->csb.eyedrop_grabbed = True;

    ev.type = 0;
    EyedropPointer(w, (XtPointer) csb, &ev, (Boolean *) NULL);
}

/* ARGSUSED */

static void EyedropPointer(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    XColor fg, black;
    Window root, child, stop, old_child = None;
    int root_x, root_y, x, y;
    unsigned int mask;
    XWindowAttributes att;
    XImage *image;
    Pixel pixel;
    Colormap colormap = 0;
    Display *dpy = XtDisplay(w);

    if (!csb->csb.eyedrop_grabbed) return;

    if (event->type == ButtonPress || event->type == ButtonRelease) {
	root = event->xbutton.root;
	root_x = event->xbutton.x_root;
	root_y = event->xbutton.y_root;

	XTranslateCoordinates(dpy, root, root, root_x, root_y, &x, &y, &child);

    } else {
	XQueryPointer(dpy, RootWindowOfScreen(XtScreen(w)),
		      &root, &child, &root_x, &root_y, &x, &y, &mask);
    }

    if (child == None) child = root;
    else {
	stop = child;

	while (stop != None) {
	    XTranslateCoordinates(dpy, root, stop, x, y, &x, &y, &child);
	    root = stop;
	    if (child != None && XGetWindowAttributes(dpy, child, &att) &&
		att.class != InputOutput) break;
	    stop = child;
	}
	child = root;
    }

    if (child != old_child) {
	XGetWindowAttributes(dpy, child, &att);
	colormap = att.colormap;
	old_child = child;
    }

    image = XGetImage(dpy, child, x, y, 1, 1, AllPlanes, XYPixmap);

    pixel = XGetPixel(image, 0, 0);

    XDestroyImage(image);
    fg.pixel = pixel;
    XQueryColors(dpy, colormap, &fg, 1);
	
    black.red = 0;
    black.green = 0;
    black.blue = 0;

    XRecolorCursor(dpy, csb->csb.eyedrop, &fg, &black);

    if (event->type == ButtonRelease) {
	XtUngrabPointer(w, XtLastTimestampProcessed(dpy));
	csb->csb.eyedrop_grabbed = False;

	csb->csb.current_color.red = (float) fg.red / 65535.0;
	csb->csb.current_color.green = (float) fg.green / 65535.0;
	csb->csb.current_color.blue = (float) fg.blue / 65535.0;
	UpdateColorSpaces(csb, CSBSpaceRGB);
	DoValueChangedCallback(csb);
	FillPatch(csb);
	SetSliders(csb);
   }
}

/* ARGSUSED */

static void PatchPress(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    Pixmap squareBitmap, squareMaskBitmap;
    XColor black, fg;
    Display *dpy;

    dpy = XtDisplay(w);

    black.red = 0;
    black.green = 0;
    black.blue = 0;

    fg.red = TO_X(csb->csb.current_color.red);
    fg.green = TO_X(csb->csb.current_color.green);
    fg.blue = TO_X(csb->csb.current_color.blue);

    if (csb->csb.square == None) {
	squareBitmap =
		XCreateBitmapFromData(dpy, XtWindow(w), (char *) square_bits,
				      square_width, square_height);

	squareMaskBitmap =
		XCreateBitmapFromData(dpy, XtWindow(w),
				      (char *) squaremask_bits,
				      squaremask_width, squaremask_height);

	csb->csb.square =
		XCreatePixmapCursor(dpy, squareBitmap, squareMaskBitmap,
				    &fg, &black, square_x_hot, square_y_hot);
    } else {
	XRecolorCursor(dpy, csb->csb.square, &fg, &black);
    }

    (void) XtGrabPointer(w, False, ButtonReleaseMask,
			 GrabModeAsync, GrabModeAsync,
			 None, csb->csb.square, XtLastTimestampProcessed(dpy));
}

/* ARGSUSED */

static void PatchRelease(
    Widget w,
    XtPointer data,
    XEvent *event,
    Boolean *goOn)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) data;
    Dimension width, height;
    Position left, top;

    XtUngrabPointer(w, XtLastTimestampProcessed(XtDisplay(w)));
    XFlush(XtDisplay(w));

    XtVaGetValues(csb->csb.dock_child, XtNwidth, &width,
		  XtNheight, &height, NULL);

    XtTranslateCoords(csb->csb.dock_child, (Position) 0, (Position) 0,
		      &left, &top);

    if ((int) event->xbutton.x_root >= left &&
	(int) event->xbutton.x_root <= left + (int) width &&
	(int) event->xbutton.y_root >= top  &&
	(int) event->xbutton.y_root <= top + (int) height) {
	StoreColorInDock(csb, event->xbutton.x_root - left,
			 event->xbutton.y_root - top, height);
    }
}

static void GetVisualInfo(
    ColorSelectionBoxWidget csb,
    Visual **visual)
{
    Widget w = (Widget) csb;
    XVisualInfo *vip, viproto;
    int n;
    XWindowAttributes xwa;

    XGetWindowAttributes(XtDisplay(w), XtWindow(w), &xwa);

    *visual = viproto.visual = xwa.visual;
    viproto.visualid = XVisualIDFromVisual(xwa.visual);
    vip = XGetVisualInfo(XtDisplay(w), VisualIDMask, &viproto, &n);

    if (n != 1) {
	csb->csb.static_visual = False;	/* Actually we have no idea, but... */
	csb->csb.visual_class = PseudoColor;
    } else {
	csb->csb.visual_class = vip->class;
	csb->csb.static_visual = (vip->class == StaticGray ||
				  vip->class == TrueColor ||
				  vip->class == StaticColor);
    }

    if (n > 0) XFree((char *) vip);
}

static void SetBackground(ColorSelectionBoxWidget csb)
{
    Colormap c;
    XColor xc;
    int status;
    unsigned long pix;
    unsigned long mask;

    XtVaGetValues(csb->csb.patch_child, XtNcolormap, (XtPointer) &c, NULL);

    if (csb->csb.current_space == CSBSpaceGray) {
	xc.red = xc.green = xc.blue = TO_X(csb->csb.current_color.gray);
    } else {
	xc.red = TO_X(csb->csb.current_color.red);
	xc.green = TO_X(csb->csb.current_color.green);
	xc.blue = TO_X(csb->csb.current_color.blue);
    }

    if (csb->csb.static_visual) {
	status = XAllocColor(XtDisplay(csb), c, &xc);
	if (status == 0) NoBackgroundPixel(csb);
	else {
	    csb->csb.background = xc.pixel;
	    XtVaSetValues(csb->csb.patch_child,
			  XtNbackground, csb->csb.background, NULL);
	}

    } else {
	if (csb->csb.visual_class == DirectColor) {
	    status = XAllocColorPlanes(XtDisplay(csb), c,
				       False, &pix, 1, 0, 0, 0,
				       &mask, &mask, &mask);
	} else {
	    status = XAllocColorCells(XtDisplay(csb), c,
				      False, (unsigned long *) NULL, 0,
				      &pix, 1);
	}

	if (status == 0) NoBackgroundPixel(csb);
	else {
	    xc.pixel = pix;
	    xc.flags = DoRed | DoGreen | DoBlue;
	    XStoreColor(XtDisplay(csb), c, &xc);

	    csb->csb.background = xc.pixel;
	    XtVaSetValues(csb->csb.patch_child,
			  XtNbackground, csb->csb.background, NULL);
	}
    }
}

/* ARGSUSED */

static void Initialize(
    Widget request, Widget new,
    ArgList args,
    Cardinal *num_args)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) new;
    Bool inited;
    int i;

    if (csb->csb.rgb_labels != NULL) {
	csb->csb.rgb_labels = XtNewString(csb->csb.rgb_labels);
    }
    if (csb->csb.cmyk_labels != NULL) {
	csb->csb.cmyk_labels = XtNewString(csb->csb.cmyk_labels);
    }
    if (csb->csb.hsb_labels != NULL) {
	csb->csb.hsb_labels = XtNewString(csb->csb.hsb_labels);
    }
    if (csb->csb.gray_labels != NULL) {
	csb->csb.gray_labels = XtNewString(csb->csb.gray_labels);
    }
    if (csb->csb.fill_me != NULL) {
	csb->csb.fill_me = XtNewString(csb->csb.fill_me);
    }
    if (csb->csb.broken_palette_label != NULL) {
	csb->csb.broken_palette_label =
		XtNewString(csb->csb.broken_palette_label);
    }
    if (csb->csb.broken_palette_message != NULL) {
	csb->csb.broken_palette_message =
		XtNewString(csb->csb.broken_palette_message);
    }

    for (i = 0; i < PALETTE_MAX; i++) {
	if (csb->csb.palette_function[i] != NULL) {
	    csb->csb.palette_function[i] =
		    XtNewString(csb->csb.palette_function[i]);
	}
    }

    if (csb->csb.num_cells <= 0) csb->csb.num_cells = 1;

    /* Get the context */

    if (csb->csb.context == NULL) {
	csb->csb.context = XDPSGetSharedContext(XtDisplay(csb));
    }

    if (_XDPSTestComponentInitialized(csb->csb.context,
				      dps_init_bit_csb, &inited) ==
	dps_status_unregistered_context) {
	XDPSRegisterContext(csb->csb.context, False);
    }

    if (!inited) {
	(void) _XDPSSetComponentInitialized(csb->csb.context,
					    dps_init_bit_csb);
	InitializePalettes(csb);
    }

    if (csb->csb.current_palette < 0 ||
	csb->csb.current_palette > PALETTE_MAX ||
	csb->csb.palette_function[csb->csb.current_palette] == NULL) {
	csb->csb.current_palette = 0;
    }

    /* Initialize non-resource fields */

    CreateChildren(csb);
    csb->csb.no_background = False;
    csb->csb.patch_gstate = csb->csb.dock_gstate = 0;
    csb->csb.red_pixmap = csb->csb.green_pixmap = csb->csb.blue_pixmap =
	    csb->csb.cyan_pixmap = csb->csb.magenta_pixmap =
	    csb->csb.yellow_pixmap = csb->csb.black_pixmap =
	    csb->csb.hue_pixmap = csb->csb.sat_pixmap =
	    csb->csb.bright_pixmap = csb->csb.gray_pixmap = None;

    csb->csb.square = csb->csb.eyedrop = None;
    csb->csb.eyedrop_grabbed = False;

    for (i = 0; i < PALETTE_MAX; i++) csb->csb.palette_broken[i] = False;
    csb->csb.palette_pixmap_valid = False;

    csb->csb.current_color.hue = 0.0;
    csb->csb.current_color.saturation = 1.0;
    csb->csb.current_color.brightness = 1.0;
    UpdateColorSpaces(csb, CSBSpaceHSB);
    csb->csb.save_color = csb->csb.current_color;
    SetSliders(csb);

    InitializeDock(csb);    
    SetColorSpace(csb);
    SetRendering(csb);
}

static void Destroy(Widget widget)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) widget;
    Display *dpy = XtDisplay(csb);
    int i;

    /* Lots of stuff to destroy! */

    if (csb->csb.patch_gstate != 0) {
	XDPSFreeContextGState(csb->csb.context, csb->csb.patch_gstate);
    }
    if (csb->csb.dock_gstate != 0) {
	XDPSFreeContextGState(csb->csb.context, csb->csb.dock_gstate);
    }
    if (csb->csb.base_gstate != 0) {
	XDPSFreeContextGState(csb->csb.context, csb->csb.base_gstate);
    }

    if (csb->csb.rgb_labels != NULL) XtFree(csb->csb.rgb_labels);
    if (csb->csb.cmyk_labels != NULL) XtFree(csb->csb.cmyk_labels);
    if (csb->csb.hsb_labels != NULL) XtFree(csb->csb.hsb_labels);
    if (csb->csb.gray_labels != NULL) XtFree(csb->csb.gray_labels);
    if (csb->csb.fill_me != NULL) XtFree(csb->csb.fill_me);
    if (csb->csb.broken_palette_message != NULL) {
	XtFree(csb->csb.broken_palette_message);
    }
    if (csb->csb.broken_palette_label != NULL) {
	XtFree(csb->csb.broken_palette_label);
    }
	
    XtFree((XtPointer) csb->csb.dock_cyan);
    XtFree((XtPointer) csb->csb.dock_magenta);
    XtFree((XtPointer) csb->csb.dock_yellow);
    XtFree((XtPointer) csb->csb.dock_black);
    XtFree((XtPointer) csb->csb.dock_used);

    for (i = 0; i < PALETTE_MAX; i++) {
	if (csb->csb.palette_function[i] != NULL) {
	    XtFree(csb->csb.palette_function[i]);
	}
    }

    if (csb->csb.eyedrop != None) XFreeCursor(dpy, csb->csb.eyedrop);
    if (csb->csb.square != None) XFreeCursor(dpy, csb->csb.square);

    if (csb->csb.red_pixmap != None) XFreePixmap(dpy, csb->csb.red_pixmap);
    if (csb->csb.green_pixmap != None) XFreePixmap(dpy, csb->csb.green_pixmap);
    if (csb->csb.blue_pixmap != None) XFreePixmap(dpy, csb->csb.blue_pixmap);
    if (csb->csb.cyan_pixmap != None) XFreePixmap(dpy, csb->csb.cyan_pixmap);
    if (csb->csb.magenta_pixmap != None)
	    XFreePixmap(dpy, csb->csb.magenta_pixmap);
    if (csb->csb.yellow_pixmap != None)
	    XFreePixmap(dpy, csb->csb.yellow_pixmap);
    if (csb->csb.black_pixmap != None) XFreePixmap(dpy, csb->csb.black_pixmap);
    if (csb->csb.hue_pixmap != None) XFreePixmap(dpy, csb->csb.hue_pixmap);
    if (csb->csb.sat_pixmap != None) XFreePixmap(dpy, csb->csb.sat_pixmap);
    if (csb->csb.bright_pixmap != None)
	    XFreePixmap(dpy, csb->csb.bright_pixmap);
    if (csb->csb.gray_pixmap != None) XFreePixmap(dpy, csb->csb.gray_pixmap);
}

static void ChangeManaged(Widget w)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) w;

    w->core.width = csb->composite.children[0]->core.width;
    w->core.height = csb->composite.children[0]->core.height;
}

/* ARGSUSED */

static XtGeometryResult GeometryManager(
    Widget w,
    XtWidgetGeometry *desired, XtWidgetGeometry *allowed)
{
#define WANTS(flag) (desired->request_mode & flag)

    if (WANTS(XtCWQueryOnly)) return XtGeometryYes;

    if (WANTS(CWWidth)) w->core.width = desired->width;
    if (WANTS(CWHeight)) w->core.height = desired->height;
    if (WANTS(CWX)) w->core.x = desired->x;
    if (WANTS(CWY)) w->core.y = desired->y;
    if (WANTS(CWBorderWidth)) {
	w->core.border_width = desired->border_width;
    }

    return XtGeometryYes;
#undef WANTS
}

static void SetColorSpace(ColorSelectionBoxWidget csb)
{
    switch(csb->csb.current_space) {
	case CSBSpaceRGB:	
	    SetRGBCallback((Widget) csb, (XtPointer) csb, (XtPointer) NULL);
	    break;

	case CSBSpaceCMYK:
	    SetCMYKCallback((Widget) csb, (XtPointer) csb, (XtPointer) NULL);
	    break;

	case CSBSpaceHSB:
	    SetHSBCallback((Widget) csb, (XtPointer) csb, (XtPointer) NULL);
	    break;

	case CSBSpaceGray:
	    SetGrayCallback((Widget) csb, (XtPointer) csb, (XtPointer) NULL);
	    break;
    }
}

static void SetRendering(ColorSelectionBoxWidget csb)
{
    Widget w;

    switch(csb->csb.current_rendering) {
	default:
	case CSBDisplayDPS:
	    w = XtNameToWidget((Widget) csb, "*displayDPS");
	    break;
	case CSBDisplayX:
	    w = XtNameToWidget((Widget) csb, "*displayX");
	    break;
	case CSBDisplayBoth:
	    w = XtNameToWidget((Widget) csb, "*displayBoth");
	    break;
    }
    XtVaSetValues(csb->csb.display_option_menu_child, XmNmenuHistory, w, NULL);
    if (XtIsRealized(csb->csb.patch_child)) {
	XClearArea(XtDisplay(csb), XtWindow(csb->csb.patch_child),
		   0, 0, 1000, 1000, True);
    }
}

static void SetPalette(ColorSelectionBoxWidget csb)
{
    Widget w;
    char buf[10];

    sprintf(buf, "*palette%d", csb->csb.current_palette);
    w = XtNameToWidget((Widget) csb, buf);

    XtVaSetValues(csb->csb.palette_option_menu_child, XmNmenuHistory, w, NULL);

    csb->csb.palette_pixmap_valid = False;
    DrawPalette(csb);
}

static void SetBaseGState(
    ColorSelectionBoxWidget csb,
    Visual *visual)
{
    XStandardColormap colorCube, grayRamp;
    int match;

    /* If the context's colormap matches the widget's colormap, assume that
       everything is already set up right in the color cube department.  This
       allows an application to supply us with a custom color cube by
       installing it in the context before calling us */

    _DPSCColormapMatch(csb->csb.context, csb->core.colormap, &match);

    if (match) {
	XDPSSetContextParameters(csb->csb.context, XtScreen(csb),
				 csb->core.depth, XtWindow(csb),
				 csb->core.height, NULL, NULL,
				 XDPSContextScreenDepth | XDPSContextDrawable);
    } else {
	grayRamp.colormap = colorCube.colormap = csb->core.colormap;

	XDPSCreateStandardColormaps(XtDisplay(csb), XtWindow(csb), visual,
				    0, 0, 0, 0, &colorCube, &grayRamp, False);

	XDPSSetContextParameters(csb->csb.context, XtScreen(csb),
				 csb->core.depth, XtWindow(csb),
				 csb->core.height,
				 (XDPSStandardColormap *) &colorCube,
				 (XDPSStandardColormap *) &grayRamp,
				 XDPSContextScreenDepth | XDPSContextDrawable |
				 XDPSContextRGBMap | XDPSContextGrayMap);
    }

    XDPSCaptureContextGState(csb->csb.context, &csb->csb.base_gstate);
}

/* ARGSUSED */

static Boolean SetValues(
    Widget old, Widget req, Widget new,
    ArgList args,
    Cardinal *num_args)
{
    ColorSelectionBoxWidget oldcsb = (ColorSelectionBoxWidget) old;
    ColorSelectionBoxWidget newcsb = (ColorSelectionBoxWidget) new;
    Bool inited;
    char buf[10];
    Widget w = 0;
    int i;

#define NE(field) newcsb->csb.field != oldcsb->csb.field

    if (NE(rgb_labels)) {
	XtFree(oldcsb->csb.rgb_labels);
	newcsb->csb.rgb_labels = XtNewString(newcsb->csb.rgb_labels);
    }
    if (NE(cmyk_labels)) {
	XtFree(oldcsb->csb.cmyk_labels);
	newcsb->csb.cmyk_labels = XtNewString(newcsb->csb.cmyk_labels);
    }
    if (NE(hsb_labels)) {
	XtFree(oldcsb->csb.hsb_labels);
	newcsb->csb.hsb_labels = XtNewString(newcsb->csb.hsb_labels);
    }
    if (NE(gray_labels)) {
	XtFree(oldcsb->csb.gray_labels);
	newcsb->csb.gray_labels = XtNewString(newcsb->csb.gray_labels);
    }

    if (NE(context)) {
	if (newcsb->csb.context == NULL) {
	    newcsb->csb.context = XDPSGetSharedContext(XtDisplay(newcsb));
	} 
	if (_XDPSTestComponentInitialized(newcsb->csb.context,
					  dps_init_bit_csb, &inited) ==
	    dps_status_unregistered_context) {
	    XDPSRegisterContext(newcsb->csb.context, False);
	}
	if (!inited) {
	    (void) _XDPSSetComponentInitialized(newcsb->csb.context,
						dps_init_bit_csb);
	    InitializePalettes(newcsb);
	}
	newcsb->csb.patch_gstate = newcsb->csb.dock_gstate = 0;
	XDPSFreeContextGState(newcsb->csb.context, newcsb->csb.patch_gstate);
	XDPSFreeContextGState(newcsb->csb.context, newcsb->csb.dock_gstate);
	if (XtIsRealized(newcsb)) {
	    XWindowAttributes xwa;

	    XGetWindowAttributes(XtDisplay(newcsb), XtWindow(newcsb), &xwa);
	    SetBaseGState(newcsb, xwa.visual);
	}
    }	

    if (NE(fill_me)) {
	XtFree(oldcsb->csb.fill_me);
	newcsb->csb.fill_me = XtNewString(newcsb->csb.fill_me);
    }

    if (NE(broken_palette_label)) {
	XtFree(oldcsb->csb.broken_palette_label);
	newcsb->csb.broken_palette_label =
		XtNewString(newcsb->csb.broken_palette_label);
    }

    if (NE(broken_palette_message)) {
	XtFree(oldcsb->csb.broken_palette_message);
	newcsb->csb.broken_palette_message =
		XtNewString(newcsb->csb.broken_palette_message);
    }

    if (newcsb->csb.num_cells <= 0) newcsb->csb.num_cells = 1;
    if (NE(num_cells)) {
	int i, min;

	AllocateDock(newcsb);
	min = MIN(newcsb->csb.num_cells, oldcsb->csb.num_cells);
	for (i = 0; i < min; i++) {
	    newcsb->csb.dock_cyan[i] = oldcsb->csb.dock_cyan[i];
	    newcsb->csb.dock_magenta[i] = oldcsb->csb.dock_magenta[i];
	    newcsb->csb.dock_yellow[i] = oldcsb->csb.dock_yellow[i];
	    newcsb->csb.dock_black[i] = oldcsb->csb.dock_black[i];
	    newcsb->csb.dock_used[i] = oldcsb->csb.dock_used[i];
	}
	XtFree((XtPointer) oldcsb->csb.dock_cyan);
	XtFree((XtPointer) oldcsb->csb.dock_magenta);
	XtFree((XtPointer) oldcsb->csb.dock_yellow);
	XtFree((XtPointer) oldcsb->csb.dock_black);
	XtFree((XtPointer) oldcsb->csb.dock_used);
    }

    for (i = 0; i < PALETTE_MAX; i++) {
	if (NE(palette_function[i]) || NE(palette_label[i])) {
	    sprintf(buf, "*palette%d", i);
	    w = XtNameToWidget((Widget) newcsb, buf);
	}
	if (NE(palette_function[i])) {
	    if (newcsb->csb.palette_function[i] != NULL) {
		DPSPrintf(newcsb->csb.context,
			  "/palette%dfunc%d { %s } bind def\n", i,
			  (int) newcsb, newcsb->csb.palette_function[i]);
		/* Assume the best... */
		newcsb->csb.palette_broken[i] = False;
		XtManageChild(w);
	    } else {
		XtUnmanageChild(w);
		if (newcsb->csb.current_palette == i) {
		    newcsb->csb.current_palette = -1;
		}
	    }
	}
	if (NE(palette_label[i]) || NE(palette_function[i])) {
	    XtSetSensitive(w, True);
	    XtVaSetValues(w, XtVaTypedArg, XmNlabelString, XtRString,
			  newcsb->csb.palette_label[i],
			  strlen(newcsb->csb.palette_label[i])+1, NULL);
	}
    }

    if (NE(current_palette)) {
	if (newcsb->csb.current_palette < 0 ||
	    newcsb->csb.current_palette > PALETTE_MAX ||
	    newcsb->csb.palette_function[newcsb->csb.current_palette] == NULL ||
	    newcsb->csb.palette_broken[newcsb->csb.current_palette]) {
	    newcsb->csb.current_palette = 0;
	}
    }
    if (NE(current_palette) ||
	NE(palette_function[newcsb->csb.current_palette])) SetPalette(newcsb);

    if ((NE(cell_size) || NE(fill_me)) &&
	XtIsRealized(newcsb->csb.dock_child)) {
	XClearArea(XtDisplay(newcsb), XtWindow(newcsb->csb.dock_child),
		   0, 0, 1000, 1000, True);
    }

    if (NE(current_space)) SetColorSpace(newcsb);
    if (NE(current_rendering)) SetRendering(newcsb);

    return False;
#undef NE
}

static void Realize(
    Widget w,
    XtValueMask *mask,
    XSetWindowAttributes *attr)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) w;
    Visual *v;

    (*colorSelectionBoxClassRec.core_class.superclass->core_class.realize)
	    (w, mask, attr);

    GetVisualInfo(csb, &v);
    SetBackground(csb);
    SetBaseGState(csb, v);
    _DPSCGetInvCTM(csb->csb.context, csb->csb.itransform);
}

static void Resize(Widget widget)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) widget;

    XtResizeWidget(csb->csb.form_child, csb->core.width, csb->core.height, 0);
}

static Boolean SetColor(
    Widget w,
    CSBColorSpace space,
    double c1, double c2, double c3, double c4,
    Bool setSpace)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) w;
#define CHECK(c) if ((c) > 1.0 || (c) < 0.0) return False;

    CHECK(c1);
    switch (space) {
	case CSBSpaceRGB:
	    CHECK(c2);
	    CHECK(c3);
	    csb->csb.current_color.red = c1;
	    csb->csb.current_color.green = c2;
	    csb->csb.current_color.blue = c3;
	    break;
	case CSBSpaceCMYK:
	    CHECK(c2);
	    CHECK(c3);
	    CHECK(c4);
	    csb->csb.current_color.cyan = c1;
	    csb->csb.current_color.magenta = c2;
	    csb->csb.current_color.yellow = c3;
	    csb->csb.current_color.black = c4;
	    break;
	case CSBSpaceHSB:
	    CHECK(c2);
	    CHECK(c3);
	    csb->csb.current_color.hue = c1;
	    csb->csb.current_color.saturation = c2;
	    csb->csb.current_color.brightness = c3;
	    break;
	case CSBSpaceGray:
	    csb->csb.current_color.gray = c1;
	    break;
    }
    UpdateColorSpaces(csb, space);
    csb->csb.save_color = csb->csb.current_color;
    DoValueChangedCallback(csb);
    FillPatch(csb);
    SetSliders(csb);
    if (setSpace) XtVaSetValues(w, XtNcurrentSpace, space, NULL);
    return True;
#undef CHECK
}

Boolean CSBSetColor(
    Widget w,
    CSBColorSpace space,
    double c1, double c2, double c3, double c4,
    Bool setSpace)
{
    XtCheckSubclass(w, colorSelectionBoxWidgetClass, NULL);

    return (*((ColorSelectionBoxWidgetClass) XtClass(w))->
	    csb_class.set_color) (w, space, c1, c2, c3, c4, setSpace);
}

static void GetColor(
    Widget w,
    CSBColorSpace space,
    float *c1, float *c2, float *c3, float *c4)
{
    ColorSelectionBoxWidget csb = (ColorSelectionBoxWidget) w;

    switch (space) {
	case CSBSpaceRGB:
	    *c1 = csb->csb.current_color.red;
	    *c2 = csb->csb.current_color.green;
	    *c3 = csb->csb.current_color.blue;
	    break;
	case CSBSpaceCMYK:
	    *c1 = csb->csb.current_color.cyan;
	    *c2 = csb->csb.current_color.magenta;
	    *c3 = csb->csb.current_color.yellow;
	    *c4 = csb->csb.current_color.black;
	    break;
	case CSBSpaceHSB:
	    *c1 = csb->csb.current_color.hue;
	    *c2 = csb->csb.current_color.saturation;
	    *c3 = csb->csb.current_color.brightness;
	    break;
	case CSBSpaceGray:
	    *c1 = csb->csb.current_color.gray;
	    break;
    }
}

void CSBGetColor(
    Widget w,
    CSBColorSpace space,
    float *c1, float *c2, float *c3, float *c4)
{
    XtCheckSubclass(w, colorSelectionBoxWidgetClass, NULL);

    (*((ColorSelectionBoxWidgetClass) XtClass(w))->
	    csb_class.get_color) (w, space, c1, c2, c3, c4);
}
